Stripe APIバージョンを`2023-08-16`以降に変更して、Stripe Elements / Payment Intentにエラーが発生した場合の対処法

ダッシュボード上の操作やSDKまたはAPIリクエストのヘッダーなどで、StripeのAPIバージョンを変更した場合、Stripe Elementsを利用した決済フローで以下のエラーが発生することがあります。

>Uncaught (in promise) IntegrationError: stripe.confirmPayment():  the `confirmParams.return_url` argument is required unless passing `redirect: 'if_required'

アプリケーションコードの変更がないにもかかわらず、このエラーが発生した場合は、アプリケーションが利用しているStripe APIのバージョンが`2023-08-16`またはそれよりも新しいバージョンに切り替わっている可能性があります。

## Stripe APIのメジャーアップデート

Stripeは定期的に後方互換性のないAPIアップデート（メジャーアップデート）を実施します。

これは多様化する決済手段への対応や、新しい決済取引に関する規制への対応、より便利でシンプルなコードでの開発を実現するためなど、様々な理由があります。

### APIのメジャーアップデートを確認する方法

「どのような変更が、いつ・どのバージョンで行われたか」はStripeのドキュメントから確認できます。

https://stripe.com/docs/upgrades#api-%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3

「新しいパラメータの追加」など、後方互換性を維持したアップデートについては、以下のドキュメントにて紹介しています。

https://stripe.com/docs/changelog

### メジャーアップデートの適応はダッシュボードの操作かSDKおよびAPIの設定を変更して実施

**後方互換性のないAPIアップデートが自動で適応されることはありません**。

Stripeダッシュボードで「APIバージョンのアップグレード」操作を行うことで、デフォルトのAPIバージョンを変更できます。

![スクリーンショット 2023-09-01 14.10.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/165ac270-7dd6-c0b4-a4cd-cf8b5195c11e.png)

また、SDKやAPIのリクエストヘッダーを利用して、特定のバージョンのAPIを利用することも可能です。

```js
const stripe = new Stripe('sk_test_xxx', { apiVersion: '2022-11-15' })
```

```bash
curl https://api.stripe.com/v1/customers \
  -u sk_test_your_key: \
  -H "Stripe-Version: 2023-08-16"
```

https://stripe.com/docs/api/versioning

もし意図せずにAPIバージョンがアップグレードされている場合、**72時間以内**であれば、以前のバージョンにロールバックできます。

## APIバージョン`2023-08-16`での変更点

APIバージョン`2023-08-16`では、Payment Intent /　Setup Intentを中心にアップデートが行われました。

このバージョンからは、「動的な決済手段を使用する」オプションがデフォルトで有効となります。

> PaymentIntents and SetupIntents now have automatic_payment_methods enabled by default, which allows you to configure payment method settings from the Dashboard—no code required. The previous default was to accept only card payments when both payment_method_types and automatic_payment_methods were not specified. For more information, view the upgrade guide.
> https://stripe.com/docs/upgrades#2023-08-16

冒頭で紹介しているエラーは、この「動的な決済手段を使用する」オプションが、APIバージョンのアップグレードによって有効化された際に発生することがあります。

### 動的な決済手段とは

Stripeは世界で利用されている様々な決済手段をサポートしています。

しかし決済手段によっては、「対応通貨」や「最低利用可能金額」「顧客(`Customer`)データの有無」など様々な利用条件が発生します。

例えば次のコードでは、「全世界でクレジットカード決済を受け付ける」「ユーロの取引ではPayPalも利用できるようにする」「日本円では、コンビニ支払いもサポートする」の３条件を設定しています。

```js
  const orderAmount = req.body.amount;
  const orderCurrency = req.body.currency;

  const paymentMethodTypes = ["card"];
  switch(currency) {
    case "jpy":
      if (orderAmount >= 120 && orderAmount <= 300000) {
        paymentMethodTypes.push("konbini");
      }
      break;
    case "eur":
      paymentMethodTypes.push("paypal");
      break;
    default:
      break;
  }
  const paymentIntent = await stripe.paymentIntents.create({
    amount: orderAmount,
    currency: orderCurrency,
    payment_method_types: paymentMethodTypes,
  });
````

この方法では、「Stripeが新しい決済手段のサポートを開始した場合」や「越境ECなどで、新しい通貨・地域での決済の受付を開始する場合」に、コードの変更とアプリケーションのデプロイ作業が発生します。

「動的な決済手段を使用する」オプションを有効化すると、「どの決済手段を顧客に提示すべきか」の判断を、Stripeダッシュボード上の設定に任せることができます。

![スクリーンショット 2023-09-01 13.29.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/9f65227d-0f43-da83-a6ed-fa0bf8b3a56f.png)

アプリケーションのソースコードからも、決済手段に関する実装を削除できます。

```diff_javascript
  const orderAmount = req.body.amount;
  const orderCurrency = req.body.currency;

-  const paymentMethodTypes = ["card"];
-  switch(currency) {
-    case "jpy":
-      if (orderAmount >= 120 && orderAmount <= 300000) {
-        paymentMethodTypes.push("konbini");
-      }
-      break;
-    case "eur":
-      paymentMethodTypes.push("paypal");
-      break;
-    default:
-      break;
-  }
  const paymentIntent = await stripe.paymentIntents.create({
    amount: orderAmount,
    currency: orderCurrency,
-    payment_method_types: paymentMethodTypes,
  });
````

APIバージョンが`2022-11-15`以前の場合、この機能を利用するには、`automatic_payment_methods`パラメータを明示的に設定する必要がありました。

```diff_javascript
  const orderAmount = req.body.amount;
  const orderCurrency = req.body.currency;

  const paymentIntent = await stripe.paymentIntents.create({
    amount: orderAmount,
    currency: orderCurrency,
+    automatic_payment_methods: {
+      enabled: true
+    },
  });
````

APIバージョン`2023-08-16`以降は、`automatic_payment_methods[enabled]`を`false`にしていない場合、「動的な決済手段を使用する」オプションが有効となります。

## `confirmPayment`や`confirmSetup`がエラーになる場合の対応方法

APIバージョン`2023-08-16`以降では、Payment Intent / Setup Intentを作成する際に、以下のどちらかを行う必要があります。

- `confirmPayment`や`confirmSetup`の第二引数に、`confirmParams[return_url]`を設定する
- `automatic_payment_methods[allow_redirects]=never`を設定し、リダイレクトを必要とする決済を除外する

```diff_jsx
                <form onSubmit={async e => {
                    e.preventDefault()
                    if (!stripe || !elements) return
                    await stripe.confirmPayment({
                        elements,
+                        confirmParams: {
+                            return_url: 'http://localhost:3001'
+                        }                       
                    })
                }}>
                    <PaymentElement />
                    <button type='submit'>Pay</button>
                </form>
```

これにより、Stripe側が決済フォームに「リダイレクトを必要とする決済手段」を表示した場合に、適切なURLへリダイレクトさせることができます。

:::note
各言語ごとの対応方法については、以下のドキュメントをご確認ください。

デフォルトとしてダッシュボードで決済手段を管理する
https://stripe.com/docs/upgrades/manage-payment-methods
:::

## 「動的な決済手段」に切り替えることで、何が起きるか

この機能を有効化すると、金額・通貨などを元に顧客に提示する決済手段を動的に変更できます。

また、複数の決済手段を表示する際の表示順序についても、Stripeがもつ決済データを元に最適化された順番で表示できます。

![スクリーンショット 2023-09-01 13.49.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/5286219f-a645-dac4-1767-cd5966d0558b.png)
https://stripe.com/docs/payments/payment-element

金額や通貨・その他の決済設定によって、どの決済手段が表示されるかや、「なぜこの決済手段は表示されないか」を確認するツールもダッシュボードにて提供しています。

![スクリーンショット 2023-09-01 13.47.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/5a4ab44c-12d4-dd48-d636-cb065ffc7bff.png)
https://dashboard.stripe.com/test/settings/payment_methods/review

Stripe Docsで紹介している、いくつかのベータ機能を紹介します。

### 決済手段の設定を複数用意する

「サブスクリプションでは、クレジットカード決済のみにしたい」など、Stripeが判断する条件とは別の条件で表示する決済手段を設定することも可能になります。

https://stripe.com/docs/payments/multiple-payment-method-configs

この機能を利用すると、Payment IntentやCheckoutのセッション作成時に、設定IDを渡すことで個別に設定した条件で決済手段を表示できます。

```diff_javascript
await stripe.checkout.sessions.create({
  mode: 'payment',
  line_items: [
    {
      price: '{{PRICE_ID}}',
      quantity: 1,
    },
  ],
  success_url: 'https://example.com/success',
  cancel_url: 'https://example.com/cancel',
  currency: 'usd',
+  payment_method_configuration: 'pmc_234',
});
```

### 決済手段のA/Bテスト

複数の決済手段を顧客に提供した場合の購入完了率（コンバージョン率）やStripeに支払う手数料・不審請求の申立て率などをA/Bテストすることができます。

https://stripe.com/docs/payments/a-b-testing-payment-methods

## アップデートされ続ける「最適化された決済体験」をStripeで手軽に手に入れよう

Stripeでは、今回のように後方互換性を持たないバージョンのAPIを、定期的にリリースします。

しかし新しいバージョンのAPIを利用することで、より少ないコード・少ない保守作業で、Stripeがアップデートし続ける「最適化された決済体験」を顧客に提示し続けることができます。

まだStripeを触ったことのない方は、Stripeが用意するサンプル集などを利用して、ぜひStripeの決済体験をお試しください。

https://github.com/stripe-samples/accept-a-payment

## 関連記事

https://stripe.com/blog/dynamic-payment-methods

https://stripe.com/docs/upgrades/manage-payment-methods

https://stripe.com/docs/upgrades