Stripeで従量課金制サービスを作る

2020年4月18日(土)に[Stripeのコミュニティイベント](https://connect2020.jpstripes.com)が開催されますので、ぜひチェックしてみてください。

従量課金制のサービスを作る時、これまではアプリケーション側で集計や決済ロジックを書くために色々な工夫が必要でしたが、Stripe Billingを使えばサクッと作れます。

Stripe Billingの全体像については[@y_tokuさんの記事をどうぞ。](https://qiita.com/y_toku/items/235b5e7ee00792edcbbf)

## 従量課金って何？

サービスの利用量に応じて支払額が変動する課金方法です。身近な例だと電気代・ガス代・通話代などの生活インフラの支払い方法に採用されてますね。AWSやGCPなども無料枠付きの従量課金制です。

利用者にとっては**使った分だけ料金を支払えば良い**ので合理的な料金プランと言えますが、その反面ちょっとでも使ったら料金が発生したり、使いすぎると料金が青天井になるという恐怖感を持たれてしまうことも。

事業者としては無料トライアルや上限料金を組み合わせることでいかに安心感とお値打ち感を醸成するかが工夫のしどころ。奥深い課金方法ですね。

Stripe Billingを使うと、従量課金と基本料金（ミニマムチャージ）を組み合わせたり、利用量に応じて単価を段階的にスライドさせたり、無料トライアルやクーポン割引を適用するなど、あらゆる工夫をミニマムコードで実現できます。

## 従量課金をやるためのステップ

3つのポイントを押さえれば従量課金制のサービスを提供できます。

1. 従量課金型のプランを作る
2. 利用者をサブスクリプションに紐付ける
3. 利用量をStripeに送る

早速やってみます！

### 従量課金型のプランを作る

試しにこんな料金体系のサービスを作ってみます。

| | 月額料金(税抜) |
| --- | --- |
| 5名様まで無料だからじっくり使える! | 0円
| 6〜10名様の小規模利用にオススメ! | 10,000円 |
| 11名様〜の利用にはさらにお得! | 1名様追加ごとに500円 |

まずは少人数の組織に無料で試してもらい、気に入ってくれたら料金が段階的にスライドしていくというフリーミアムなサービスですね。

ついでに15日間のフリートライアルも付けて従量課金型のプランを作ってみます。

```ruby
require 'stripe'

Stripe.api_key = ENV['STRIPE_API_SECRET']

Stripe::Plan.create(
  # フリーミアムという名のプロダクト
  id: 'freemium',
  product: {
    name: 'フリーミアム'
  },
  # 日本円で毎月請求
  currency: 'jpy',
  interval: 'month',
  # 期間中に発生した利用量の最新値を元にした従量課金制
  usage_type: 'metered',
  aggregate_usage: 'last_during_period',
  # スライド料金
  billing_scheme: 'tiered',
  tiers_mode: 'graduated',
  # 15日間の無料トライアル
  trial_period_days: 15,
  # トライアル期間が終わったら以下の料金を適用
  tiers: [
    {
      # 最初の5名まで無料
      up_to: 5,
      flat_amount: 0
    },
    {
      # 10名まで10,000円ポッキリ
      up_to: 10,
      flat_amount: 10000
    },
    {
      # 11名以降はプラス500円
      up_to: :inf,
      unit_amount: 500
    }
  ]
)
```

### 利用者をサブスクリプションに紐付ける

作成したプランに利用者(Customerオブジェクト)を紐付けてみましょう。紐付けにはサブスクリプションという仕組みを使います。

Customerオブジェクトの作成方法は割愛しますが、接頭辞`cus_`を持つIDがStripeから発行されます。

```ruby
require 'stripe'

Stripe.api_key = ENV['STRIPE_API_SECRET']

Stripe::Subscription.create(
  customer: 'cus_1234567890',
  items: [
    {
      # cus_1234567890に従量課金型プランを適用
      plan: 'freemium'
    }
  ]
)
```

このAPIコールが成功すると利用者がサブスクリプションを購読している状態となり、フリートライアルも有効になります。

### 利用量をStripeに送る

この利用者が5人のメンバーを追加したとします。

これをStripeに伝えるには、Usage API経由でサブスクリプションアイテム（請求書でいう費目のようなもの）に対して利用量をセットします。

```ruby
require 'stripe'

Stripe.api_key = ENV['STRIPE_API_SECRET']

# SubscriptionからSubscription Itemの一覧を辿る
items = Stripe::SubscriptionItem.list(
  subscription: 'sub_1234567890'
)

# Usage APIでSubscription Item (si_xxxxxxxx)に対して利用量を送る
Stripe::UsageRecord.create(
  timestamp: Time.now.to_i,
  subscription_item: items.data.first.id,
  quantity: 5
)
```

これだけで利用者を従量課金制の定期購読に紐付けることができました。ちなみにプラン作成やCustomerオブジェクトを作るにはStripeダッシュボードから手動で操作できますが、利用量を送る時にはUsage APIがマストです([設計思想はこちら](https://qiita.com/y_toku/items/235b5e7ee00792edcbbf#%E5%88%A9%E7%94%A8%E9%87%8F%E3%81%AE%E6%9B%B4%E6%96%B0%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6))。

Stripeダッシュボードでシミュレーションできます。

![Artboard.png](https://qiita-image-store.s3.amazonaws.com/0/2592/c6d681a3-8e2d-76df-8b97-b60744419fad.png)

意図した通りになってますね。
ちなみに消費税が見当たりませんが、ちゃんとセットできますのでご安心を。

## Tips

実際に開発・運用するにあたってのTipsも。

### ベストな集計方法を選ぶ

従量課金の集計方法は4種類あるので、ビジネスモデルに合ったものを選べます。
プランを作成する時の`aggregate_usage`に以下のいずれかをセットします。

- `sum` 決済期間内に記録された利用量を合算する（デフォルト）
- `last_during_period` 決済期間内に記録された利用量のうち、最新の値を使う。
- `last_ever` 全期間に記録された利用量のうち、最新の値を使う。
- `max` 決済期間内に記録された利用量のうち、最大の値を使う。もし期間内に記録が無ければゼロとする。

チャートにするとこんな感じでしょうか。

![Artboard Copy.png](https://qiita-image-store.s3.amazonaws.com/0/2592/cb140c25-41cb-2095-0c26-78b2b8c614c5.png)

シンプルに見えて色んなビジネスモデルをカバーできますね。これに加えて段階的に料金が変わるスライド制や、無料トライアル・クーポン割引を組み合わせて魅力的な料金体系を作りましょう。


### アプリケーション側で集計しなくてOK

Stripeの従量課金型プランの大きな恩恵だと思うのが、利用量の集計ロジックと決済ロジックをStripeに任せられること。アプリケーションの実装が楽になります。

特に集計ロジックを書かなくて良いのが素晴らしい！

というのも、MySQLなどのリレーショナルデータベースならクエリ数発で必要なレコードを取得できますが、DynamoDBなどの[NoSQLデータベースはその特性から集計機能が弱め](https://employment.en-japan.com/engineerhub/entry/2017/11/22/110000#NoSQL%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%A8%E8%A7%A3%E6%B1%BA%E7%AD%96)。特定期間の利用量を抽出するだけのためにキー設計やクエリを工夫したり、それでもダメならAthenaやElasticSearchをセットアップしたり。Stripeの仕組みを使えばこれらの実装を一掃できます。

サーバレスアーキテクチャを採用しているWebサービスとの相性が抜群に良いです。

### Usage APIは流量制限しながら非同期で叩く

Usage APIには同時実行制限があるので並列で叩きすぎると制限に引っかかります。同時実行数はドキュメントに明記されていないため流量制限(Throttling)しておいた方が安全です。

例えば1分100円のリモート英会話アプリなどでレッスン終了後に同期的にAPIコールすると、サービスの成長に伴って同時実行制限を突き抜けてしまうため、SQSのようなタスクキュー＆非同期ワーカー構成にするか、バッチで逐次処理するならインターバルを設けるのが無難かと思います。

#### 逐次処理でインターバルを置く例

```
targets.each do |target|
  (Usage APIを叩くコード)

  sleep 1 # 1秒間スリープ
end
```

### ワーストケースを回避する

Stripeは利用量を元に課金額を決定するため、Usage APIを担当するコードにバグがあるとおかしなことになります。

例えばGitHubのようにチーム内のメンバー数に応じて料金がスライドするサービスの場合、アプリケーションとStripeが保持しているメンバー数（利用量）が合致していないと以下のケースが発生します。

- 過少課金: 本来の請求額よりも少なく決済してしまう
- 過多課金: 本来の請求額よりも多く決済してしまう

**過少課金は事業者の丸損なのでまあ良いとして、過多課金は即クレームになるので宜しくありません。**もし大型連休などで対応が後手に回り、決済が実行されてしまうとワーストケースに突入します。

手っ取り早い防止策として、ウチでは全利用者の利用量を毎日送信してUsage API絡みのエラーを日々把握できるようにしています。もしエラーが発生してもデバッグに十分な時間が確保できるので精神衛生上もオススメです。

もし利用者が沢山いて逐次バッチ処理が突き抜けそうな場合は、対象レコードを絞り込んでサンプリングしたり、Stripe Webhookをトリガーにする手もあります。

## まとめ・PR

- 従量課金制のサービスをやるならStripeオススメ
- [Advent Calendar](https://adventar.org/calendars/3096)を見ればStripeの最新動向が分かる
- [在庫管理サービスTana](https://get.tana.app/ja/)は当記事内で紹介したことをサーバレスで実現しています

最後までありがとうございました！良いお年をお過ごしください。