[Stripeで始めるRevOps] Stripe Checkoutとテストクロックで、未払いや契約内容の変更などの運用業務をテストする

サブスクリプションでサービスを提供する場合、様々な運用ケースを想定した設計や動作確認が必要です。

## サブスクリプションや請求に関する「想定が必要なケース」の例

サブスクリプションやSaaSは、ユーザーが継続的に課金やシステム利用を行います。

そのため、契約・アカウント作成からしばらくしてから、次のような問い合わせや問題が発生することがあります。

- クレジットカードの有効期限切れによる未払いの発生
- 領収書や請求書の再発行
- 担当者の交代や運営会社の変更による契約の引き継ぎや内容確認
- 大口顧客向けの特別プランの提供と、提供内容の定期的な見直し・変更

これらのケースは、「契約・申し込み後に一定期間が経過してから発生することが多い」性質を持ちます。

そのため、テスト環境でシミュレートや動作確認をするには、そのためのサブスクリプションを事前に用意する必要がありました。

## 収益に関わる運用業務（RevOps）を、Stripeでシミュレートする

Stripeでは、「テストクロック」を利用することで、時間経過を伴う運用業務（Ops）も簡単にテストできます。

https://stripe.com/docs/billing/testing/test-clocks

:::note
「Revenue Operationsについて知りたい」という方は、HubSpotやAdobeが公開している記事をご覧ください。

https://blog.hubspot.jp/revenue-operations

https://business.adobe.com/jp/blog/basics/what-is-revenue-operations-revops

:::

### Step1: テストクロックで、シミュレーションを開始する

テストクロックはAPIまたはダッシュボードから作成しましょう。

![スクリーンショット 2023-04-24 14.59.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/bc7e3b6b-f709-a0e6-58e6-db74178b9d82.png)


SDKやCLIを使って作成する場合、「シミュレーション名」や「シミュレートを開始する時間」を指定できます。

次のコードでは、「１ヶ月前」からシミュレートを開始するテストクロックを作成しています。

```js
const clock = await stripe.testHelpers.testClocks.create({
  frozen_time: dayjs().subtract(1, 'months').unix(),
  name: 'From js script'
})
```

### Step2: Stripe Checkoutで、シミュレーションする顧客データとサブスクリプションを作成する

テストクロックを使って作成したシミュレーションは、Customerデータに紐づけて使用します。

そのため、Stripe Checkoutでサブスクリプションを申し込みさせている場合、**セッション作成前**にCustomerデータを作る必要があります。

```diff_javascript
+    const customer = await stripe.customers.create({
+      test_clock: "TEST_CLOCK_ID"
+    });
    const session = await stripe.checkout.sessions.create({
+      customer: customer.id,
+      customer_update: {
+        address: 'auto',
+        shipping: 'auto',
+        name: 'auto',
+      },
      mode: "subscription",
      line_items: [
        {
          price: priceId,
          quantity: 1,
        },
      ],
      // ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
      success_url: `${domainURL}/success.html?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${domainURL}/canceled.html`,
      // automatic_tax: { enabled: true }
    });
```

この実装で作成したCheckoutからサブスクリプションを申し込むと、ダッシュボード上に「テストクロックのクロックオブジェクト」であることが表示されます。

![スクリーンショット 2023-04-24 15.06.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/4175207b-dfed-9468-a979-0a6ac10a4c6b.png)

### Step3: 契約中に発生する出来事をシミュレーションする

シミュレーションの用意ができましたので、さっそく問い合わせが起きそうなイベントを起こしてみましょう。

#### Case1: クレジットカードの期限切れ

クレジットカードの有効期限が切れたことで、決済に失敗するケースをシミュレーションします。

ダッシュボードの顧客詳細ページを開きましょう。

![スクリーンショット 2023-04-24 15.06.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/4175207b-dfed-9468-a979-0a6ac10a4c6b.png)

[支払い方法]セクションに表示されているカードの[編集]ボタンをクリックします。

![スクリーンショット 2023-04-24 15.20.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/2ea7850c-02d3-b77b-c2c9-e85e1c05c1ad.png)

有効期限を変更できますので、テストクロックのクロック時刻から１ヶ月以内の年月に変更しましょう。

![スクリーンショット 2023-04-24 15.21.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/222badf3-827b-00e2-6dd2-b2d66e3dea52.png)

:::note
クレジットカードのデータを削除するだけでも再現できます。
:::

この状態で、テストクロックの時間を１ヶ月と1時間進めましょう。

![スクリーンショット 2023-04-24 15.45.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/2b2f7331-6a5b-092d-0e8b-d7ca63210eb0.png)

:::note
Stripeで作成した月額のサブスクリプションは、ちょうど１ヶ月と1時間後に請求が行われます。
これは更新分の請求書を発行してから請求するまでに、1時間の猶予期間が設けられているためです。
:::

サブスクリプションが「期日超過」に変わりました。

![スクリーンショット 2023-04-24 15.49.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/a7dd9a63-4d5c-055b-e6ac-18a87c694819.png)

テストクロックを利用した場合でも、Webhookイベントは発行されます。

![スクリーンショット 2023-04-24 15.50.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/4a47f605-9a48-b136-245d-7e13c44d1a41.png)

`customer.subscription.updated`イベントを見ると、`status`が`active`から`past_due`に変わっていることが確認できます。

```js
{
  "object": {
    "id": "sub_1N0IWVL6R1kGwUF4yYUT8bLn",
...
    "status": "past_due",
  },
  "previous_attributes": {
    "status": "active"
  }
}
```

あとは構築・運用中のシステムが、契約の更新（サブスクリプションの決済）に失敗した場合に意図した動作をしているか検証しましょう。

#### Case2: プラン変更時の差額の処理や決済タイミング

年額契約など、長期契約のサブスクリプションでは、「プラン変更時の差額請求をいつ行うか」で売上を計上する年が変わります。

「次回請求なので、売上は来年発生します」などのケースを避けたい場合、テストクロックによるシミュレーションが有効です。

Checkoutなどでテストクロックに紐づいたサブスクリプションを作成後、テストクロックで時間を進めましょう。

![スクリーンショット 2023-04-24 15.45.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/2b2f7331-6a5b-092d-0e8b-d7ca63210eb0.png)

サブスクリプションの更新日より前の日に、プラン変更処理を行いましょう。

```js
	await stripe.subscriptions.update('sub_xxx', {
		items: [{
			price: 'price_xxx',
			quantity: 1
		}]
	})
```

カスタマーポータルを使って、コードを書かずに試すこともできます。

![スクリーンショット 2023-04-24 16.12.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/ec226cd2-7756-5db7-f0d7-4081e5a5af88.png)

あとはWebhookの`customer.subscription.updated`イベントや、ダッシュボードのサブスクリプション詳細ページで、次回の請求額などが意図した通りになっているかを確認しましょう。

![スクリーンショット 2023-04-24 16.15.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/88a6b9a5-3cae-9aae-0e1d-e9736c3586c0.png)


## 本番環境で動かないように実装する点に注意！

テストクロック機能は、テスト環境でのみ利用できます。

そのため、テストクロックに関係する処理は、テスト環境でのみ動作するように設定しましょう。

次のサンプルコードでは、Node.jsの環境変数`NODE_ENV`を利用して、本番環境ではテストクロック関連の処理を停止させています。

```diff_javascript
+		const isProduction = process.env.NODE_ENV === 'production'
-		const customer = await stripe.customers.create({
+		const customer = isProduction ? null : await stripe.customers.create({
			test_clock: 'clock_xxxx'
		})
		const session = await stripe.checkout.sessions.create({
-			customer: customer.id,
+			customer: customer?.id,
-			customer_update: {
+			customer_update: isProduction ? undefined :{
				address: 'auto',
				shipping: 'auto',
				name: 'auto',
			},
			success_url: 'https://example.com/thanks',

```

## おわりに

Stripe Checkoutでのサブスクリプション申し込みフローを例に、テストクロックを用いた運用業務のシミュレーション方法を紹介しました。

この他にも、収益を増やすための取り組み（Revenue Operations）を実現・自動化するための機能を、Stripeは多数用意しています。