この記事は Elixir Advent Calendar 2024 シリーズ4の23日目です。昨日は @piacerex さんでした!
執筆時点ではまだベータテストという建付けですが、imagepix というサービスを開発・運営しています。
imagepixの管理コンソールElixirのWebアプリケーションフレームワーク Phoenix で開発していて、従量課金を実現するために言わずと知れた Stripe を使っています。
公式ドキュメントが充実しているので困ることはないのですが、従量課金の解説記事はあまり見かけません。そこで、今回はElixirでStripeのAPIを使って従量課金を実現する流れを解説してみます
Stripeダッシュボードでの事前準備
アカウントを持っていない場合は、以下を参考に作っておきましょう。
私はもちろんアカウントを持っていますが、テスト環境を汚したくないこともあり、今回たまたま発見した サンドボックス を使うことにします。もちろんテスト環境を使っても構いません
メーターの作成
https://dashboard.stripe.com/test/meters
以下のようにメーターを作成します。メーター名は後からでも変えられますが、イベント名やその他の項目は一度決めると変えられないので要注意です。
商品の作成
https://dashboard.stripe.com/test/products
以下のように商品を作成します。今回は1ユニットあたり100円という商品にしてみます。
「その他の料金体系オプション」をクリックすると従量課金の料金体系を設定できます。
- 料金モデル
- 金額
- メーター
をそれぞれ以下のように入力しましょう。
LivebookでのElixir実行環境の準備
本題ではないので、Dockerでサクッと起動しましょう。
docker run --env LIVEBOOK_TOKEN_ENABLED=false -p 8080:8080 -p 8081:8081 --pull always ghcr.io/livebook-dev/livebook
本記事の内容をとりあえず動かしたい方は、以下の方法を試してみてください
Live markdown
以下の内容を http://localhost:8080/open/source の「Notebook source」にコピペしてインポートするだけで動かせます。
# Stripeで従量課金を実現する
```elixir
Mix.install([
{:stripity_stripe, "~> 3.2"},
{:kino, "~> 0.14.2"}
])
```
## Stripe Checkout Sessionでのサブスクリプションの作成
```elixir
secret_key_input = Kino.Input.password("SECRET_KEY")
```
```elixir
price_input = Kino.Input.password("PRICE")
```
```elixir
secret_key = Kino.Input.read(secret_key_input)
price = Kino.Input.read(price_input)
{:ok, session} =
Stripe.Checkout.Session.create(
%{
line_items: [
%{price: price}
],
mode: :subscription,
success_url: "https://dashboard.stripe.com/test/subscriptions"
},
api_key: secret_key
)
IO.puts(session.url)
```
## Stripe Meter Eventでの使用量の記録
```elixir
{:ok, %{data: [subscription | _]}} = Stripe.Subscription.list(%{}, api_key: secret_key)
customer = subscription.customer
```
```elixir
path = Stripe.OpenApi.Path.replace_path_params("/v1/billing/meter_events", [], [])
Stripe.Request.new_request(api_key: secret_key)
|> Stripe.Request.put_endpoint(path)
|> Stripe.Request.put_params(%{
event_name: "sample",
payload: %{
value: 10,
stripe_customer_id: customer
}
})
|> Stripe.Request.put_method(:post)
|> Stripe.Request.make_request()
```
ライブラリのインストール
Livebookの「Notebook dependencies and setup」に以下のコードを入力して実行し、ライブラリをインストールします。
Mix.install([
{:stripity_stripe, "~> 3.2"},
{:kino, "~> 0.14.2"}
])
stripity_stripe は、StripeのAPIをElixirで実行するためのサードパーティライブラリです。公式ライブラリは本記事の執筆時点では存在しません…
kino は、Livebook上で便利なウィジェットを使えるようにするライブラリです。本記事では秘匿情報の入力フォームのために使います。
秘匿情報の入力
Kino.Input.password/2 を使って秘匿情報の入力フォームを用意します。
Stripeシークレットキー
Livebookで以下のコードを実行すると入力フォームが表示されるので、シークレットキーを入力しましょう。
secret_key_input = Kino.Input.password("SECRET_KEY")
シークレットキーは 開発者ページ で確認できます。
Stripe価格ID
Livebookで以下のコードを実行すると入力フォームが表示されるので、価格IDを入力しましょう。
price_input = Kino.Input.password("PRICE")
価格IDは 商品カタログページ から確認できます。
Stripe Checkoutでのサブスクリプションの作成
Stripeでは、Checkout を使ってStripeが提供してくれる決済画面で手続きさせることができます。
Stripe.Checkout.Session/create/2 を使って決済画面のURLを発行します。秘匿情報は Kino.Input.read/1 で取得できます。
secret_key = Kino.Input.read(secret_key_input)
price = Kino.Input.read(price_input)
{:ok, session} =
Stripe.Checkout.Session.create(
%{
line_items: [
%{price: price}
],
mode: :subscription,
success_url: "https://dashboard.stripe.com/test/subscriptions"
},
api_key: secret_key
)
IO.puts(session.url)
表示されるURLにアクセスすると以下のような決済画面が表示されるので、カード情報(テストカード を使います)など諸々入力して申し込みましょう。
申し込みが完了すると、サブスクリプションページ から内容を確認できます。
この時点では使用量を記録していないので、次回請求は0円になっています。
Stripe Meter Eventでの使用量の記録
使用量を記録すると、次回請求に反映されます。
使用量を記録するには顧客IDが必要です。今回は、作成済みのサブスクリプションから顧客IDを取得します。
{:ok, %{data: [subscription | _]}} = Stripe.Subscription.list(%{}, api_key: secret_key)
customer = subscription.customer
ここで、使用量を記録しますが、残念ながらstripity_stripeは執筆時点でMeter Events APIに対応していないため、顧客作成APIのコード を参考に自前で実装しましょう。
今回は10ユニット使用したと想定します。event_name
にはメーターを作る際に入力した「sample」を指定します。
path = Stripe.OpenApi.Path.replace_path_params("/v1/billing/meter_events", [], [])
Stripe.Request.new_request(api_key: secret_key)
|> Stripe.Request.put_endpoint(path)
|> Stripe.Request.put_params(%{
event_name: "sample",
payload: %{
value: 10,
stripe_customer_id: customer
}
})
|> Stripe.Request.put_method(:post)
|> Stripe.Request.make_request()
少し待ってからサブスクリプションページを見ると、次回請求が1,000円になっています 100円の商品を10ユニット使用した想定なので、計算も合っていますね
最後に
今回はLivebookで完結させられるようにシークレットキーを都度指定していますが、Elixirアプリケーションを開発する際は config/*.exs
で設定するのが良いでしょう。
StripeではAPIのバージョンが更新されていき、挙動が変わってしまうこともあるので、明示的にバージョンを指定しておくとより安心です
config :stripity_stripe,
api_key: System.get_env("STRIPE_SECRET"),
api_version: "2024-11-20.acacia"
今回はかなり単純な例でしたが、Stripeでは段階的な価格にしたり複数の商品を組み合わせたりといった複雑な料金体系も簡単に実現できるので、ぜひ試してみてください
それにしても、一昔前と比べて決済を組み込むの楽になりましたねぇ(遠い目)。まあ、法律やセキュリティ基準もだいぶ厳しくなりましたが…
明日の Elixir Advent Calendar 2024 シリーズ4の24日目は @RyoWakabayashi さんです!
お楽しみに