ども、@kimihom です。
最近は Stripe でのサブスクリプション決済の実装について色々調べているので、その現状についてレポートする。
実装のユースケース
では早速、よくありがち(?) な SaaS での月初決済を例に挙げて、それに対応する方法についてご紹介する。
今回は、日本の財務管理を根底から変える、財務管理 SaaSをローンチする予定だと仮定しよう。その財務管理 SaaS は、3つのプランから成り立つ。今回はそのうちの Basic プランを月額2,000円でセットすると仮定する。利用企業は、その Basic プラン料金に加えて、特定の連携機能を使った分だけ課金される従量課金を提供するとしよう。つまり、月額2,000円 + 使った分だけの従量課金 を請求する料金体系となる。
月額2,000円だけの SaaS なら、めっちゃ簡単に実装できるのだけど、使った分だけの従量課金を追加しようとした途端に手間のかかる実装になる。月ごとに異なる金額となるし、前月の従量課金分をできる限り早く回収したいと考えるはずだ。てことで従量課金分は月初に決済すると想定する。これを実現する方法としては、2つ考えられる。
- プランの決済タイミングは顧客ごとに異なり、従量課金の部分は別で月初に決済する。つまり2つの決済タイミングを分ける
- 月初にプランの決済と従量課金分を合計した金額を決済する
2つ考えられると書いたけど、どう考えてもやりたいのは2つ目の方法だろう。そして、この月初に統一して決済する方法を、Stripe は最近になって提供するようになった。Congrats!
てな訳で、固定金額 + 従量課金な SaaS のユースケースに沿った実装サンプルを以下にご紹介する。
定額 + 従量課金の実装
さて、まずは月初に金額を統一させるためには、全ての Subscription の決済タイミングを月初に統一させる必要がある。これに対して、最近 Stripe API (2018-02-28)で登場した billing_cycle_anchor
を利用することで実現できる。
subscription = Stripe::Subscription.create( customer: self.stripe_customer_id, items: [ { plan: plan_name } ], prorate: true, trial_period_days: ENV['TRIAL_PERIOD'], billing_cycle_anchor: DateTime.now.next_month.beginning_of_month.to_i )
上記の実装で、「Trial 期間を設けつつ、アップグレード直後に billing_cycle_anchor
までの日割り料金を決済し、それ以降は毎月月初に決済する」というフローを構築できるはずだ。ひとまず、これで定額の方の決済はクリアできる。ちなみに、trial_period_days
を設けると、対象の Customer にクレジットカード登録がなくても、この Subscription を登録することができる。Trial 期間の間にクレカ登録してくれれば OK という流れを作れる。
続いて従量課金の部分に移ろう。これを実現するために、Stripe では "Invoice Item" ってのを提供してくれている。まさに、Subscription に対して追加で料金を請求できる仕組みである。
invoice = Stripe::InvoiceItem.create({ amount: amount, currency: currency, customer: self.stripe_customer_id, description: description })
上記の実装で、「対象 Customer の次の決済タイミングで、追加の料金を請求する」ことを実現できる。つまり、Subscription の月初の決済時に従量課金分を追加請求できるようになったってわけだ。上記の InvoiceItem の追加を、月初の定期決済の前に実行すれば良いことになる。めでたしめでたし。
この InvoiceItem がなかなか便利で、InvoiceItem にはマイナスの値をセットすることもできる。これは Subscription における Coupon の概念に近いんだけど、InvoiceItem の場合は顧客ごとにその割引金額を変えることができる。個人的には Coupon より柔軟性が高いので、InoviceItem を積極的に利用するのがいいのではないかと現時点では思っている。
その他の懸念事項
さて、それ以外で検討する項目について考えてみよう。
決済タイミングでのメール送信
まずは、決済成功/失敗時に顧客にメールを送りたいってことが考えられる。Stripe では、便利なことに決済成功/失敗/返金/カード期限間近 のタイミングで Stripe 側で用意した文言で自動でメールを送ってくれる機能を提供している。もちろん、言語を日本語に指定することも可能だ!てことで、 Stripe のレールに乗ればそもそもメール送信のために Webhook を使う必要がなくなる。
ただ、例えば顧客ごとにメール文言や言語を変えたいだとか、顧客だけでなく運営側にも通知を送りたいなどの細かい話になってくると、どうしても Webhook が必要になる。そこで問題が出てくるのが billing_cycle_anchor
を指定したことによる、Webhook の負荷問題だ。月初に ほぼ同じタイミングで一気に決済が走るため、Webhook が大量に飛んでくる問題が懸念される。これに対して聞いた噂によると、それなりに分散して Webhook を送ってくれるらしい。ただし、もしやる場合は Webhook 先を AWS Lambda などの Function as a Service 側に向けておいたほうがいいだろう。
失敗時の再決済
決済失敗時の再決済も、Stripe が勝手にやってくれる。例えば"定期決済に失敗した場合は3週間の間で3回トライする"などの設定をしておけば、Stripe 側で最適なタイミング(機械学習を使っているらしい)で再決済をおこなってくれる。そしてその失敗に対しても Stripe がメールを送ってくれる。それら再決済のトライに全部失敗した場合、定期購読をキャンセルしたりそのままにしたりするのも Stripe がやってくれる。
領収書のPDFダウンロード
SaaS を運用していると、領収書を PDF でダウンロードしたいって要望をよく受ける。これも、Stripe で自動で送ったメールの中に、"ブラウザで表示" リンクがあるので、そこをクリックして 印刷で PDF としてダウンロードすればいい。てことで無駄に PDF ダウンロードの機能を実装する必要もなくなる。自前でメールを送る実装にすると、 "Webで表示" の項目がないので自前で実装する必要が出てくるかもしれない。
終わりに
今回は Stripe での SaaS 決済についてありがちな設計とそこで出てくる課題解決について書いた。billing_cycle_anchor
登場以前は、定額+従量な決済を設計しづらかったけど、このオプションの登場でより柔軟な決済の設計に対応できるようになったと思う。
Stripe はいいサービスだけど、個人的には 当分の間は全く使わないであろう Radar
や 注文
、Connect
, Apple Pay
などのナビゲーションが常に左に表示されているのが ちょっと複雑さを醸し出してる感じがしている。たくさん機能が出てくるのはいいことだけど、シンプルと複雑のバランスについて考えさせられる。あと SQL で決済情報を収集できる最高にクールな機能である Stripe Sigma のナビゲーションなくなったけど、なんでだろ?
ひとまず今後はちょいちょい Stripe ネタも絡んでいく予定なので、期待していてほしい。