Stripe Webhookで、追加されたカードを自動的に「デフォルトの支払い方法」へ設定する処理を書く


Stripe Checkoutを利用してカード情報登録フォームを作ることができます。

https://qiita.com/hideokamoto/items/0ea4fe01cac8c8ad8669

# Checkoutの`mode='setup'`は「デフォルトの支払い方法」を設定しない

`mode='setup'`で登録した場合、その決済方法は「デフォルトの支払い方法」に設定されません。

そのため、Checkout以外のルートで決済を行う必要がでた場合に、支払い方法を個別に指定する必要がでます。

# Webhookで、登録した支払い方法を自動的にデフォルトの支払い方法に設定する

対策の１つとして、Webhookで追加された支払い方法をデフォルトに自動設定する方法があります。

## Next.jsのAPIでの実装例

実装例としてNext.jsのAPIを紹介します。
コード詳解部分を参考にすることで、Expressなどへも応用することができます。

**pages/api/webhook.js**

```js
import Stripe from 'stripe'
import {buffer} from 'micro'

const endpointSecret = 'whsec_xxxx'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2020-08-27'
})


export const config = {
    api: {
        bodyParser: false
    }
}
export default async function handler(
  request,
  response
) {
  const sig = request.headers['stripe-signature'];
  let event;
  const buf = await buffer(request)

  try {
    if (!sig) throw new Error("No signature provided")
    event = stripe.webhooks.constructEvent(buf, sig, endpointSecret);
  } catch (e) {
    const err = e instanceof Error ? e : new Error("Bad Request")
    console.log(err)
    response.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }

  if ('checkout.session.completed' !== event.type) {
      response.status(204).end()
      return
  }
  const data = event.data.object
  const customerId = data.customer
  const setupIntentId = data.setup_intent
  if (!customerId || ! setupIntentId) {
    return response.status(204).end()
  }

  const setupIntent = await stripe.setupIntents.retrieve(setupIntentId)
  if (!setupIntent.payment_method) {
    return response.status(204).end()
  }

  const paymentMethodId = typeof setupIntent.payment_method === 'string' ? setupIntent.payment_method : setupIntent.payment_method.id
  if (paymentMethodId) {
    await stripe.customers.update(customerId, {
        invoice_settings: {
          default_payment_method: paymentMethodId
        }
      })
    }
  }

  return response.status(200).end()
}
```

まず、関係のないイベントやStripe以外からのAPIリクエストで処理が走らないようにブロックしています。

```js
  const sig = request.headers['stripe-signature'];
  let event;
  const buf = await buffer(request)

  try {
    if (!sig) throw new Error("No signature provided")
    event = stripe.webhooks.constructEvent(buf, sig, endpointSecret);
  } catch (e) {
    const err = e instanceof Error ? e : new Error("Bad Request")
    console.log(err)
    response.status(400).send(`Webhook Error: ${err.message}`);
    return;
  }
  if ('checkout.session.completed' !== event.type) {
      response.status(204).end()
      return
  }
```

次に、Webhookイベントで受け取ったデータの内、`setup_intent`と`customer`を取得します。
もし存在しない場合は、後続の処理が行えませんので、そこでAPI処理を終了させましょう。

```js
  const data = event.data.object
  const customerId = data.customer
  const setupIntentId = data.setup_intent
  if (!customerId || ! setupIntentId) {
    return response.status(204).end()
  }
```


`setupIntent.retrieve`APIを呼び出して、Checkoutでユーザーが登録した決済方法のIDを取得しましょう。

```js
  const setupIntent = await stripe.setupIntents.retrieve(setupIntentId)
  if (!setupIntent.payment_method) {
    return response.status(204).end()
  }

```

最後に、取得した決済方法のIDをデフォルトの支払い方法へ設定しています。

```js
  const paymentMethodId = typeof setupIntent.payment_method === 'string' ? setupIntent.payment_method : setupIntent.payment_method.id
  if (paymentMethodId) {
    await stripe.customers.update(customerId, {
        invoice_settings: {
          default_payment_method: paymentMethodId
        }
      })
    }
  }
```


# 動作を確認する

最後に動作を確認しましょう。

Next.jsとStripe CLI両方を動かしますので、ターミナルを２画面用意します。

```bash
# １つめのターミナルで実行
% npm run dev -p 3000

# 2つめのターミナルで実行
% stripe listen --forward-to localhost:3000/api/webhook
```

続いてStripe CLIやcUrlなどでCheckoutのセッションを作成します。

```bash
curl https://api.stripe.com/v1/checkout/sessions \
  -u sk_test_xxxxxx: \
  -d success_url="https://example.com/success" \
  -d cancel_url="https://example.com/cancel" \
  -d "payment_method_types[]=card" \
  -d mode=setup

stripe checkout sessions create \
  --success-url="https://example.com/success" \
  --cancel-url="https://example.com/cancel" \
  -d "payment_method_types[]=card" \
  --mode=setup
```

レスポンスの`url`にある`https://checkout.stripe.com/pay/cs_xxxx~`のURLにアクセスしましょう。


![スクリーンショット 0003-12-24 17.27.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/f564dda5-9dbb-e2dc-9882-1955c82bfea8.png)

StripeのAPIキーがテスト環境用であれば、[Stripeテストカード番号](https://stripe.com/docs/testing#cards)を利用してカード情報を登録しましょう。

「カードを保存」ボタンをクリックした後、Stripe Dashboardに顧客データとデフォルトの支払い情報が登録されていれば、Webhookの設定も成功です。

![スクリーンショット 0003-12-24 17.32.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/300a2fb2-5174-7136-83f9-79487980376f.png)

# おわりに

実際に組み込みする場合は、「デフォルトの支払い方法が未登録の場合のみ、登録する」判定が必要になるかもしれません。

また、別途デフォルトとの支払い方法を変更できるAPIやUIを実装することで、顧客が複数のカードを利用して注文できるUXをより快適にすることができます。

# 参考ドキュメント

https://stripe.com/docs/api/checkout/sessions/create?lang=cli

https://stripe.com/docs/api/customers/update?lang=cli

# [PR] Stripe開発者向け情報をQiitaにて配信中！
2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。

- [Stripe Updates]：開発者向けStripeアップデート紹介・解説
- ユースケース別のStripe製品や実装サンプルの紹介
- Stripeと外部サービス・OSSとの連携方法やTipsの紹介
- 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について随時更新してまいります。

-> [Stripe Organizationsをフォローして最新情報をQiitaで受け取る](https://qiita.com/organizations/stripe)