概要
書きかけです。2020年9月末時点でのまとめ。初心者です。
決済代行サービスの1つであるStripeを組み込みんだ際の調査メモ。
APIサーバ側はRailsでの例を記載。
Stripeの特に、サブスクリプション系の新しいフロー(Intentを使う)
についてまとめようと思っています。
Stripeとは
Stripeとは決済代行プラットフォームの1つ。
最終的に他の比較検討候補として、PAY.jp / Square を検討。
・PAY.JP
国内の決済プラットフォーム。ドユメントも分かりやすく組み込みやすそう。
数回のやりとりではあったが、レスポンスもとても早くて親身に対応していただきサポート対応に好感が持てた。
やりとりも(言語的に)安心。決済手数料も比較候補の中では相対的に安い。
海外市場への展開も見越して、今回は断念。
・Squqre
大手の決済代行サービス。実績/機能面とも申し分ない。
開発ドキュメントの雰囲気でStripeにした。
(Stripeのほうが後発だからか全体的に洗練されてるという個人的な印象)
全体理解
開発を始める前に、Stripeの全体構造を理解する必要がある。
Stripeが提供する機能をまとめると以下の感じ。(2020/9 現在)
-
支払い
-
Payments: オンライン支払い => ECサイトの1回限りの商品購入など
-
Billing: 定期支払いと請求処理 => サブスク型のビジネスなど
-
Terminal: 対面支払い => カードリーダを使った対面支払い(POS構築とか、2020年現在は米国のみ)
-
Connect: 顧客間の送金など決済プラットフォーム => クラウドファンドサイトやマーケットプレイスなど
-
入金
-
Payouts: プログラム入金 => 大量の顧客講座への銀行振込の自動化など
-
Issuing: 仮想/物理カード作成 => 出張者向けに特定の宿泊施設/移動しか使えないような仮想カードを発行など
-
運営
- Radar: 不正検知 => 標準手数料3.6t%では無料で有効にできる不正検知機能
- Sigma: カスタムレポート => クエリを発行しての決済履歴の確認、レポート機能
- Atlas: 起業支援 => 書類準備支援や起業時の優遇特典などを受けられる。(米法人を作るなら便利か?..)
個人で事業を起こす場合やスタートアップで、よく使用するのは、
Payments, Billing, Radar (場合により Sigma)あたりだろうか。
ここを調べてみる。
Payments
まずは、基本となる単一の1回限りの決済から見ていく。
2つの決済フロー(ChargesとPayment Intents)
現状、Stripeの決済組み込みについては、Chargesフロー(旧) と Payment Intentsフロー(新) の2つの実装フローがある。
昨今の特にヨーロッパでのオンライン決済の規制強化の流れを受けてAPIレベルでリニューアルしたものが新フロー。
Chargesは廃止の予定はないが、今後はPayment Intentsが主になるとのことなので、Payment Intentsでの実装方をみていく。
Intentsが主流になるというソースは下記。
we plan to focus product development on Payment Intents
https://stripe.com/docs/payments/payment-intents/migration/charges
Payment Intents 課金シーケンス
https://stripe.com/docs/stripe-js/elements/payment-request-button
Payment Intentを利用した決済は大きく2つのステップに別れる。
1.Intentを「作成する」ステップ と 2.「確認する」ステップである
1.Intentを作成する
上記シーケンス図の上半分の部分である。
顧客のカート情報や決済セッションつき作成する。
Payment Intentは、 支払い方法/ 金額/ 通貨 など決済に関連するトランザクション情報をまとめたものである。
クライアントから要求しサーバ側で生成する。( サーバ側の方がintentの改竄など悪さしにくく安全 )
ポイントは最後クライアントへ戻す際には不用意にIntent情報の全てを渡さずにsecret情報だけ返却すること。
# 事前準備
# gem 'stripe' はinstallしておく。
# Stripeにテストアカウントを登録しておく。
# Stripe.api_key に アカウント登録時に発行される秘密鍵情報を設定しておくこと(railsならinitializerあたりで環境変数から引っ張ってセットする)
# 適当なControllerで作成してください
def create
@payment_intent = Stripe::PaymentIntent.create({
amount: 120,
currency: 'jpy',
payment_method_types: ['card'],})
# clientにはclient_secret情報を返却する必要があるのでjson形式で返してあげる
render json: {client_secret: @payment_intent.client_secret}
end
上記は、最低限必要となる項目で作成している。(amount, currencyは必須、payment_method_typesは許容される支払い方法を記載。デフォ値でcard。)
他に、任意項目として、顧客情報、配送情報、説明文、メタデータ(key-valueのhash形式)などを付与できる。
create時に指定できるパラメータはの詳細は下記の公式ドキュメント参照。
2. Intentを確認する
ボタン押下時に、クライアント側で下記のメソッドを実行する
// client_secret にはサーバ側で生成したものをセットする
// payment_methodにはクレジットカードのフォームのエレメントを渡す
stripe.confirmCardPayment(client_secret, {
payment_method: {card: cardElement}});
confirmCardPaymentの関数の詳細は以下を参照
https://stripe.com/docs/js/payment_intents/confirm_card_payment
合わせてサーバ側で必要な処理(Webhook)
confrimCardPaymentがエラーなく実行されると、この時点で決済が成立する。(お金が入る)
シーケンス図の中では記載が分かりにくいが、このタイミングでサーバ側で何かしらの処理が必要な場合が多いと思う。
ってかそっちの方ほとんどだと思う。
この場合、webhookを利用する。
ダッシュボード > 開発者 > Webhook
任意のエンドポイントURLを登録することで、Stripeからサービスのサーバへ任意のイベントリクエストをPOSTで飛ばすことができる。
URLについては、公開されている必要がありhttpsでの登録となるので注意。
Webhookを使ってpayment intentが確認されたことをハンドリングし、サービスで課金された際に必要な処理を行うと良い。
例えば、ユーザの状態をプレミアム会員にアップグレードしたり、ライセンスのアクティベートメールを送信したり、
商品マスタの在庫を減らしたり。
具体的には、登録したURLの受け口をつくり、イベントパラメータがpayment_intent.succeededのものを購読して処理する。
気になるのは、 再起動とかでWebhookをサーバが受け取れない時どうなるの?というところだが、その辺も考慮されている。
Webhooksは200系のステータスコード(200 to 299)が返されるまで、最大3日間1時間に1回再送される。
Unlike before, webhooks are now retried once an hour for up to 3 days or until a 200 status code is returned.
安心して使えそうだ。
それでも失敗し続けたらどうなるの?ってところだがエラー通知メールが届く。この後手動での対応が入るだろう。
実際に404を返すエンドポイントを設定してみたところ、一定回数失敗したところでアカウントに対して以下のメールが届いた。
subject : Stripe webhook delivery issues for <xxxx WebhookのURL>
We’re contacting you because we’ve had some trouble sending requests to a webhook endpoint
associated with your <xxxx> account in test mode.
Webhooks are used to notify your server about events that happen in your Stripe account,
such as a payout completing or an invoice being created.
The failing webhook endpoint is xxxxx.
(以下は省略するが、下記のような情報も追加で記載されている)
・送信した日付と、失敗した回数、タイムアウトした回数などの詳細情報もメールに記載される。
・webhookの通知停止日時も記載される。(メール通知してから一定期間の猶予がある)
Billing
Billingを使ってサブスクリプション実装する際の公式ドキュメントは下記にある。
翻訳しつつ必要なところだけ要点をかいつまんでまとめる。 (202009E時点)
サブスクリプションフロー概要
買い切りフローと異なり、継続して課金するために顧客に関する多くの情報を保持・管理する必要がある。
多くの情報とは具体的には下記の6つのオブジェクト。
オブジェクト名 | 説明 |
---|---|
Customers(顧客) | 顧客の情報 |
Products(製品情報) | 売り物の情報 |
Price(価格) | 旧来のPlanを置き換えるもの。 Price=単なる価格ではなく、ここでは価格に関連する全般を含む。商品の料金と頻度設定。商品に複数価格を定義して、通貨または間隔(月、年など)に基づいて異なる金額を請求することができる。 |
Subscriptions(継続課金契約) | 顧客の製品へのアクセス権・利用権。継続課金を作成するには、先に、製品/顧客/支払い方法を作成する必要がある。 |
Invoices(請求情報) | サブスクリプション契約時に顧客に請求が発生する際に生成される。請求書には、明細、税率、および顧客が支払うべき合計金額が含まれる。 |
Payment Intnt(支払い意思) | 請求情報に基づき支払いを試みた際に生成される。 |
組み込み
組み込みに関しては、すでにサービスで会員登録をしていること。
値段のついたプランが1つ以上あり、継続課金契約ボタンを押下した地点から始める。
支払いに関する動き
- クレジットカード情報(のトークン)を取得。支払い方法をpayment methodを保存する。(デフォルトの決済先としてサービスで保存する)
- 顧客と支払い方法(customersIdとpriceId)を使ってSubscription = 継続課金契約を作成する。
- 最初のサブスクリプションサイクルにたいして1つのinvoice(請求情報)が作成される。
- incoiceを保存されている支払い方法(payment method)で支払う。
支払い成立後の処理(プロビジョニング等)
支払い完了ページで以下の処理を実施
- サブスクリプションステータスがactiveになっていること
- 顧客が契約した製品・機能を利用可能にすること
最初の支払いの成否
サブスクリプションの支払いステータスは、以下の3種類あるのでそれぞれでハンドリングが必要である。
ステータス名 | 内容 |
---|---|
succeeded | 正常に支払いが行われた。サービス側では支払い応じたプロビジョニング等の処理を行う。 |
requires_payment_method | カード情報周りでエラーが起きた場合。番号間違いなど。新しい支払い情報を登録してもらう。メールや画面等で通知し、新しい支払い情報をサービス側に保管し、サブスクリプションを更新する。 |
requires_action | 支払いプロセスを完了するために何らかのユーザアクションが必要な場合。3Dセキュア認証など。認証アクションを促す処理を入れる。メールなどを送信するとか。 |
2回目以降の支払い
2回目以降は自動で定期的に請求処理が行われる。エラーになる可能性もあるので、
webhookにて customer.subscription.updated のイベントを購読しておく。
エラーの処理
スリーアウト制を採用した。(造語です)
サブスクリプションがpast_due(延滞)のステータスになったら、顧客にリマインドメールを送信する。
クレジットカードの残高不足などでは期間をあけて3回程度おくり、3回目でBANする等。
継続課金のステータスがactiveになったらOKとする。
より固くいく場合には、未払いの請求書を発行する。
トライアルや従量課金など、契約日が後ろにずれ込むものに関して、
未払いで請求書を発行しておくと、取りっぱぐれるリスクを軽減できる。(カード情報の妥当性を見るために0円で請求処理だけ確認するような感じ)
このたSetupIntentというものを使用する。
具体的な実装内容
下記に、fixd-price(固定価格) pre-sheat(ユニット数課金) metered(従量課金)での決済方法がそれぞれのっている。
タブで選択して、リンクの先参照。