こんにちは。@gorou です。この記事はStripe Advent Calendar 2018の18日目の記事です。
Stripeの定期課金とても便利で活用させてもらっていますが、webhook(Stripe側で発生したイベントを、指定したURLにPOSTリクエストで詳細を通知してくれる仕組み)の流れ(ライフサイクル)が少しわかりにくいのと、しっかり動きを把握していないと意図した動作にならないため、定期課金のライフサイクルについてまとめてみます。
StripeのSubscriptionsはいろいろな料金プランを作成できますが、今回は料金体系が「定期利用」でかつ「段階別の料金設定なし」の定額を一定周期で課金する場合について説明します。
定期課金のライフサイクル
以前Stripe公式ドキュメントにフローチャートがあったのですが、無くなっていたので作ってみました。この順番でStripeのイベントが発生します。
Stripe APIのバージョンは高頻度でバージョンアップされています。ライフサイクルの仕様も変わることがあるため、ドキュメントも一緒にご確認ください。
初回課金
初回の課金では以下イベントが発生します。
customer.created
customer.subscription.created
invoice.created
invoice.finalized
charge.succeeded
invoice.payment_succeeded
このタイミングで課金に失敗した場合、charge.failed
がcustomer.created
イベントの後に発生します。2回目以降の再課金時にも同じイベントが発生するため、初回課金と再課金で何かしら違う処理がしたい場合、区別する情報がおそらくないため注意しましょう。
自動課金時(2回目以降)
2回目以降の自動定期課金では以下のイベントが順番に発生します。
invoice.upcoming
invoice.created
invoice.finalized
charge.succeeded
invoice.payment_succeeded
invoice.upcoming
が課金日の1週間前
くらいに発生して、invoiceのステータスがdraft
として作成されます。その後、課金日時になると残りのイベントが発生して自動課金が行われます。
課金が成功した場合は最終的にinvoice.payment_succeeded
が発生します。何らかの理由で課金に失敗した場合、charge.failed
が発生したあとにinvoice.payment_failed
が発生します。
課金に失敗した場合の処理
課金に失敗する理由はたくさんあり、利用者都合で偶然失敗してしまうこともあります(クレジットカードの更新に間に合わなかったとか)。そこで課金のリトライ処理を導入すべきなのですが、定期定時の処理のシステム開発はとても大変です。
Stripeはこの課金リトライの仕組みをもっており、回数・間隔を設定するだけで対応できてしまいます。
リトライルール
Stripe管理画面の「Billing」 → 「設定」を開くと自動支払いのリトライルールの設定ができます。
「スマート督促処理機能」を選択すると、自動的にAIで利用者の傾向から再課金のタイミングを調整して、指定期間で最大4回再課金のスケジュールをたてて実施してくれます(1ヶ月は28日間になるようです)。
「カスタム督促ルール」を選択すると、最大3回の再課金の枠が設定でき、タイミングを任意に決めることができます。かっちり決まったルールで運用したい場合はこちらが良いでしょう。
最終アクション
リトライルール設定ですべての再課金に失敗した場合の動作になります。「最終アクション」の設定によってライフサイクルのイベントが若干変わってきます。
「定期支払のキャンセル」は、StripeのCustomerのSubscriptionが削除されます。Subscriptionだけ削除したい場合はこちらを選択しましょう。
この場合customer.subscription.deleted
イベントが発生します。
「定期支払を未払いとしてマーク」は、Invoiceのステータスがunpaid
に変更された後、
customer.subscription.deleted
invoice.payment_faild
の順番でイベントが発生します。こちらは後でちゃんと請求したい場合や、猶予期間を設けたい場合に利用しましょう。
「定期支払を現状のままにする」は、特にイベントは発生しません。課金に失敗した場合の処理時のイベントのみ発生します。何かしら人力での運用を考えている場合はこちらでしょうか(あまりユースケースが思い浮かばなかったです)。
最終アクション時の個別処理の実装方法
上記のように、最終アクションによって発行されるイベントが異なりますが、同じイベントが最終アクション時以外でも発生している点に注意です。最終アクションを行ったあとにメールを送りたい・データを削除したい場合は以下のように最終アクションであることの判定を行うと良いでしょう。
最終アクションが「定期支払のキャンセル」の場合
この場合、customer.subscription.deleted
イベントが発生しますが、他のタイミングで発生するcustomer.subscription.deleted
と区別するにはrequest
がnullではない
ことを確認すれば良いようです。
webhookでcustomer.subscription.deleted
を受け取りrequestがnullではない
場合に個別処理を実行しましょう。
最終アクションが「定期支払を未払いとしてマーク」の場合
この場合、invoice.payment_faild
を利用しますが、自動課金時にも発生します。区別するにはdata.object.next_payment_attempt
を確認します。
next_payment_attempt
には次回の再課金日時のタイムスタンプが格納されるのですが、リトライがまだ計画されていれば値が設定されており、最終アクションの場合はnull
になります(ちなみにdata.object.attempt_count
が自動再課金回数です)。
invoice.payment_faild
イベントのdata.object.next_payment_attempt
がnull
の場合に個別処理を実装しましょう。以下コードはPHPでのサンプルです(エラー処理は省略しています)。
<?php $body = file_get_contents("php://input"); $event_json = json_decode($body); \Stripe\Stripe::setApiKey('privateKey'); \Stripe\Stripe::setApiVersion('API_VERSION'); $event = \Stripe\Event::retrieve($event_json->id); if ($event->type === 'invoice.payment_failed') { if (!is_null($event->data->object->next_payment_attempt)) { // 自動課金に失敗した場合の処理 } else { // 最終アクション時の処理(データ削除や退会処理など) } }
まとめ
Stripeの定期課金のイベントライフサイクルについてまとめました。Stripeの設定とイベントの流れを把握しないとわかりにくいところですが、上記のライフサイクルを参考にStripeの定期課金の仕組みをぜひ作ってみてください。
それでは明日もStripe Advent Calendar 2018の記事をお楽しみに!