この記事は、Vue Advent Calendar 2022 12日目の記事です。
Stripeを利用してサブスクリプションやオンライン決済を処理するには、フロントエンドアプリだけでなくサーバーサイドの処理も必要です。
Vueの場合、Nuxtを利用することで、フロントエンド・サーバーサイドのアプリを1つにまとめて構築・運用できます。
今回は、2022年11月にローンチされたばかりの、Nuxt3を利用してサブスクリプションの申し込みフォームを用意する方法を紹介します。
ViteでNuxt3プロジェクトをセットアップする
今回プロジェクトのセットアップには、Viteを利用しました。
$ yarn create vite
プロジェクト名を指定した後、「利用するフレームワーク」と「アプリの設定(JS / TS / create-vueをカスタマイズ / nuxt)」を選べますので、Vue
とNuxt
を選びましょう。
✔ Project name: … vite-nuxt
✔ Select a framework: › Vue
✔ Select a variant: › Nuxt ↗
Nuxtがインストールされます。
Need to install the following packages:
nuxi@3.0.0
Ok to proceed? (y) y
Nuxi 3.0.0 10:12:00
✨ Nuxt project is created with v3 template. Next steps: 10:12:01
› cd vite-nuxt 10:12:01
› Install dependencies with npm install or yarn install or pnpm install 10:12:01
› Start development server with npm run dev or yarn dev or pnpm run dev 10:12:01
✨ Done in 13.99s.
最後に、作成したプロジェクトディレクトリへ移動して、ライブラリをインストールします。
$ cd vite-nuxt
$ yarn
$ yarn dev
起動に成功すれば、ローカルで確認するURLが表示されますので、アクセスしましょう。
[get-port] Unable to find an available port (tried 3000). Using alternative port: 3001
> Local: http://localhost:3001/
> Network: http://192.123.45.46:3001/
✔ Nitro built in 251 ms
ℹ Vite client warmed up in 747ms
デフォルトの表示が確認できれば、セットアップ完了です。
Stripeのサブスクリプション申し込みフローを、Nuxt3で組み込む
ここからは、Nuxt3アプリにサブスクリプション申し込みフォームを組み込みます。
Stripeでの組み込みは、次の3ステップで行えます。
- [Server] Stripe上に顧客・サブスクリプションデータを作成する
- [Client] 作成されたサブスクリプションデータのclient secretで、決済フォームを表示する
- [Client] フォームのsubmit処理で、サブスクリプションの申し込みを完了する
事前準備: ライブラリインストールと環境変数でのAPIキー設定
StripeのSDKを利用して組み込みますので、ライブラリをインストールしましょう。
$ yarn add stripe @stripe/stripe-js
また、StripeのAPIキーを.env.localファイルに保存しましょう。
VITE_STRIPE_PUBLIC_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
[Server] Stripe上に顧客・サブスクリプションデータを作成する
まずサブスクリプションデータを作成するAPIを用意しましょう。
server/api/subscription.post.ts
ファイルを作成し、以下のコードを追加しましょう。
import { Stripe } from 'stripe'
export default async function () {
const config = useRuntimeConfig()
const stripe = new Stripe(config.stripeSecretAPIKey, {
apiVersion: '2022-11-15'
})
const customer = await stripe.customers.create()
const product = await stripe.products.create({
name: 'Nuxt3デモ用'
})
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{
price_data: {
unit_amount: 1000,
currency: 'jpy',
recurring: {
interval: 'month',
},
product: product.id
}
}],
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent'],
payment_settings: {
save_default_payment_method: 'on_subscription',
},
})
return {
subscriptionId: subscription.id,
clientSecret: ((subscription.latest_invoice as Stripe.Invoice).payment_intent as Stripe.PaymentIntent).client_secret
}
}
Subscription作成処理の参考記事について
今回のサンプルコードは、事前にダッシュボードでデータを作成する必要のないように用意しました。
事前準備が不要な代わりにページ読み込みの度にCustomerや商品データが生成されますのでご注意ください。
引数の解説等については、以下の記事で紹介しています。
[Client] 作成されたサブスクリプションデータのclient secretで、決済フォームを表示する
APIを作成しましたので、フロントエンドで呼び出してStripe Elementsの決済フォームを表示させましょう。
<template>
<NuxtLayout>
<form>
<div id="payment-element"></div>
<button type="submit">Subscribe</button>
</form>
</NuxtLayout>
</template>
<script setup lang='ts'>
import { loadStripe } from '@stripe/stripe-js'
const runtimeConfig = useRuntimeConfig()
const { stripePublicAPIKey } = runtimeConfig.public
let elements
let paymentElement
let stripe
const { clientSecret } = await $fetch('/api/subscription', {
method: 'POST'
})
if (!process.server) {
stripe = await loadStripe(stripePublicAPIKey)
elements = stripe.elements({
clientSecret,
appearance: {
theme: 'stripe'
}
})
paymentElement = elements.create('payment')
paymentElement.mount('#payment-element')
}
</script>
作成したAPIでサブスクリプションのclient secretを作成し、フロントエンド側の処理でElementによる決済フォームの表示処理を行なっています。
ページを再読み込みして、決済フォームが表示されればOKです。
[Client] フォームのsubmit処理で、サブスクリプションの申し込みを完了する
最後にFormにSubmitイベントを設定して、サブスクリプション申し込みを完了する処理を書きましょう。
form
タグにsubmitイベントを設定します。
<template>
<NuxtLayout>
- <form>
+ <form @submit.prevent="confirmSubscription">
<div id="payment-element"></div>
<button type="submit">Subscribe</button>
</form>
</NuxtLayout>
</template>
confirmSubscription
を実装して、処理を完了させます。
async function confirmSubscription() {
const { error } = await stripe.confirmPayment({
elements,
redirect: 'if_required'
})
if (error) {
window.alert(JSON.stringify(error))
} else {
window.alert('done')
}
}
テストのクレジットカード番号を入力して、[done]のメッセージが表示されていれば、成功です。
Stripeダッシュボードでも、サブスクリプションの申し込みが完了していることが確認できます。