Stripe Billingで定期課金システムを提供している場合、サブスクリプションの更新時の請求は以下のような流れです。
- サブスクリプションによって、draft ステータスの新しいインボイスが生成されます。
- インボイスは、作成されてから約 1 時間後に確定されます (変更はできなくなります)。
- ステータスは open に設定され、Stripe はデフォルトの支払い方法を使用して自動的に支払いを試みます。
- 支払いが成功すると、ステータスは paid に更新されます。
- 支払いが失敗すると、インボイスは open のままで、サブスクリプションが past_due になります。
Docs: https://stripe.com/docs/billing/subscriptions/overview#invoice-lifecycle
「サブスクリプションの更新」と「請求完了」までには、1時間の時間差がある
先ほどのステップの2番目を確認すると、作成した請求書が実際に請求されるのは1時間後であることがわかります。
Webhookのイベントで整理すると次のイメージです。
- サブスクリプションを新しいサイクルに更新:
customer.subscription.updated
- 請求書を作成:
invoice.created
- 1時間経過
- 請求書を確定:
invoice.finalized
- カードなどで請求に成功:
invoice.paid
- 請求に失敗:
invoice.payment_failed
ポイントとしては、「カードなどの決済情報が削除されている場合でも、サブスクリプションの更新自体は可能な」ことです。
決済に関する情報はサブスクリプションが作成した請求書側で処理されますので、更新自体は成功します。
その後、1時間後に請求内容の確定を請求を行う際に、決済に失敗したことが記録され、Stripeの用意する決済の再試行を経てサブスクリプションのステータスが未払いに変わります。
決済失敗時のサブスクリプション更新の流れ
-
invoice.payment_failed
: 請求に失敗 -
invoice.updated
: 失敗回数と次回のリトライ時間を請求書データに追加 - (管理画面で設定したリトライ回数分、上記の2ステップを繰り返す)
-
invoice.updated
: リトライを停止する -
customer.subscription.updated
またはcustomer.subscription.deleted
: 管理画面で設定した処理を行う
請求書発行から確定までの1時間にできること
Stripeで発行した請求書は、1度確定すると変更ができません。
請求内容のカスタマイズが必要な場合は、作成されてから確定するまでの1時間で実行する必要があります。
そのため、通常以下のようなコードを実装したWebhook APIを用意します。
const data = event.data.object;
if (event.type === 'invoice.created') {
if (
// サブスクリプションが作成した請求書である。
data.billing_reason === "subscription_cycle" &&
// 確定していない請求書である。
data.status === "draft"
) {
// ここで請求書を操作する
}
}
invoice.created
イベントを利用しますが、「サブスクリプションが作成した」「確定していない」請求書のみを対象にする必要があります。
そのため、billing_reason
やstatus
を利用して、対象の請求書かを確認します。
実際の処理では、請求書invoices
や請求内容invoiceItems
の操作を実施します。
// 請求書の更新
await stripe.invoices.update(data.id, /.../)
// 請求書の中身を更新
await stripe.invoiceItems.create({
customer: data.customer,
invoice: data.id,
/.../
})
即座に請求したい場合は、APIで確定・請求処理を実行する
1時間の猶予時間なしに、できるだけ早く請求を行いたい場合は、Webhookイベントで確定処理を行います。
// 請求書の確定
await stripe.invoices.finalizeInvoice(data.id)
// カードに請求を行う
await stripe.invoices.pay(data.id)
finalizeInvoice
だけですと、請求内容は確定できますが、請求自体はまだ実行されません。
その場で請求・決済まで行いたい場合は、pay
を実行しましょう。
また、「Webhookのイベントを受信後にAPIを呼び出す」ため、「完全に即時」にはなりません。
が、Stripe内部でイベントが起き次第確定処理のAPIを呼び出すことができます。
「請求書を手動で送信」する場合はsendInvoice
を利用する
銀行振込などのメールで請求書を送付するタイプのサブスクリプションでは、pay
の代わりにsendInvoice
を利用します。
// 請求書の確定
await stripe.invoices.finalizeInvoice(data.id)
// 請求メールを送付する
await stripe.invoices.sendInvoice(data.id)
どちらの請求方法にもサポートさせたい場合
InvoiceのWebhookイベントやデータには、請求方法を表すcollection_method
属性が含まれています。この値を利用して、「カードではpay
APIで即時決済、銀行振込はsendInvoice
でメール送信」のような振り分けも可能です。
await stripe.invoices.finalizeInvoice(data.id)
if (data.collection_method === 'send_invoice') {
await stripe.invoices.sendInvoice(data.id)
} else {
await stripe.invoices.pay(data.id)
}
ドキュメント