• Home
  • Akiko Yokoyama
  • Contact
  • Feed
ja | en |

How to build a Stripe API x Rails (include code sample)

I’ve created a sample Rails API using the Stripe Gem, which can be found in the Github repository below.
acotie/rails_stripe_connect_api: Stripe charge & subscription sample code w/ Rails 5.1.x + Grape 1.0

Assumption - References of Stripe official web site:

  • Stripe.com - Payment processing for internet businesses
  • Documentation
  • Rails Checkout Guide

Using Gems

  • rails 5.1.4+
  • devise
  • devise_token_auth
  • grape
  • stripe
  • dotenv-rails

The User is created in Devise, and the important thing is how to link the stripe api with the Rails User data. This was the approach we took. I had to implement Stripe’s billing API within a week at the time, and I searched Github like harder for reference.
I learned about the idea of having a stripe_customer_id in the User model, and mimicked it.

Installing and using Stripe

Stripe’s dashboard screen upper left menu +New Account > Get API KEY.

Add to Gemfile

1
gem 'stripe'

With bundle install, you can use Stripe:: namespace.

1
$ bundle install --path vendor/bundle

Sample Charge call from Ruby file. You can also try it with $ rails c. stripe-sample.rb

1
2
3
4
5
6
7
8
9
10
11
12
require "stripe"
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"
Stripe.api_version = "2017-12-14"

Stripe::Charge.list()

Stripe::Charge.create(
  :amount => 2000,
  :currency => "usd",
  :source => "tok_amex", # obtained with Stripe.js
  :description => "Charge for elizabeth.taylor@example.com"
)

About the structure of Stripe

The Stripe library has the following objects.

Customers (User)

ref: Stripe API Reference

  • id : cus_96gOUYURouuWjz
  • email
  • description
  • currency : usd
  • card_id : card_18oXxZJgEbT6Ft4J0QBohKDi

Charge (One-time billing)

ref: Stripe API Reference

  • id : ch_1BmybM2eZvKYlo2Cdl1GKXze
  • amount : 1500
  • currency : usd

Subscription (Flat rate billing)

ref: Stripe API Reference

  • id : sub_95lUmykFOcUUMH
  • amount : 500, 3000, 6000
  • plan_id : silver, gold ref: Stripe API Reference
    • This is the unique ID string that I created in Plan.create in Stripe.
  • status
  • interval
  • interval_count

Plan (Plan for flat rate billing) ref: Stripe API Reference

  • plan_id : silver, gold

About Implementation

The flow is as follows.

1. Create a User in Devise, and create a user who can login.

Create a user using devise with the $ rails g command. The details of devise are omitted.

1
2
$ rails generate devise User
$ rails g devise:views

The configuration file of the application in general can be found in config, and the function control of User using Devise can be found in model.

  • config/initializers/devise.rb
  • app/models/user.rb

2. Add ID for Stripe to the User model

As mentioned above, you will add stripe_customer_id and stripe_card_id to the User model.

1
$ rails generate migration AddStripeCustomerIdToUsers stripe_customer_id:string

3. Create the Charge model.

Create the Charge model. Add stripe_charge_id and user_id. If you only have the ID of the charge in the stripe, you have to make an HTTP request every time, we added the amount of money amount and the unit of currency currency in the Rails model.

1
$ rails g model Charge user_id:integer stripe_charge_id:string amount:integer currency:string

4. Create the Subscription model.

Create the Subscription model. Add stripe_subscription_id, user_id and stripe_customer_id. The amount is the amount of money, and the interval and interval_count of the cycle are added later in the Rails model, because we have to make HTTP requests every time if we only have the ID of the subscription in the strip.

1
$ rails g model Subscription user_id:integer amount:integer stripe_customer_id:string stripe_subscription_id:string

Summary of Model in Rails side

  • User
    • stripe_customer_id
    • stripe_card_id
    • subscribed
    • subscribed_at
  • Charge
    • user_id
    • stripe_charge_id
    • amount
    • currency
  • Subscription
    • user_id
    • amount
    • stripe_subscription_id
    • stripe_plan_id
    • interval
    • interval_count
    • stripe_status

Actual code sample

app/models/user.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  has_one :subscription
  has_many :charges


  #############################################
  ##### Start Connection Stripe Objects #######
  #############################################
  # stripe customer_id
  # return string  e.g. "cus_96gOUYURouuWjz"
  def customer_id
    customer.id
  end

  # stripe card_id
  # return string  e.g. "card_18oXxZJgEbT6Ft4J0QBohKDi"
  def card_id
    card.id
  end

  # stripe card_id
  # return string  e.g. "card_18oXxZJgEbT6Ft4J0QBohKDi"
#  def card_id
#    customer.sources.first.id
#  end

  # stripe subscription_id
  # return string  e.g. "sub_95lUmykFOcUUMH"
  def subscription_id
    customer.subscriptions.first.id
  end


  # Stripe::Card object
  # see https://stripe.com/docs/api#card_object
  def card_data
    customer.sources.first
  end

  # Stripe::Subscription object
  # see https://stripe.com/docs/api#subscription_object
  def subscription_data
    customer.subscriptions.first
  end

  # for API
  # Stripe::Customer object
  # see https://stripe.com/docs/api#customer_object
  # TODO: plan, cardは後からupdate
  def customer
    if self.stripe_customer_id.present?
      Stripe::Customer.retrieve(self.stripe_customer_id)
    else
      create_customer
    end
  end

  # TODO Research STRIPE updated new API (create & update card)
  def card
    if self.stripe_card_id.present?
      customer.sources.retrieve(self.stripe_card_id)
    else
      create_card
    end
  end

  #############################################
  ###### End Connection Stripe Objects ########
  #############################################
  

  private

  def create_customer
    customer = Stripe::Customer.create(
      email:       self.email,
      description: "Customer for #{self.email}",
    )
    self.stripe_customer_id = customer.id
    self.save
    customer
  end
end

app/api/sample/charges.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Sample::Charges < Grape::API
  resource "charges" do

    desc "create new Charge"
    params do
      requires :amount, type: Integer, desc: "amount (ドル OR 円)"
      requires :currency, type: String, default: 'usd', values: ['usd', 'jpy']
    end
    post do
      error!('not subscribed', 400) if !current_user.subscribed?  # [MUST]メンバーシップ加入済み

      amount = params[:amount] * 100 if params[:currency] == 'usd'
      amount = params[:amount]       if params[:currency] == 'jpy'

      customer  = current_user.customer
      charge    = Stripe::Charge.create(
        customer:    customer,
        currency:    params[:currency],
        amount:      amount,
        description: "Charge for #{current_user.email}",
      )

      charges = current_user.charges.create({
        stripe_charge_id:       charge.id,
        amount:                 charge.amount,
        currency:               charge.currency,
      })
      charges.save

      present :charges, charges, with: Sample::Entities::Charge, user: current_user
    end
  end
 end

I also prepared the Subscription API.
app/api/sample/subscriptions.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Sample::Subscriptions < Grape::API
  resource "subscriptions" do

    desc "create new Subscription (new: only subscription)"
    params do
      requires :plan_id, type: String, desc: "stripe plan_id (text)"
    end
    post do
      error!('already subscription', 400) if current_user.subscribed?

      customer = current_user.customer
      card     = current_user.card

      customer.plan = params[:plan_id]
      customer.save

      # update credit card
      #current_user.save_credit_card(customer.sources.first)

      subscription = current_user.create_subscription({
        stripe_subscription_id: customer.subscriptions.data[0].id,
        stripe_plan_id:         customer.subscriptions.data[0].plan.id,
        amount:                 customer.subscriptions.data[0].plan.amount,
        interval:               customer.subscriptions.data[0].plan.interval,
        interval_count:         customer.subscriptions.data[0].plan.interval_count,
        stripe_status:          customer.subscriptions.data[0].status,
      })
      subscription.save

      current_user.update_attributes(subscribed: true, subscribed_at: Time.now.utc)

      # TODO create OK/NG response
      present :subscription, subscription, with: Sample::Entities::Subscription, user: current_user
    end
  end
end
user-image
Akiko yokoyama in Coding
10 minute read

Similar Posts

Actually Made This Blog Multilingual Three Years Ago

Review of the Leica Q2

Re-Builded the Blog with Jekyll + Netlify + Github

Excel Tips vol.1

Setup for WSL / Windows Subsystem for Linux + Ubuntu

user-image

Published Jan 13, 2018

Akiko yokoyama in Coding

Also found in

  • Coding

Share this article

Actually Made This Blog Multilingual Three Years Ago

Review of the Leica Q2

Re-Builded the Blog with Jekyll + Netlify + Github

Excel Tips vol.1

Setup for WSL / Windows Subsystem for Linux + Ubuntu

Setting hubot adapter slack & chatwork