サブスクリプション申し込み時に表示する初回請求の見積額を、StripeのRetrieveUpcoming Invoice APIで計算する

Stripeでは、「定額料金 + 数量や利用量ベースの段階的料金」のような複雑な料金体系のサブスクリプションも簡単に作成することができます。

![スクリーンショット 2023-12-13 14.27.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/3a73c441-ad0b-15bb-d327-c85e9756b3e1.png)

このような料金体系を利用することで、「利用した分だけ支払う」サービスを顧客に提供することができます。また、大口契約に対する割引なども、個別対応を行うことなく実現できます。一方で複雑な料金体系を作成する場合、顧客が「この数量・プランだといくら支払うことになるのか？」を調べやすくする必要もあります。

## RetrieveUpcoming Invoice APIで、契約前のサブスクリプションの請求額を見積もりする

Stripeでは、「次回の請求内容をプレビューする」ことができる「[Retrieve upcoming Invoice API](https://stripe.com/docs/api/invoices/upcoming)」を用意しています。

```bash
curl -G https://api.stripe.com/v1/invoices/upcoming \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d customer=cus_NeZwdNtLEOXuvB
```

このAPIに`subscription_items`パラメータを渡すことで、まだ作成・契約が完了していないサブスクリプションに対する金額のプレビューが行えます。

```bash
curl -X GET "https://api.stripe.com/v1/invoices/upcoming" \
  -u 'REPLACE_WITH_YOUR_SECRET_KEY':  \
  -d "subscription_items[0][price]"="price_1NzvVRL8xlxrZ26gHdYI8j1Y" \
  -d "subscription_items[0][quantity]"=1
```

請求金額や請求明細などがAPIレスポンスとして取得できます。

```js
{
  "object": "invoice",
  "account_country": "JP",
  "account_tax_ids": null,
  "amount_due": 9800,
  "amount_paid": 0,
  "amount_remaining": 9800,
  "amount_shipping": 0,
  "application": null,
  "application_fee_amount": null,
  "attempt_count": 0,
  "attempted": false,
  "automatic_tax": {
    "enabled": false,
    "status": null
  },
  "billing_reason": "subscription_create",
  "charge": null,
  "collection_method": "charge_automatically",
  "lines": {
    "object": "list",
    "data": [
      {
        "id": "il_tmp_1463b4L8xlxrZ26g09a794ec",
        "object": "line_item",
        "amount": 9800,
        "amount_excluding_tax": 9800,
        "currency": "jpy",
        "description": "1 × スタンダードプラン (at ¥9,800 / month)",
        "discount_amounts": [],
        "discountable": true,
        "discounts": [],
        "livemode": false,
        "metadata": {},
        "period": {
          "end": 1705124379,
          "start": 1702445979
        },
...
```

## JavaScript / TypeScriptで請求内容のプレビューを作る

取得したAPIレスポンスを元に、「初回の請求が何円で、どんな請求項目があるか？」を計算するコードを書いてみましょう。

請求の明細については、`lines.data`の中にある配列から取得できます。

```typescript
        const stripe = new Stripe(c.env.STRIPE_SECRET_KEY, {
            apiVersion: '2023-10-16'
        });
        
        const estimateTheFirstInvoiceFromSubscription = await stripe.invoices.retrieveUpcoming({
            subscription_items: [{
                price: 'price_1NzvVRL8xlxrZ26gHdYI8j1Y',
                quantity: 1
            }],
        });
        estimateTheFirstInvoiceFromSubscription.lines.data.forEach((line, i) => {
            console.log(`#${i} - ${line.description}: ${line.amount.toLocaleString()}`);
        });
        console.log(`#Amount - ${estimateTheFirstInvoiceFromSubscription.subtotal.toLocaleString()}`);
```

このスクリプトを実行すると、次のように設定した`subscription_items`の内容に応じた金額や明細が表示されます。

```bash
#0 - 1 × スタンダードプラン (at ¥9,800 / month): 9,800
#Amount - 9,800
```

### 複雑な料金体系で計算してみる

続いて冒頭に紹介した複雑な料金体系についても試してみましょう。今回は次の画像に紹介するような、「定額の手数料 + 数量に応じた追加料金（１１ユーザー以降は値引き）」で設定しました。

![スクリーンショット 2023-12-13 11.06.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/63aff89b-7dde-0cf7-b7cb-deb22e029f2d.png)

まずは`quantity = 1`で実行してみましょう。料金表の通り5,000円が出力されました。

```bash
#0 - 1 × スタンダードプラン (Tier 1 at ¥0 / month): 0
#1 - スタンダードプラン (Tier 1 at ¥5,000 / month): 5,000
#Amount - 5,000
```

複雑にするため、`quantity = 90`にしてみましょう。定額手数料や単価が値引きされていることや、設定した数量で計算がされていることがわかります。

```bash
#0 - 50 × スタンダードプラン (Tier 3 at ¥90 / month): 4,500
#1 - スタンダードプラン (Tier 3 at ¥4,980 / month): 4,980
#Amount - 9,480
```

## 請求サイクルを指定場合は、`subscription_billing_cycle_anchor`を利用する

「月初を請求日とする」など、請求サイクルを固定化したいケースについて考えてみましょう。こちらはサブスクリプションを作成する際に利用する`billing_cycle_anchor`と同じように、UNIXタイムスタンプを`subscription_billing_cycle_anchor`に設定します。

```typescript
        const stripe = new Stripe(c.env.STRIPE_SECRET_KEY, {
            apiVersion: '2023-10-16'
        });
        const now: Date = new Date();
        const nextMonth: Date = new Date(now.getFullYear(), now.getMonth() + 1, 1);
        const unixTimestamp: number = Math.floor(nextMonth.getTime() / 1000);

        const estimateTheFirstInvoiceFromSubscription = await stripe.invoices.retrieveUpcoming({
            customer: 'cus_P5r7uozm9GP09E',
            subscription_items: [{
                price: 'price_1NzvVRL8xlxrZ26gHdYI8j1Y',
                quantity: 1
            }],
            subscription_billing_cycle_anchor: unixTimestamp
        });
        estimateTheFirstInvoiceFromSubscription.lines.data.forEach((line, i) => {
            console.log(`#${i} - ${line.description}: ${line.amount.toLocaleString()}`);
        });
```

月の中頃（12/13)にスクリプトを動かしたため、9,800円のサブスクリプション料金が比例配分されて出力されました。

```bash
#0 - Time on スタンダードプラン after 13 Dec 2023: 5,860
#Amount - 5,860
```

## プラン変更時の見積もりだけでなく、初回申し込み時にも`RetrieveUpcoming Invoice API`を活用しよう

Stripe Billingでサブスクリプションを提供する場合、比例配分への対応のため請求内容のプレビューが欠かせません。

https://stripe.com/docs/billing/subscriptions/prorations#preview-proration

`RetrieveUpcoming Invoice API`は比例配分や複雑な料金体系を、「初回申し込み時」にも「プランの変更時」にもプレビューすることができます。

よりユーザーが安心して利用できるサービス提供やユーザー体験を提供するためにも、ぜひこのAPIをご活用ください。