環境
- Rails 5.2.3
-
stripe-ruby 4.21.3
- ドキュメント作成時(2019年8月頃)の最新版
ドキュメント(Stripe公式)
- 支払いの作成
-
API Reference
- エラーハンドリング ※画面右のコード例はプログラミング言語によって切り替え出来る。
設定
KEYの設定(環境変数)
環境変数に以下を設定(以下は、dotenv
への設定例)。
.envrc
export STRIPE_SECRET_KEY=sk_test_4eC39HqLyjWDarjtT1zdp7dc
export STRIPE_PUBLISHABLE_KEY=pk_test_TYooMQauvdEDq54NiTphI7jx
STRIPE_SECRET_KEY
、STRIPE_PUBLISHABLE_KEY
は、以下の 3種類がある。
- 完全にパブリックなキー (A)
- ユーザ固有のキー(アカウント登録をしたら、Stripeのdashboardで取得可能)
- テスト用 (B)
- 本番 (C)
(A)でもテスト(Stripeのサイトにリクエストを投げてレスポンスを受け取ること)は可能。(A)と(B)の違いは、(B)であればStripeのdashboardにログが残る。
initializersの設定
↑で設定したSTRIPE_SECRET_KEY
とSTRIPE_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
を使用している)。- https://stripe.com/docs/testing#cards
-
https://stripe.com/docs/testing#international-cards (※
currency
としてjpy
を使う場合はこっち。但し、テストなのでどちらもで良い)
-
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
-
Stripe::InvalidRequestError
のinitialize参照
実装時の注意点
クライアントから送られてきた金額は、サーバで検証する(改竄されうるため)。
- クライアントからamount(請求額)を受け取る。
- オーダー情報(※この記事の例ではorder_id)を元にサーバでamountを計算する。
- 上記 2つが異なる場合は、改竄の疑いがあるためエラーを返す。
二重課金の防止
おまけ
フリーランスで活動しています。
開発案件(Go、Rails、Blockchain、機械学習など)をお待ちしております。