9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】Stripeの実装(都度カード情報入力版)

Last updated at Posted at 2019-07-30

環境

  • Rails 5.2.3
  • stripe-ruby 4.21.3
    • ドキュメント作成時(2019年8月頃)の最新版

ドキュメント(Stripe公式)

設定

KEYの設定(環境変数)

環境変数に以下を設定(以下は、dotenvへの設定例)。

.envrc
export STRIPE_SECRET_KEY=sk_test_4eC39HqLyjWDarjtT1zdp7dc
export STRIPE_PUBLISHABLE_KEY=pk_test_TYooMQauvdEDq54NiTphI7jx

STRIPE_SECRET_KEYSTRIPE_PUBLISHABLE_KEYは、以下の 3種類がある。

  • 完全にパブリックなキー (A)
  • ユーザ固有のキー(アカウント登録をしたら、Stripeのdashboardで取得可能)
    • テスト用 (B)
    • 本番 (C)

(A)でもテスト(Stripeのサイトにリクエストを投げてレスポンスを受け取ること)は可能。(A)と(B)の違いは、(B)であればStripeのdashboardにログが残る。

initializersの設定

↑で設定したSTRIPE_SECRET_KEYSTRIPE_PUBLISHABLE_KEYを読み込む。

config/initializers/stripe.rb
Rails.configuration.stripe = {
  secret_key: ENV['STRIPE_SECRET_KEY'],
  publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
}

Stripe.api_key = Rails.configuration.stripe[:secret_key]
# 以下は必要に応じて。
# Stripe.log_level = Stripe::LEVEL_DEBUG if Rails.env.development? || Rails.env.test?

コンソールでテスト

テスト結果は、stripeのダッシュボードで確認可能。
※ユーザ固有のキーを使っている場合のみ。
※ダッシュボードで 「テストデータを表示」 をチェックすること。

リクエスト例

  • amountはドル換算で 50セント以上になるようにする(50セント未満の場合、exceptionが発生する)。
  • tokenは以下の何れかを参照 (※以下の例ではtok_jpを使用している)。
  • metadataはオプションで、任意のキーバリューを登録可能。
  • 詳細はこちら
> amount = 500
> token = 'tok_jp'
> order_id = 'sample_order_id'
> charge = Stripe::Charge.create(
    amount: amount,
    currency: 'jpy',
    source: token,
    metadata: { order_id: order_id },
  )

レスポンス例(↑の例ではchargeに入るもの)

charge.class
=> Stripe::Charge
#<Stripe::Charge:0x3fcffca797f0 id=ch_1F2xIk2eZvKYlo2CQKTDhT9r> JSON: {
                        :id => "ch_1F1p6aH3tpHGjL9VSxp7q6sc",
                    :object => "charge",
                    :amount => 50,
           :amount_refunded => 0,
               :application => nil,
           :application_fee => nil,
    :application_fee_amount => nil,
       :balance_transaction => "txn_1F1p6bH3tpHGjL9V0HnBtcyF",
           :billing_details => {
        :address => {
                   :city => nil,
                :country => nil,
                  :line1 => nil,
                  :line2 => nil,
            :postal_code => nil,
                  :state => nil
        },
          :email => nil,
           :name => nil,
          :phone => nil
    },
                  :captured => true,
                   :created => 1564469200,
                  :currency => "jpy",
                  :customer => nil,
               :description => "foo",
               :destination => nil,
                   :dispute => nil,
              :failure_code => nil,
           :failure_message => nil,
             :fraud_details => {},
                   :invoice => nil,
                  :livemode => false,
                  :metadata => {
        :order_id => "sample_order_id"
    },
              :on_behalf_of => nil,
                     :order => nil,
                   :outcome => {
        :network_status => "approved_by_network",
                :reason => nil,
            :risk_level => "normal",
            :risk_score => 46,
        :seller_message => "Payment complete.",
                  :type => "authorized"
    },
                      :paid => true,
            :payment_intent => nil,
            :payment_method => "card_1F1p6aH3tpHGjL9VgCk3up1H",
    :payment_method_details => {
        :card => {
                     :brand => "visa",
                    :checks => {
                      :address_line1_check => nil,
                :address_postal_code_check => nil,
                                :cvc_check => nil
            },
                   :country => "JP",
                 :exp_month => 7,
                  :exp_year => 2020,
               :fingerprint => "nU5DqM3V4oA3mRpV",
                   :funding => "credit",
                     :last4 => "0003",
            :three_d_secure => nil,
                    :wallet => nil
        },
        :type => "card"
    },
             :receipt_email => nil,
            :receipt_number => nil,
               :receipt_url => "https://pay.stripe.com/receipts/acct_1Efh0qH3tpHGjL9V/ch_1F1p6aH3tpHGjL9VSxp7q6sc/rcpt_FWssev6e7emJfUcBYqnzY0hWdl0D6ox",
                  :refunded => false,
                   :refunds => {
             :object => "list",
               :data => [],
           :has_more => false,
        :total_count => 0,
                :url => "/v1/charges/ch_1F1p6aH3tpHGjL9VSxp7q6sc/refunds"
    },
                    :review => nil,
                  :shipping => nil,
                    :source => {
                         :id => "card_1F1p6aH3tpHGjL9VgCk3up1H",
                     :object => "card",
               :address_city => nil,
            :address_country => nil,
              :address_line1 => nil,
        :address_line1_check => nil,
              :address_line2 => nil,
              :address_state => nil,
                :address_zip => nil,
          :address_zip_check => nil,
                      :brand => "Visa",
                    :country => "JP",
                   :customer => nil,
                  :cvc_check => nil,
              :dynamic_last4 => nil,
                  :exp_month => 7,
                   :exp_year => 2020,
                :fingerprint => "nU5DqM3V4oA3mRpV",
                    :funding => "credit",
                      :last4 => "0003",
                   :metadata => {},
                       :name => nil,
        :tokenization_method => nil
    },
           :source_transfer => nil,
      :statement_descriptor => nil,
                    :status => "succeeded",
             :transfer_data => nil,
            :transfer_group => nil
}

エラー処理

  • エラーがあった場合、exception(Stripe::InvalidRequestError)が発生する。
    • 以下は、amountが50cents以下だった場合の例。
Stripe::InvalidRequestError: Amount must convert to at least 50 cents. ¥50 converts to approximately $0.45.
from /Users/foo/src/github.com/bar/baz/vendor/bundle/ruby/2.6.0/gems/stripe-4.21.3/lib/stripe/stripe_client.rb:350:in `handle_error_response'
Caused by Faraday::ClientError: the server responded with status 400
from /Users/foo/src/github.com/bar/baz/vendor/bundle/ruby/2.6.0/gems/faraday-0.15.4/lib/faraday/response/raise_error.rb:13:in `on_complete'

独自Exception

「amountの最低金額を500円にする」等、独自でExceptionを発生させる例。

MINIMUM_AMOUNT = 500
raise Stripe::InvalidRequestError.new('Amount must be more than or equal to 500', amount) if amount < MINIMUM_AMOUNT

実装時の注意点

クライアントから送られてきた金額は、サーバで検証する(改竄されうるため)。

  • クライアントからamount(請求額)を受け取る。
  • オーダー情報(※この記事の例ではorder_id)を元にサーバでamountを計算する。
    • 上記 2つが異なる場合は、改竄の疑いがあるためエラーを返す。

二重課金の防止

おまけ

フリーランスで活動しています。
開発案件(Go、Rails、Blockchain、機械学習など)をお待ちしております。

職務経歴書(wantedlyへのリンク)

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?