この記事は個人ブログと同じ内容です
Nuxt.js+LaravelでStripeのCheckoutの実装をやってみた【決済フォーム埋め込み編】
前回の記事の続きです。 https://zenn.dev/ota_rg/articles/5bb03b17198f58
概要
LaravelとNuxt.jsでStripeの決済処理を実装しました。 今回はCheckoutの「自前のページに決済フォームを埋め込む方法」の紹介をしていきたいと思います。
https://stripe.com/docs/payments/checkout/how-checkout-works?payment-ui=embeddable-payment-form
実装したもの
モーダルにStripeの決済フォームを埋め込み、顧客が1つの商品を購入して完了するまでの実装をしました
事前準備
前回説明したので、詳細は割愛しますが、以下の準備をしてください。
- StripeのAPIキーの取得
- 商品の追加
- 顧客アカウントの作成
処理の簡単な流れ
- フロントがバックエンドのCheckoutセッション作成APIを叩く(フロント)
- バックエンドがstripeのセッション作成APIを叩き、その結果をフロントに返す(バックエンド)
- フロントがモーダルを表示する
- フロントがレスポンスのclient_secretを使用し、stripeの決済フォームを作成、モーダルにマウントする(フロント)
- 決済をし、任意のページにリダイレクト(フロント)
バックエンドの実装(Laravel)
前回同様にstripe-phpという、 Stripe公式のphp用のライブラリを使用して実装しました。 ライブラリを使用し、StripeのCheckoutセッション作成APIを叩き、その結果を返すAPIの作成をしました。
https://stripe.com/docs/api/checkout/sessions/create https://github.com/stripe/stripe-php
重要なところの解説
$stripe->checkout->sessions->create()
でStripeのCheckoutセッション作成APIを叩いています。
前回も説明したので、今回は埋め込み式にする上で必要なパラメーターのみ解説します。
ui_mode
このパラメーターをembeddedにすることで、埋め込み式に変わります。 デフォルトはhostedとなっており、Stripeの決済ページに移動する方法になっています。
return_url
埋め込み式の場合のみ使用し、決済成功後のリダイレクト先のURLを指定する。 Stripeの決済ページに移動する方の時は、success_urlとcancel_urlとなっていて、少し違うので注意する
redirect_on_completion
if_requiredに設定することで、決済が成功した後にreturn_urlで指定したURLにリダイレクトしなくなり、 フロントエンドでstripe.initEmbeddedCheckoutの時のonCompleteに自由に処理を書くことができる。
https://stripe.com/docs/payments/checkout/custom-redirect-behavior
ソースコード
public function createCheckoutSession(): JsonResponse { /** @var string $config */ $config = config('define.stripe.token'); $stripe = new StripeClient($config); //Checkoutセッション作成 $checkout = $stripe->checkout->sessions->create([ // 商品 'line_items' => [[ 'price' => 'price_id',//商品ID 'quantity' => 1,//個数 ], ], 'mode' => 'payment', // 支払いモード 'customer' => 'customer_id', // 顧客ID 'ui_mode' => 'embedded',// 埋め込み式にするため //支払い成功時のリダイレクト先 ({CHECKOUT_SESSION_ID}とするとセッションIDが取得できる) 'return_url' => 'http://localhost:8080/success', // 税金を自動徴収するかどうか(3万の商品だったら、決済ページで3万3千円になる) 'automatic_tax' => [ 'enabled' => true, ], //フロントのstripe.initEmbeddedCheckoutの時にonCompleteを使用することができる。ただし、return_urlには行かなくなる //参考:https://stripe.com/docs/js/embedded_checkout/init#embedded_checkout_init-options-onComplete 'redirect_on_completion'=> 'if_required', // 支払い方法を保存するかどうか 'payment_method_options' => [ 'card' => [ 'setup_future_usage' => 'on_session', ], ], // 支払い方法 'payment_method_types' => ['card'] ]); return response()->json($checkout); }
フロントエンドの実装
ボタンを押したら、バックエンドのセッション作成のAPIを叩き、モーダルを表示し、レスポンスのclientSecretを使用し、Stripeの決済フォームを作成し、モーダルにマウントするようになっています。
前回同様に、stripe-jsという、Stripeの公式のJavaScript用のライブラリを使用しました。 モーダルは、vue-js-modalというライブラリを使用しました。 https://www.npmjs.com/package/vue-js-modal
重要なところの解説
決済フォームの作成
バックエンドからもらったclientSecretを使用し、ライブラリのinitEmbeddedCheckout()を呼べば、 Stripeの決済フォームを作成します。
const checkout = await stripe?.initEmbeddedCheckout({ clientSecret: res.data.client_secret, }) // Mount Checkout checkout?.mount('#checkout')
マウントしたフォームの削除
Stripeの決済フォームを一度マウントした後に、そのままもう一度フォームを作成し、マウントしようとするとエラーになる。 なので、以下のようにモーダルを閉じるたびにマウントしたフォームを削除する必要があります。 vue-js-modalを使用しているので、hide()に処理を書くだけでモーダルが閉じた時の処理を書くことができる。
async hide() { // 一度destroyしないとcheckoutが残ってしまうので削除 await this.checkout?.destroy() await this.$modal.hide('modal-content') },
ソースコード
<template> <div> <div> <button @click="submit">支払いをする</button> </div> <modal name="modal-content" height="auto" :scrollable="true" :click-to-close="false" > <button @click="hide">閉じる</button> <div id="checkout"> <!-- Checkout will insert the payment form here --> </div> </modal> </div> </template> <script lang="ts"> import Vue from 'vue' import { StripeEmbeddedCheckout, loadStripe } from '@stripe/stripe-js' import axios from 'axios' export default Vue.extend({ name: 'StripeTest', head: () => ({ title: 'StripeTest | back check', }), components: {}, data() { return { publishableKey: process.env.STRIPE_PUBLISHABLE_KEY, loading: false, clientSecret: '', checkout: undefined as StripeEmbeddedCheckout | undefined, } }, methods: { async submit() { const stripe = await loadStripe( process.env.STRIPE_PUBLISHABLE_KEY ? process.env.STRIPE_PUBLISHABLE_KEY : '', ) await axios .post('http://localhost:8080/api/create_checkout_session') .then(async (res) => { this.$modal.show('modal-content') this.clientSecret = res.data.client_secret // StripeのCheckoutの作成 const checkout = await stripe?.initEmbeddedCheckout({ clientSecret: res.data.client_secret, }) this.checkout = checkout // Mount Checkout checkout?.mount('#checkout') }) }, async hide() { // 一度destroyしないとcheckoutが残ってしまうので削除 await this.checkout?.destroy() await this.$modal.hide('modal-content') }, }, }) </script>
終わりに
今回は、StripeのCheckoutの実装で決済フォームを自前のページに埋め込む方法で行いました。 前回の決済フォームに遷移しての決済と比べると、モーダルなどを使用した分少し工数はかかりましたが、そこまで工数がかかるわけではないので、UIを重視したい方はこちらをお勧めします。 Stripeには、決済フォーム自体を自前で作る方法もあるので、さらにUIにこだわりたい方はそちらを使用してください。ただし工数がかなりかかると思います、、、
現在 back check 開発チームでは一緒に働く仲間を募集中です。 herp.careers