4
0

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とStripe Elementsで、Webhookを利用せずにサブスクリプションの支払い方法を設定する方法

Posted at

Stripeで、Elementsを利用して契約時にカード情報を入力させる形でサブスクリプションを開始した場合、「次回以降の支払いに利用する決済方法」をWebhookで指定する必要がありました。

Webhookで支払い方法を設定するサンプルコード
引用元: https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements#default-payment-method

if (dataObject['billing_reason'] == 'subscription_create') {
  const subscription_id = dataObject['subscription']
  const payment_intent_id = dataObject['payment_intent']

  // Retrieve the payment intent used to pay the subscription
  const payment_intent = await stripe.paymentIntents.retrieve(payment_intent_id);

  const subscription = await stripe.subscriptions.update(
    subscription_id,
    {
      default_payment_method: payment_intent.payment_method,
    },
  );
};

2022/06のAPIアップデートで、「Webhookを利用せずにデフォルトの支払い方法を設定する方法」が追加されましたので、紹介します。

save_default_payment_methodsubscriptions.createで設定する

Elementsを利用して決済情報を入力させるフローでは、以下のように「決済未完了のサブスクリプション」を事前に作成します。

  const subscription = await stripe.subscriptions.create({
    items: [{
      price: 'price_xxx',
      quantity: 1
    }],
    customer:'cus_xxx',
    payment_behavior: 'default_incomplete',
    expand: ['latest_invoice.payment_intent'],
  })
  res.status(200).json({
    subscriptionId: subscription.id,
    clientSecret: (subscription.latest_invoice as any).payment_intent.client_secret,
  })

ここに新しく、save_default_payment_methodを追加しましょう。

  const subscription = await stripe.subscriptions.create({
    items: [{
      price: 'price_xxx',
      quantity: 1
    }],
    customer:'cus_xxx',
+    payment_settings: {
+      save_default_payment_method: 'on_subscription',
+    },
    payment_behavior: 'default_incomplete',
    expand: ['latest_invoice.payment_intent'],
  })
  res.status(200).json({
    subscriptionId: subscription.id,
    clientSecret: (subscription.latest_invoice as any).payment_intent.client_secret,
  })

フロントエンドの実装は変更なし

フロントエンド側での追加実装は不要です。

これまでと同じく、APIで作成したサブスクリプションのclient_secretを受け取りとElement.jsのセットアップを行います。

import { Elements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
import { useState } from 'react'

const Home = () => {
  const [paymentIntentClientSecret, setPaymentIntentClientSecret] = useState('')
  return (
      <main>
        <form onSubmit={async e => {
          e.preventDefault()
          const response = await fetch('/api/subscription')
          const result = await response.json()
          setPaymentIntentClientSecret(result.clientSecret)
        }}>
          <button type='submit'>購読する</button>
        </form>
        <section>
          {paymentIntentClientSecret ? (
          <Elements
            options={{
              loader: 'always',
              appearance: {
                theme: 'stripe'
              },
              clientSecret: paymentIntentClientSecret,
            }}
            stripe={loadStripe('pk_test_xxx')}
          >
            <SubscriptionForm />
          </Elements>

          ): null}
        </section>
      </main>
  )
}

あとはuseStripeなどを利用して、決済処理を行うだけです。

import { PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js'

const SubscriptionForm = () => {
  const stripe = useStripe()
  const elements = useElements()
  return (
    <form onSubmit={async e => {
      e.preventDefault()
      if (!elements || !stripe) return;
      const { error } = await stripe.confirmPayment({
        elements,
        confirmParams: {
          return_url: 'http://localhost:3000'
        }
      })
      console.log(error)
    }}>
      <PaymentElement />
      <button type='submit'>定期購読を開始する</button>
    </form>
  )
}

Webhookを利用する必要があるケース

今回のAPIアップデートに対応することで、Webhookでデフォルトの決済方法を指定する必要はなくなります。

ですが、今後も「初回の決済に成功した場合に、システム側で処理を行う」要件が発生した場合には、Webhookを利用する必要があります。

  • 銀行振込など、その場で決済成功を確認できない決済手段をサポートするケース
  • 支払いの完了をもってサービスの提供を開始させたいケース
  • CRM / MAなど、外部ツールやDBとデータを連携させたいケース
  • etc..

これらのケースでは、以下のWebhookイベントと条件を利用したAPIの実装が引き続き必要です。

if (event.type !== 'customer.subscription.updated') return
const dataObject = event.data.object
if (dataObject['status'] == 'active') {
  // 各種処理を記述
}

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

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

など、Stripeを利用してオンラインビジネスを始める方法について週に2〜3本ペースで更新中です。

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

4
0
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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?