Edited at

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


環境


  • 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


その他のGem


実装時の注意点


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


  • クライアントからamount(請求額)を受け取る。

  • オーダー情報(※この記事の例ではorder_id)を元にサーバでamountを計算する。


    • 上記 2つが異なる場合は、改竄の疑いがあるためエラーを返す。




二重課金の防止


おまけ

フリーランスで活動しています。

開発案件(Go、Rails、Blockchain、機械学習など)をお待ちしております。

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