Stripe定期課金のライフサイクル

f:id:gurimmer:20181218113854j:plain
Stripeのライフサイクルを実際に調べていたときのメモ

こんにちは。@gorou です。この記事はStripe Advent Calendar 2018の18日目の記事です。

Stripeの定期課金とても便利で活用させてもらっていますが、webhook(Stripe側で発生したイベントを、指定したURLにPOSTリクエストで詳細を通知してくれる仕組み)の流れ(ライフサイクル)が少しわかりにくいのと、しっかり動きを把握していないと意図した動作にならないため、定期課金のライフサイクルについてまとめてみます。

StripeのSubscriptionsはいろいろな料金プランを作成できますが、今回は料金体系が「定期利用」でかつ「段階別の料金設定なし」定額を一定周期で課金する場合について説明します。

定期課金のライフサイクル

以前Stripe公式ドキュメントにフローチャートがあったのですが、無くなっていたので作ってみました。この順番でStripeのイベントが発生します。

f:id:gurimmer:20181218095212p:plain
Stripe定期課金のライフサイクル

Stripe APIのバージョンは高頻度でバージョンアップされています。ライフサイクルの仕様も変わることがあるため、ドキュメントも一緒にご確認ください。

stripe.com

初回課金

初回の課金では以下イベントが発生します。

  1. customer.created
  2. customer.subscription.created
  3. invoice.created
  4. invoice.finalized
  5. charge.succeeded
  6. invoice.payment_succeeded

このタイミングで課金に失敗した場合、charge.failedcustomer.createdイベントの後に発生します。2回目以降の再課金時にも同じイベントが発生するため、初回課金と再課金で何かしら違う処理がしたい場合、区別する情報がおそらくないため注意しましょう。

自動課金時(2回目以降)

2回目以降の自動定期課金では以下のイベントが順番に発生します。

  1. invoice.upcoming
  2. invoice.created
  3. invoice.finalized
  4. charge.succeeded
  5. invoice.payment_succeeded

invoice.upcoming課金日の1週間前くらいに発生して、invoiceのステータスがdraftとして作成されます。その後、課金日時になると残りのイベントが発生して自動課金が行われます。

課金が成功した場合は最終的にinvoice.payment_succeeded が発生します。何らかの理由で課金に失敗した場合、charge.failedが発生したあとにinvoice.payment_failedが発生します。

課金に失敗した場合の処理

課金に失敗する理由はたくさんあり、利用者都合で偶然失敗してしまうこともあります(クレジットカードの更新に間に合わなかったとか)。そこで課金のリトライ処理を導入すべきなのですが、定期定時の処理のシステム開発はとても大変です。

Stripeはこの課金リトライの仕組みをもっており、回数・間隔を設定するだけで対応できてしまいます。

リトライルール

f:id:gurimmer:20181218101217p:plain
リトライルールのカスタム督促の設定

Stripe管理画面の「Billing」 → 「設定」を開くと自動支払いのリトライルールの設定ができます。

「スマート督促処理機能」を選択すると、自動的にAIで利用者の傾向から再課金のタイミングを調整して、指定期間で最大4回再課金のスケジュールをたてて実施してくれます(1ヶ月は28日間になるようです)。

「カスタム督促ルール」を選択すると、最大3回の再課金の枠が設定でき、タイミングを任意に決めることができます。かっちり決まったルールで運用したい場合はこちらが良いでしょう。

最終アクション

リトライルール設定ですべての再課金に失敗した場合の動作になります。「最終アクション」の設定によってライフサイクルのイベントが若干変わってきます。

「定期支払のキャンセル」は、StripeのCustomerのSubscriptionが削除されます。Subscriptionだけ削除したい場合はこちらを選択しましょう。 この場合customer.subscription.deletedイベントが発生します。

「定期支払を未払いとしてマーク」は、Invoiceのステータスがunpaidに変更された後、

  1. customer.subscription.deleted
  2. invoice.payment_faild

の順番でイベントが発生します。こちらは後でちゃんと請求したい場合や、猶予期間を設けたい場合に利用しましょう。

「定期支払を現状のままにする」は、特にイベントは発生しません。課金に失敗した場合の処理時のイベントのみ発生します。何かしら人力での運用を考えている場合はこちらでしょうか(あまりユースケースが思い浮かばなかったです)。

最終アクション時の個別処理の実装方法

上記のように、最終アクションによって発行されるイベントが異なりますが、同じイベントが最終アクション時以外でも発生している点に注意です。最終アクションを行ったあとにメールを送りたい・データを削除したい場合は以下のように最終アクションであることの判定を行うと良いでしょう。

最終アクションが「定期支払のキャンセル」の場合

この場合、customer.subscription.deletedイベントが発生しますが、他のタイミングで発生するcustomer.subscription.deletedと区別するにはrequestnullではないことを確認すれば良いようです。

stripe.com

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_attemptnullの場合に個別処理を実装しましょう。以下コードは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の記事をお楽しみに!