Stripe Elements (PaymentElement / Checkout)でリダイレクトをオフにする方法

この記事では、Stripeを利用した決済フォームを実装する際に、リダイレクトをオフにする方法をElements / Checkoutの2つで紹介します。kintoneなどのビジネスアプリケーションに決済機能を埋め込む時のような、現在いるページからの離脱をできるだけ回避したいケースでの決済機能実装を検討されている方はぜひお試しください。

## Stripeの決済フローにはリダイレクトが含まれる

Stripeで決済フォームを実装する場合、Checkout / Elements両方でリダイレクトが発生します。これは決済プロバイダによる認証ページへの遷移等を処理するための挙動です。多くの決済手段をサポートし、Stripeが新しく決済手段を追加した場合の対応コストを低く抑えるためにも、できるだけリダイレクトが発生する前提での決済フロー実装を推奨しています。

:::note
リダイレクトの設定や、リダイレクト後の動作をカスタマイズする方法については、[Stripe Docsにドキュメント](https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=embedded-form)が用意されていますので、こちらをご確認ください。

https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=embedded-form
:::

しかし一方で別のサービス・プラットフォームやフレームワーク上に決済フローを導入する場合、ユーザーが現在いるページの中で決済フローを完結させなければならないケースも存在します。例えばモーダル上での操作や、URL / Cookieなどで状態が管理されていない画面にいる場合は、一度離脱するとユーザーがその画面に戻って来ることができません。そのため、決済手段の種類が制限される可能性はありますが、リダイレクトさせない決済フローの実装方法を知る必要があります。


## Stripe Checkoutを利用した埋め込み決済フォームでリダイレクトを停止する

Stripe Checkoutでは、決済フォームをWebページ上に埋め込むことができます。

![スクリーンショット 2024-12-12 16.29.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/6dc1e7ea-2da3-b794-abda-6755cd2677b4.png)

この場合決済フォームの操作が完了すると`return_url`で指定したURLへのリダイレクトが実行されます。

```js
  const checkoutSession = await stripe.checkout.sessions.create({
    ui_mode: 'embedded',
    line_items: [
      {
        // Provide the exact Price ID (for example, pr_1234) of the product you want to sell
        price: '{{PRICE_ID}}',
        quantity: 1,
      },
    ],
    mode: 'payment',
    return_url: `${YOUR_DOMAIN}/return.html?session_id={CHECKOUT_SESSION_ID}`,
  });
```

リダイレクトをオフにする場合、`return_url`の代わりに`redirect_on_completion='never'`を設定します。これによってリダイレクトが必要な決済手段はフォーム上に表示されなくなりますが、決済処理フローの中でリダイレクトが発生することはなくなります。ただし`payment_method_types`を利用して顧客に表示する決済手段を制御している場合は、リダイレクト必須な決済手段を指定するとエラーになりますのでご注意ください。

```diff_javascript
  const checkoutSession = await stripe.checkout.sessions.create({
    ui_mode: 'embedded',
    line_items: [{
        price: '{{PRICE_ID}}',
      quantity: 1
    }],
    mode: 'payment',
+    redirect_on_completion: 'never',
-    return_url: `${YOUR_DOMAIN}/return.html?session_id={CHECKOUT_SESSION_ID}`,
  })
```

### 決済フォームの操作完了後のアクションを設定する

リダイレクトをオフにした場合、決済フォームの操作が完了した場合の処理をフロントエンドのJavaScriptで実装する必要があります。決済フォームの操作が完了した時に実行される処理は、`initEmbeddedCheckout`関数の引数に`onComplete`として設定します。

以下のサンプルコードでは、決済フォームの操作が完了すると、フォーム自体を非表示にしています。`initEmbeddedCheckout`の戻り値で取得できる`elements`を利用して操作を行いますので、`let`を使って`onComplete`内でも値を利用できるようにしましょう。

```html
      <h1>Stripe Checkout Demo</h1>
      <form id="payment-form">
        <div id="payment-element"></div>
      </form>
    
      <script>
        let elements
        const stripe = Stripe('${STRIPE_PUB_API_KEY}')
  
        // Stripe Elementsを初期化
        stripe.initEmbeddedCheckout({
          fetchClientSecret: async () => "${checkoutSession.client_secret}",
          onComplete: async () => {
            if (!elements) return
            elements.destroy()
          }
        }).then(elm => {
          elements = elm
          elements.mount('#payment-element')
        })
      </script>
```

## Stripe Elementsを利用した埋め込み決済フォームでリダイレクトを停止する

もう一つの決済フォーム実装方法が、[Stripe Elements](https://docs.stripe.com/payments/payment-element)です。Checkoutよりも利用できる機能は限定されますが、[Appearance API](https://docs.stripe.com/payments/payment-element#appearance)を利用してフォームのデザインをカスタマイズできるなどのメリットがあります。

Elementsを利用した決済フォームでも、リダイレクトが発生します。フォームを`submit`した際に、ユーザーは`confirmPayment`関数に設定した`confirmParams[return_url]`のURLへリダイレクトされます。

```js
  const { error } = await stripe.confirmPayment({
    elements,
    confirmParams: {
      // Make sure to change this to your payment completion page
      return_url: "http://localhost:4242/complete.html",
    },
  });
```

このリダイレクトを使わないようにするには、`confirmParams[return_url]`の代わりに`redirect=if_required`を設定しましょう。これによってリダイレクトが行われない形で決済処理が実行されます。こちらもリダイレクトが必要な決済手段での処理が実行できなくなりますので、切り替える際はサポート予定の決済手段が動作するかを必ず確認しましょう。

```diff_javascript
  const { error } = await stripe.confirmPayment({
    elements,
+    redirect: 'if_required'
-    confirmParams: {
-      return_url: "http://localhost:4242/complete.html",
-    },
  });
```

決済処理が完了した後の実装については、`confirmPayments`のレスポンスを利用します。レスポンスに含まれる`paymentIntent`オブジェクトと`error`オブジェクトの2つを利用して、決済処理の成否やその後のユーザーに向けた表示の制御などを実装しましょう。

```javascript
  const { error, paymentIntent } = await stripe.confirmPayment({
    elements,
    redirect: 'if_required'
  });
  if (error) {
    const errorMessage = document.getElementById('error-message');
    errorMessage.textContent = result.error.code + ':' + result.error.message;
  } else if (paymentIntent) {
    // 成功時の処理
  }
```

## まとめ

Stripeを利用してさまざまなサービスやプラットフォームに決済機能を追加することができます。しかし構築するサービス・プラットフォームの制約などによって、リダイレクトを行わない形で実装する必要があるケースも存在します。このようなケースでは、Stripe.jsのパラメータを調整することで、リダイレクトの制御を行いましょう。

ただしクレジットカード以外の決済手段をサポートしている場合、決済手段によっては、決済処理のためにリダイレクトが必須であることもあります。このような決済手段はフォームに表示されないか、決済処理がエラーになることがありますので、実装方法を変更する場合には、決済手段ごとの動作について事前にテストを行いましょう。

Stripeを使うことでさまざまな要件やビジネスケースにあわせた実装を、効率的に行えます。ぜひ[Stripe Docs](https://docs.stripe.com/payments/use-cases/get-started)や[QiitaのStripeページ](https://qiita.com/organizations/stripe)を参考に、実装方法についてお調べください。

## ドキュメント

https://docs.stripe.com/payments/checkout/custom-success-page?payment-ui=embedded-form

https://docs.stripe.com/api/checkout/sessions/object#checkout_session_object-redirect_on_completion

https://docs.stripe.com/js/payment_intents/confirm_payment#confirm_payment_intent-options-redirect