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>
            </>
        )
    }

    参考にした記事など

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark