Stripe という決済 SaaS の存在を、最近知りました。いろんな API を組み合わせて、いろんな決済処理を実装できます。個々の API エンドポイントのパラメータや挙動はドキュメントがあり、テストモードで簡単に試せます。
一方、外側からみて、Stripe におけるデータオブジェクトのコンセプトが分かると、個々の API をやみくもに調べるよりは、設計の役に立つような気がします。決済というのが、そもそも比較的抽象度の高い概念です。サービス上のユーザー体験が API と 1対1 で対応しないことがあります。そんなときは、少しレイヤーを上げた視点 も あるといいんじゃないかなー、と思います。
そこで JP_Stripes Advent Calendar 2020 にぶらさがって、自分なりの解釈をまとめました。
以下、クレジットカードでの支払いを想定します。
Payment Intent
Payment Intent は 1回の「支払い」の設定、状態、処理を管理するオブジェクトです。設定というのは「1000円」みたいな情報です。
状態というは、カード番号が必要だとか、認証が必要だとか、処理中だとか、キャンセルされたとかです。
状態遷移をするときに、裏側では必要に応じて、クレジットカードのシステムと通信が走ります。
とりあえず、どんなのか試したいときは、Integration Builder
が、手っ取り早いです。もう少し細かい説明 もあります。
大雑把な状態遷移はこんな感じ。
- requires_payment_method: Payment Method (クレジットカード番号など)が必要。
- requires_confirmation: アプリケーションによる確認(これでOKっす、って指定する API コール)が必要。
- requires_action: 何かアクションが必要。3Dセキュア のパスワード入力とか。
- processing: 処理中。大抵、数秒で、次の状態に遷移する。
- succeeded: 支払い完了。クレジットカードの引き落とし日を楽しみにしてください
- canceled: アプリケーションがキャンセルした。
決済に失敗したら、requires_payment_method に遷移します。
破線枠の状態は、スキップする場合がある状態を表します。正確に分岐を明示すると、ごちゃごちゃするので省略しました。
Payment Intent は支払いの行動を追跡・処理するオブジェクトです。カード番号なんかを直接管理しません。
Payment Method
Payment Method は支払い方法・手段を表すオブジェクトです。クレジットカードによる支払いの場合、クレジットカード番号、有効期限、CVC などを保持します。
Payment Intent を作ってから、フロントエンドで Payment Method を作成して Payment Intent と関連づけることができます。
反対に、先に Payment Method を作成して(=クレジットカード番号を入力させて)から、Payment Intent を作成して関連づけてもよいです。クレジットカードのブランドによって、挙動を変えたいような場合に有効な順序です。
Customer
会員的な概念があって、繰り返し利用するクレジットカード番号を保存したい、というケースがあると思います。その場合は、Payment Method を Customer オブジェクトに attach します。
Customer オブジェクトは、顧客、より正確には支払いをする主体を表します。B2C だったらおそらくは会員とかユーザーという概念に対応します。
ひとつの Customer は、0個以上の Payment Method と関連づけられます。ひとつの Payment Method は、0〜1個の Customer と関連づけられます。
Payment Method を一度 Customer と関連づけると、他の Customer と関連づけることはできません。
サブスクリプション・ビジネス
Customer と Payment Method の関連づけができれば、毎月バッチを走らせると月額課金ができます。「毎月3,000円支払ってくれたら、うちで飼っている猫が、毎朝、朝食をとっている動画を見せてあげる」みたいなサービスを作れるわけです。やったね。
さて Billing と呼ばれるサービス・API群を使うと、このへんの処理を自動化できます。月に一度決済をするだけならいいんですが、バッチを回すだけより、もうちょい複雑なコンセプトが絡みます。
世の中には3つの嘘があります。嘘、ひどい嘘、定期的に決済するだけ、です。
まずは、定期的に決済するだけに必要なデータオブジェクトを、順番に見ていきましょう。
Product と Price
まず商品と料金体系を定義します。
Product は商品やサービスを表す抽象的な概念です。たとえば「猫の朝食動画ストリーミング」というサービスを表します。
Price は、Product と関連づいた価格体系を表します。ひとつの Product に対して、複数の Price を関連づけられます。
複数の Price の用途は大きくふたつあります。複数通貨の場合です。月額3,000円と、月額30ドルを用意するとかですね。
もうひとつは期間が違う場合です。月額3,000円と、年額30,000円を用意するような場合です。
これで商品と価格体系の定義ができました。
「猫の夕食動画ストリーミング」というサービスを追加する場合や、「朝夕バンドル」を追加する場合には、Product 側を変更するとすっきりするでしょう。
3ヶ月ごと、半年ごとの価格体系を追加するときは、Price を操作するのがよいでしょう。
ただ Price 側でいろいろ定義したり、後述する Coupon を使ったりして、いろんなバリエーションを作れます。このあたりこそが、設計者の腕の見せ所だと思います。
Subscription
Subscription オブジェクトは、定期課金の契約の定義と状態の保持、それから関連する自動化処理のトリガーの役割があります。
「ある Customer が、ある Price にサブスクライブする」という契約を定義するのがSubscription です。Price で定義したとおり、Customer に関連づいた Payment Method から資金を回収したいよ、という契約です。
注意すべきは Customer は支払いの主体だ、ということです。たとえば「3ユーザーまで視聴可」みたいな契約のとき、Customer はおそらく「ユーザー」には関連づかず、支払いをする代表ユーザーのみを表すことになるでしょう。
先払いの契約を想定しているため、契約開始時に最初の請求が発生します。
しかし Subscription は契約を保持するだけで、直接決済をしません。じゃあ、どうするかっていうと、支払いが必要になったとき、自動的に Invoice オブジェクトをつくります。
Invoice
Invoice オブジェクトは1回の請求行為を表します。猫ストリームの例では「今月ぶんの3,000円を請求するのだ」という活動です。
未払いだとか、支払い済みだとか、期限切れだとか、もういらねぇよ(void)などの状態を持ちます。
請求の状態を持ちますが、支払いを直接管理しません。じゃあどうするのかというと、ここで最初に出てきた Payment Intent が登場します。Invoice が資金の回収ができる条件がそろうと、自動的に Payment Intent を作り、実行しようとします。
Payment Intent は、Customer -> Subscription -> Invoice と伝搬・継承してきた Payment Method から、支払いを回収しようとします。その結果は、今度は逆向きに Invoice -> Subscription のほうに伝搬していきます。
Invoice が回収に失敗したとき、スマートリトライなる自動化処理があり、なんとなくいいタイミングでリトライします。それでも失敗し続けると、諦めます。その場合に、未回収だけど subscription を生かしておく、subscription をキャンセルするなどの挙動定義は、ダッシュボードで設定できます。
trial
さて、めでたくサブスクリプション・ビジネスのサービスが開発できました。やったね! すると、マーケティング担当とか、グロース担当とかが、こういうことを言い出すわけです。
「今月サブスクしたら、一ヶ月無料キャンペーンをしよう!」
だるいですね。
初月無料キャンペーンのだるいところは、契約開始日と資金回収開始日がずれてしまうことです。そんなデータ構造持ってねぇよ、って言いたくなりますよね。
そこで trial です。Subscription オブジェクトを作るときに、使用期間を指定できます。これは subscription が始まってからしばらく無料が続き、そのあとで最初の支払いを発生させるためのパラメータです。
Subscription を作った時点では、0円の invoice が作られます。支払いは発生しないので Payment Intent は作られず、Invoice はすぐに paid 状態になります。そのため subscription は生きたままです。
trial 期間が終わると、そこから支払いサイクルが始まり、Invoice -> Payment Intent の連鎖が始まります。
Coupon
やりました。開発者のみなさんは、trial をも乗り越えました。すると、グロース担当()が言うわけです。
「競合が似たサービスを作ってきた! 継続してもらうために、既存顧客を2ヶ月間 50% オフにしよう!」
はー。だるいですね。
「難しかったら、1ヶ月でもいいよ」
そういう問題じゃねーよ、お前のギャラを120%オフにすんぞ。
それはそれとして、Coupon オブジェクトが使えるかもしれません。Subscription に Coupon を関連づけると、その Subscription の今後の請求に割引が適用されます。定義できる割引の挙動には、以下のようなパラメータがあります。
- 何%オフにするか、◯◯円引きにするか
- 1度だけか、指定した回数か、ずーっとか
- クーポン適用できる期限
- クーポンを適用できる最大数
- など
物理的に配るクーポン、デジタルクーポンを管理するものではありません。「Subscription に適用する割引」を表す、抽象的な概念です。
Coupon が適用された結果、支払いサイクルで請求額が0円になる場合もあます。そのときは、0円 の Invoice が作られます。
おわりに
バッチでできなくもないんですが、ワークフロー的な挙動をある程度 SaaS に任せられると、だるさから開放されることも多いと思います。ここでは、基本的なコンセプトを、自分なりにまとめてみました。
よいお年を。
Disclaimer: この記事は、公開されている情報を、個人が解釈して書き記したものです。所属する組織と関係ありそうに見えるかもしれませんが、関係ありません。
Disclaimer 2: 所属する組織の人々へ。この文書は、勤務時間外に、個人の機材を使って、個人の解釈をまとめたものです。仕事ではないので、仕事として何か言ってこないでください。もちろん問題があったら修正・削除します。よろ。
Top comments (1)
If you are looking the latest solution of Stripe subscription integration in ASP.NET, check out the demo ==> techtolia.com/StripeSubscriptions/