LoginSignup
2
1

More than 1 year has passed since last update.

Stripeで作成した「段階的な料金」または「数量ベースの料金」の詳細をAPIで取得する方法

Posted at

Stripeでは、「10ユーザーまでは固定で5,000円、それを超えた場合は1ユーザーあたり400円」のような、複雑な料金体系を実装することができます。

スクリーンショット 0004-02-16 12.46.32.png

また、「15ユーザーまでは単価50円+固定で100円、ただし16ユーザー目以降は単価45円と追加で固定90円で計算する」のような、より複雑なケースについてもサポートしています。

スクリーンショット 0004-02-16 12.45.32.png

これらの料金体系をStripeでは、「段階的な料金」そして「数量ベースの料金」とよんでいます。

スクリーンショット 0004-02-16 12.49.26.png

APIで料金データを取得する場合の注意点

PricesのList APIでは「段階的な料金」または「数量ベースの料金」を取得できない

これらの複雑な料金体系では、PriceのList APIだけでデータを取得することができません。

  const {data: priceDatas} = await stripe.prices.list({
      product: 'prod_xxx'
  })

[
  {
    "id": "price_xxxxx",
    "object": "price",
    "active": true,
    "billing_scheme": "tiered",
    "created": 1644979081,
    "currency": "jpy",
    "livemode": false,
    "lookup_key": null,
    "metadata": {},
    "nickname": "段階的な料金",
    "product": "prod_xxxx",
    "recurring": {
      "aggregate_usage": null,
      "interval": "month",
      "interval_count": 1,
      "trial_period_days": null,
      "usage_type": "licensed"
    },
    "tax_behavior": "inclusive",
    "tiers_mode": "graduated",
    "transform_quantity": null,
    "type": "recurring",
    "unit_amount": null,
    "unit_amount_decimal": null
  }
]

上記のサンプルのように、価格に関するデータが欠落した状態となります。

対応: PricesのRetrieve APIを利用して詳細を取得する

この対応として、Retrieve APIを利用する方法を紹介します。

  const {data: priceDatas} = await stripe.prices.list({
      product: 'prod_xxx'
  })
+  const prices = await Promise.all(priceDatas.map(async price => {
+      return stripe.prices.retrieve(price.id, {
+          expand: ['tiers']
+      })
+  }))

Retrieve APIのexpandtiersを指定することで、料金データの詳細を取得できます。

[
  {
    "id": "price_xxxxx",
    "object": "price",
    "active": true,
    "billing_scheme": "tiered",
    "created": 1644979081,
    "currency": "jpy",
    "livemode": false,
    "lookup_key": null,
    "metadata": {},
    "nickname": "段階的な料金",
    "product": "prod_xxxx",
    "recurring": {
      "aggregate_usage": null,
      "interval": "month",
      "interval_count": 1,
      "trial_period_days": null,
      "usage_type": "licensed"
    },
    "tax_behavior": "inclusive",
+    "tiers": [
+      {
+        "flat_amount": null,
+        "flat_amount_decimal": null,
+        "unit_amount": 200,
+        "unit_amount_decimal": "200",
+        "up_to": 10
+      },
+      {
+        "flat_amount": null,
+        "flat_amount_decimal": null,
+        "unit_amount": 190,
+        "unit_amount_decimal": "190",
+        "up_to": 50
+      },
+      {
+        "flat_amount": null,
+        "flat_amount_decimal": null,
+        "unit_amount": 185,
+        "unit_amount_decimal": "185",
+        "up_to": null
+      }
+    ],
    "tiers_mode": "graduated",
    "transform_quantity": null,
    "type": "recurring",
    "unit_amount": null,
    "unit_amount_decimal": null
  }
]

APIのパフォーマンスには要注意

ただし、上記のサンプルでは、PriceごとにRetrieve APIを呼び出すため、APIのレスポンスが遅くなる可能性があります。
下の例では、tiers_modeを持たないデータではAPI呼び出しを省略する実装を追加しています。

  const prices = await Promise.all(priceDatas.map(async price => {
+      if (!price.tiers_mode) return price
      return stripe.prices.retrieve(price.id, {
          expand: ['tiers']
      })
  }))

もし取得が必要な料金の数が多い場合や、APIパフォーマンスを重要視したい場合は、外部のDBを用意することも検討できます。
その場合、Stripe側でWebhookを利用してデータの同期を行う運用をお勧めします。

終わりに

Stripeを利用することで、ユーザー数や利用数に応じた段階的・数量ベースの価格設定を行うことができます。
ただし、今回のように、その設定内容をAPIから取得し、操作するためにはすこし工夫が必要になるケースも存在します。

今後もユースケースだけでなく、より踏み込んだ実装に関するTipsもお届けしてまいります。

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

2
1
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
1