stripe webhookをローカルでテストする

# はじめに
[GMOペパボエンジニア Advent Calendar 2024](https://adventar.org/calendars/10036)の4日目の記事です。

webシステムに決済を導入できるstripe
決済代行サービスは他にもありますが、モダンで合理的な仕組みが好みの方もいるかもしれません。
また、Laravelをお使いのPHPerの方々には、Laravel Cashier Stripe等で馴染みが深いかもしれません。
そんなstripeの決済、特にsubscriptionとwebhookを中心にローカル環境でのテスト方法を解説します。

# webhookとは
はじめに、webhookの簡単な説明します。
webhookとは、「システム上でイベントが発生した際に外部へ通知する仕組み」のことです。
今回の場合、stripeでイベントが発生した際にwebシステムへ通知することを指します。
では、なぜwebhookが必要なのでしょうか？
stripeで定義している、オンセッション・オフセッションから紐解いていきます。

## オンセッション
AmazonのようなECサイトの場合、ユーザーが決済を実行することで決済処理が実施されます。
このようなユーザーが起点となる支払いをオンセッションと言いますが、決済の結果はAPIの返却値等で即座にわかります。
![onsession.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3871802/850481d0-8558-9f45-8648-75905d191e0c.png)

## オフセッション
NETFLIXの会費の場合、初回はオンセッションで実施されますが、翌月以降はNETFLIXが指定の日付に決済を実施します。
このようなユーザー起点ではなく、自動的に開始される支払いをオフセッションと言います。
オフセッションの場合、webシステムは決済実行を検知できないため、webhookで通知することによりメール送信や領収書発行といった後続の処理を実施できるようになります。

余談ですが、subscriptionの際に、決済代行サービスが起点となり決済を行う方式が全てではありません。
他の決済代行サービスでは、決済日にwebシステム側で一件づつ決済実行APIを実施していたり、csvファイル等で一括決済依頼をかけたりといった方法も存在します。

# webhookのテスト
webhookとは、「システム上でイベントが発生した際に外部へ通知する仕組み」のことです。
つまり、stripeからwebhookを受信できる状態でないといけません。
本番環境・テスト環境といった外部公開している環境であれば問題はありませんが、ローカルPCではwebhookは受信できません。
それでは、どのようにテストすればよいのでしょうか。

## ngrok
１つ目に紹介するのは、ngrokです。
ngrokは、ローカルPCのwebシステムを外部公開できます。

ngrokをインストールし、コマンドを打つと下記形式のURLが発行されます。
`http://XXXXXXXX.ngrok.io`

一見、ngrokを使えば万事解決ですが、ngrokは起動するたびにURLが変わります。
そのため、起動するたびにstripeのwebhookの送信先URLを変更しなければなりません。
URLの固定もできますが、有料プランとなります。

https://ngrok.com/

## ローカルIPアドレス固定 + webhook振り分け
２つ目に紹介するのは、少しマイナーな方法です。

![local.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3871802/906827d2-8bbf-eda0-4c9a-edd2b71f94a4.png)

1. PCのローカルIPアドレスを固定します
1. stripeにAPIでアクセスする際に各開発者毎のmetadataを埋め込みます
1. 社内にwebhookをmetadataで分配するコンテナを自作します
1. webhookを分配するコンテナを外部公開し、ルーターで静的IPマスカレードを設定します
1. stripeのwebhookの送信先を公開したURLに設定します

これでいつでも手間なしでwebhookが飛んできます。

以下が問題点です。
- 設定が面倒な上に、技術的難易度が易しくない
- リモートワークの時、常にVPN必須
- DHCP絡みが面倒。客先行った時面倒

大問題です。

## Stripe CLI
真打ち登場、Stripe CLIです。
昔は存在しませんでした。

Stripe CLIをインストールして、下記コマンドを叩くだけです。
~~~
stripe login
stripe listen --forward-to localhost:8080/stripe_webhook
~~~

`localhost:8080/stripe_webhook`は各環境毎に変えてください。
また、stripeのwebhookの設定は、コマンド実行時に自動で登録されます。
ngrokと大差ないと思われるかもしれませんが、受信したイベントをCLI上で表示してくれるので、開発時のエラーにも優しい作りです。

https://docs.stripe.com/stripe-cli?locale=ja-JP

https://docs.stripe.com/webhooks

# subscriptionのテスト
webhookは受信できるようになりました。
しかし、subscriptionのテストでは時間経過による状態変化が発生しなければなりません。
例えば、毎月1日に決済するwebシステム、ローカル環境で初回決済後、次回決済日が30日後だとします。
下記のようなテストをしたいです。

- subscriptionの次回決済日3日前に下書きの請求を作成
- subscriptionの次回決済日に決済試行し失敗
- 次回決済日の2日後に決済リトライ

正確にテストする方法はないでしょうか。

## subscriptionの次回決済日を変更
subscriptionの次回決済日を変更することで、対応します。

1. subscriptionの次回決済日を今日から4日後の日付に設定します
1. 翌日まで待ちます
1. 下書き請求が作成されているか確認します
1. 次回決済日を今日から1日後の日付に設定します
1. 翌日まで待ちます
1. 決済が失敗していること確認します
1. 2日待ちます
1. 決済リトライが実施されたことを確認します

この間、PCは起動したままでないといけません。
外部テスト環境で実施するにしても、時間がかかります。
当時は苦肉の策だったんです。もっと良い方法はあったのだろうか。

## テストクロック
stripe側のsubscriptionの時間を進めることができる、テストクロックが存在します。
流石stripeです。
こちらも昔は存在しなかったですし、発表されてからしばらくの間はAPI経由での実行しかできませんでした。
今では、テスト環境の管理画面でポチポチするだけで簡単に実施できます。

1. テストクロックの時刻をsubscriptionの次回決済日3日前に設定します
1. 下書き請求が作成されているか確認します
1. テストクロックの時刻を次回決済日に設定します
1. 決済が失敗していること確認します
1. テストクロックの時刻を次回決済日の2日後に設定します
1. 決済リトライが実施されたことを確認します

Stripe CLIと組み合わせることで、ローカルPCでも簡単にテストができました。

![screencapture-dashboard-stripe-test-subscriptions-sub-1QL1OeASu7fmrjrBOYN696S6-2024-11-17-23_54_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3871802/3ef399a2-b911-b09c-b782-4260ba8698fe.png)

![screencapture-dashboard-stripe-test-subscriptions-sub-1QL1OeASu7fmrjrBOYN696S6-2024-11-17-23_54_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3871802/dd1d79e4-dad3-33a2-11d6-700321b3ba98.png)

https://docs.stripe.com/billing/testing/test-clocks?locale=ja-JP

# 終わりに
今回はstripeのsubscriptionとwebhookのテスト方法を解説しました。
以前と比べて格段にテストがしやすくなっていますので、ぜひ試してみてください。