Nuxt.js+LaravelでStripeのCheckoutの実装をやってみた【決済フォーム埋め込み編】
前回の記事の続きです。
概要
LaravelとNuxt.jsでStripeの決済処理を実装しました。
今回はCheckoutの「自前のページに決済フォームを埋め込む方法」の紹介をしていきたいと思います。
実装したもの
モーダルにStripeの決済フォームを埋め込み、顧客が1つの商品を購入して完了するまでの実装をしました
事前準備
前回説明したので、詳細は割愛しますが、以下の準備をしてください。
・StripeのAPIキーの取得
・商品の追加
・顧客アカウントの作成
処理の簡単な流れ
- フロントがバックエンドのCheckoutセッション作成APIを叩く(フロント)
- バックエンドがstripeのセッション作成APIを叩き、その結果をフロントに返す(バックエンド)
- フロントがモーダルを表示する
- フロントがレスポンスのclient_secretを使用し、stripeの決済フォームを作成、モーダルにマウントする(フロント)
- 決済をし、任意のページにリダイレクト(フロント)
バックエンドの実装(Laravel)
前回同様にstripe-phpという、 Stripe公式のphp用のライブラリを使用して実装しました。
ライブラリを使用し、StripeのCheckoutセッション作成APIを叩き、その結果を返すAPIの作成をしました。
重要なところの解説
$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に自由に処理を書くことができる。
ソースコード
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というライブラリを使用しました。
重要なところの解説
決済フォームの作成
バックエンドからもらった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にこだわりたい方はそちらを使用してください。ただし工数がかなりかかると思います、、、
Discussion
やった!
大変ありがとうございました!!!