JavaScriptNext.jsReactSaaS / FaaSStripe

Next.js App RouterでStripe Elementを使う場合は、`loadStripe`はクライアントコンポーネントで実行する

タイトルの通り、Stripe ElementのJSファイルを使ったデモアプリ作成時のハマりをメモ。loadStripe関数の返り値がPromise<null>になり、決済フォームが表示されない問題を解決。解決策は、’use client’を設定しているコンポーネントでloadStripeを実行すること。詳細は参考リンクに記載。

広告ここから
広告ここまで

タイトルの通りです。デモアプリ作成時にハマったのでメモとして記録します。

動かなかったケース

app/payment/page.tsxのようなファイルを作成し、その中でStripe Element側のJSファイルも記述しました。

const stripePromise = loadStripe('pk_test_xxx');
const stripe = new Stripe('sk_test_xxx', {
    apiVersion: '2023-10-16'
})

export default async function InstallmentPayment() {

    const paymentIntent = await stripe.paymentIntents.create({
        amount: 3099,
        currency: 'mxn',
    })

    return (
        <main className="flex min-h-screen flex-col items-center justify-between p-24">
            <div className="mb-32 ">
                <h1 className='text-4xl text-center font-extrabold'>Installment</h1>
                <CheckoutForm
                    stripe={stripePromise}
                    paymentIntent={{
                        client_secret: paymentIntent.client_secret,
                        payment_method_options: paymentIntent.payment_method_options
                    }}
                />
            </div>
        </main>
    )
}

クライアントコンポーネントはこのような形です。

export const CheckoutForm:FC<{
    stripe: ReturnType<typeof loadStripe>;
    paymentIntent: Pick<Stripe.PaymentIntent, 'client_secret' | 'payment_method_options'>;
}> = ({
    stripe,
    paymentIntent,
}) => {
    if (!paymentIntent.client_secret) return null;
    return (
        <>
            <Elements stripe={stripe} options={{
                clientSecret: paymentIntent.client_secret
            }}>
                <PaymentElement />
            </Elements>
        </>
    )
}

この方法では、loadStripeの戻り値が常にPromise<null>になる様子で、Payment IntentのClient Secretをいくら渡しても決済フォームが表示されません。

loadStripeはクライアントコンポーネントで

解決方法はとてもシンプルで、'use client'を設定しているコンポーネントでloadStripeを実行します。

const stripePromise = loadStripe('pk_test_xxx');
export const CheckoutForm:FC<{
    paymentIntent: Pick<Stripe.PaymentIntent, 'client_secret' | 'payment_method_options'>;
}> = ({
    paymentIntent,
}) => {
    if (!paymentIntent.client_secret) return null;
    return (
        <>
            <Elements stripe={stripePromise} options={{
                clientSecret: paymentIntent.client_secret
            }}>
                <PaymentElement />
            </Elements>
        </>
    )
}

参考にした記事など

ブックマークや限定記事(予定)など

WP Kyotoサポーター募集中

WordPressやフロントエンドアプリのホスティング、Algolia・AWSなどのサービス利用料を支援する「WP Kyotoサポーター」を募集しています。
月額または年額の有料プランを契約すると、ブックマーク機能などのサポーター限定機能がご利用いただけます。

14日間のトライアルも用意しておりますので、「このサイトよく見るな」という方はぜひご検討ください。

広告ここから
広告ここまで

Related Category posts