2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

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

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

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

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

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

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

Next.jsのAPIでの実装例

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

pages/api/webhook.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リクエストで処理が走らないようにブロックしています。

  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_intentcustomerを取得します。
もし存在しない場合は、後続の処理が行えませんので、そこでAPI処理を終了させましょう。

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

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

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

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

  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両方を動かしますので、ターミナルを2画面用意します。

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

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

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

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

StripeのAPIキーがテスト環境用であれば、Stripeテストカード番号を利用してカード情報を登録しましょう。

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

スクリーンショット 0003-12-24 17.32.24.png

おわりに

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

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

参考ドキュメント

[PR] Stripe開発者向け情報をQiitaにて配信中!

2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。

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

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

-> Stripe Organizationsをフォローして最新情報をQiitaで受け取る

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?