この記事は個人ブログと同じ内容です
Laravelを使用したStripeのCheckoutの決済済みかどうかの判定の実装
前回と前々回の記事の続きです。 https://zenn.dev/ota_rg/articles/5bb03b17198f58 https://zenn.dev/ota_rg/articles/836c891b481c23
概要
今回はCheckoutで決済した後の決済、未決済の判定について紹介していきたいと思います。 バックエンドはLaravelを使用しています。
決済、未決済の判定をする方法は以下のように2種類あり、今回はそれらについて解説しています。 ・StripeのPaymentIntentの検索APIを使用し判定する方法 ・Webhookを使用して判定する方法
結論、2種類の方法のうちWebhookのほうが早いので、そちらをお勧めします。
StripeのPaymentIntentの検索APIを使用し判定する方法
PaymentIntentの検索APIを使用し、PaymentIntentのstatusがsucceededだと決済が完了していることがわかります。 https://stripe.com/docs/api/payment_intents/object#payment_intent_object-status
この方法は、Webhookに比べると遅いです。 決済して10秒くらい経ってもstatusがかわらないことがあるので、リトライ処理を作ったほうがいいです。
PaymentIntentとは
「支払いされたか、支払い方法はどうしたか」など支払いに関する情報が保存されているオブジェクトです。 これはCheckoutを使用した場合、決済をした後に作成されます。
https://stripe.com/docs/api/payment_intents
なぜPaymentIntentの検索APIを使用したか
Checkoutのセッションでも決済、未決済判定ができますが、 セッションIDをDBに保存しておくと考えたときに、 「決済前に2人セッションを作成したら、決済されるセッションと決済されないセッションができるので、どちらをDBに保存すればいいのかわからない」 という懸念点があります。
また、セッションの検索APIは、セッションIDでしか検索することができません。 よって、別の方法を考える必要がありました。
そこでMetadataという、自由に構造化情報を設定できるパラメーターを使用して検索できるPaymentIntentの検索APIを使用した。
https://stripe.com/docs/api/payment_intents/search
実装
metadataの設定
以下のようにCheckoutセッション作成時のpayment_intent_data['metadata']
のパラメーターに自由にmetadataを設定することができます。
$checkout = $stripe->checkout->sessions->create([ 'mode' => 'payment', // 支払いモード 'payment_method_types' => ['card'], ・ ・ ・ ・ // PaymentIntentのMetadata設定 'payment_intent_data' => [ 'metadata' => [ 'reference_report_request_id' => 2, ], ], ],[ 'idempotency_key' => 'test', ]);
サンプルコード
支払いが完了していて、顧客とメタデータのitem_idで絞り込んだPaymentIntentを検索するコードです。 PaymentIntentのstatusがsucceededだと支払いが完了しています。
public function getSession(Request $request): JsonResponse { $config = config('define.stripe.token'); $stripe = new StripeClient($config); //PaymentIntentsの検索 $paymentIntents=$stripe->paymentIntents->search([ 'query' => 'status:\'succeeded\' AND customer:\'customer_id\' AND metadata[\'item_id\']:\'1\'', ]); return response()->json($paymentIntents); }
重要なところの解説
PaymentIntentsの検索は$stripe->paymentIntents->search()
で行っていますが、以下のクエリの書き方に注意する。
- =が:になる
- 'が「\'」になる
Webhookを使用した方法
StripeのWebhookを使用し、支払い完了イベントである「payment_intent.succeeded」を受け取ったら支払いが完了していることがわかります。
この方法はPaymentIntent検索APIを使う方法より、早く支払い完了であることがわかるのでお勧めです。
準備
- Webhookのendpoint_secretの取得 本番または開発環境 https://dashboard.stripe.com/test/webhooks/create?endpoint_location=hosted
ローカル環境 https://dashboard.stripe.com/test/webhooks/create?endpoint_location=local
- 受信するイベントを絞り込む 本番または開発環境のみで可能で、エンドポイント作成の時の「リッスンするイベントの選択」で 「payment_intent.succeeded」を選択することで、支払い成功以外のイベントを受信しなくなる
実装
サンプルコード
public function webhook() { // 参考:https://stripe.com/docs/payments/checkout/fulfill-orders Stripe::setApiKey(env('STRIPE_SECRET')); // StripeのWebhookのendpoint_secret $endpoint_secret = 'endpoint_secret'; $payload = @file_get_contents('php://input'); $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE']; $event = null; try { $event = Webhook::constructEvent( $payload, $sig_header, $endpoint_secret ); } catch(\UnexpectedValueException $e) { // Invalid payload http_response_code(400); exit(); } catch(\Stripe\Exception\SignatureVerificationException $e) { // Invalid signature http_response_code(400); exit(); } if($event->type==='payment_intent.succeeded'){ $payment_intent = event.data.object //支払いが完了した後に行いたい処理をかく } http_response_code(200); }
重要なところの解説
以下のようにpayment_intent.succeededのイベントを受け取るとevent.data.object
でPaymentIntentを取得することができる。
PaymentIntentに事前にメタデータを設定することで、自前DBの商品IDやユーザーIDで誰のものかを特定することができる。
if($event->type==='payment_intent.succeeded'){
$payment_intent = event.data.object
//支払いが完了した後に行いたい処理をかく
}
終わりに
今回はLaravelを使用したStripeのCheckoutでの決済済みかどうかの判定の解説をしました。 PaymentIntentのステータスが変わる時間にはムラがあるということが、実装してから分かったので、その対応策を考えたり、リトライ処理を仕込むのが大変でした。 皆さんは最初からWebhookを使いましょう。
現在 back check 開発チームでは一緒に働く仲間を募集中です。 herp.careers