猫でもわかるWebプログラミングと副業

本業エンジニアリングマネージャー。副業Webエンジニア。Web開発のヒントや、副業、日常生活のことを書きます。

Stripe の Checkout Session と Webhook で届いた Subscription の紐付け

f:id:yoshiki_utakata:20220411180950p:plain

Stripe Checkout と Webhook

Stripe で Subscription を実装する時に、 Stripe Checkout というのを利用できます。

Stripe Checkout では、クレジットカード入力の画面を Stripe 側が用意してくれているので、簡単に決済画面の実装ができます。

Stripe 公式のドキュメントの Quickstart を見ると、実装のチュートリアルがあります。

stripe.com



決済結果等は Webhook でサーバーに送られてきます。

Stripe の Quickstart を見と、 customer.subscription.created イベントが飛んできた時になんかやれ、って書いてあります。

基本的には、この customer.subscription.created をもってして、決済完了&サブスクの手続きが完了したとして、アプリケーションのDBにサブスクリプション情報を保存して、サービス提供をするのが筋です。

Checkout session と Webhook の紐付け

ここで重要になるのが、Webhook で飛んできた Subscription の情報が、アプリケーション(我々が作っているもの)のどのユーザーのものなのかを特定する必要があることです。

この Quickstart の実装だと、誰が契約した Subscription なのかわからずに詰みます(多分)

私も困りました。

Subscription の metadata を使う

Subscription に限らす、Stripe のオブジェクト(決済、請求書、プラン、などのオブジェクト)には、 metadata が付与できます。

Stripe Checkout のセッションを作成するときに、subscription_data というのを渡すと、Subscription に任意のデータを付与することができます。



Stripe で Checkout session を作成するときに使っているAPIは以下です。

https://stripe.com/docs/api/checkout/sessions/create


metadata の付与の仕方ですが、PHP だとこんな感じですね。

$session = Session::create([
    'success_url' => 'http://xxxxx/success?session_id={CHECKOUT_SESSION_ID}',
    'cancel_url' => 'http://xxxxx/canceled',
    'mode' => 'subscription',
    'line_items' => [
        ['price' => 'price id を指定', 'quantity' => 1],
    ],
    'subscription_data' => [
        'metadata' => [
            'user_id' => 'hoge',
        ],
    ],
]);

こうすると、 Webhook で届く Subscription 情報の metadata に user_id の情報が入ってくるので、どのユーザーの処理が完了したのかがわかります。

customer.subscription.created 以外の Webhook を使うのは?

customer.subscription.created 以外にも、様々な Webhook が飛んできます。

charge.succeeded だったり、checkout.session.completed だったり。

これらを組み合わせれば、サブスクリプションがどのユーザーのものか特定できそうな気がしますが、どんな順番で Webhook が飛んでくるかわかりません。(ネットワークの回線状況の問題もありますし...)

順序保証があったとしても、Webhookを処理している途中で次のWebhookが届いてしまう可能性もあるため、正確に処理しようとすると実装は複雑になります。

とすると、やはり、 subscription の作成に成功した customer.subscription.created を受け取って処理する、というのがシンプルかと思います。

success_url に到達した瞬間にサービス提供するのは?

success_url に到達した時点では、決済が正常に完了しているかわかりませんし、サブスクリプションの処理が完全に終了しているかわかりません。

やはり、 customer.subscription.created の Webhook を受け取ってサービス提供するのが無難かなと思います。