この記事はStripe Advent Calendar 2021の7日目の記事です。
開発しているStripeを使ったWebサービスで、
クーポンをいろいろ使えるようにしたいなと思い、いろいろ調べたときの備忘録。
使い方が特殊なのか、かなりハマった。。
やりたいこと
やりたかったのはこんな感じ。
- Checkoutでの決済時にクーポンを利用したい
- 継続中のときに、クーポンを利用したい
- クーポン適用後に別のクーポンを利用したい
Stripeでの仕組み
出てくるStripeの用語
用語自体、なじみがないので、簡単なまとめ
- サブスクリプション ... サブスク。継続支払い。
- インボイス ... 請求書。顧客が払うサブスクの金額など
- クーポン ... インボイスを減額できるクーポン
- プロモーションコード ... クーポンから生成する顧客向けのコード
Stripeでのクーポン
公式ドキュメントは以下。
・クーポン | Stripe のドキュメント
Stripeでは、減額(ディスカウント)するクーポンを作成できる。
特定期間、対象商品などの指定もできる。
顧客向けのプロモーションコードに関しては以下。
・顧客向けのプロモーションコードを使用する | Stripe のドキュメント
クーポンの注意点
いろいろ調べたところ、以下の点に注意する必要がある。
- クーポンは顧客もしくはサブスクリプションに適用できる
- ただし、顧客に適用すると、設定回数に関わらず、毎請求ごとに適用される
- そのため、サブスクリプションに適用することが推奨されている
- 1つのインボイスに対し、適用できるクーポンは1つのみ
- クーポンを適用できるインボイスは、支払い前のもののみ
- 支払い後にクーポンを適用できない
- Checkoutで入力できるのは、プロモーションコードのみ
- APIで適用できるのはクーポンIDとプロモーションコードIDのみ
- 継続中にAPIで適用する場合は、IDが必要
- 顧客にはプロモーションコードしか提供できない
結構ややこしい。。(´・ω・`)
やってみたこと
やりたいことに対して、それぞれ試してみた。
Checkoutでの決済時にクーポンを利用したい
まだ購入していない顧客(ユーザ)が初めて購入するときに、
プロモーションコードを入力して割引を受ける場合。
これは特に問題なくできる。
- クーポンを作成する
- クーポンからプロモーションコードを作成する
- カスタマーポータルの設定でユーザがプロモーションコードを入力できるようにする
- 「設定 > カスタマーポータル」で設定画面に移動
- 「顧客によるプロモーションコードの適用を許可します。」をONにする
Checkoutで支払いをするときに、プロモーションコードを追加できるようになる。
継続中のときにクーポンを利用したい
ここからはちょっとめんどくさい。。
以下の前提がネック。。
また、Checkoutやカスタマーポータルでは対応できないので、
APIを使ってがんばる感じ。
流れはこんな感じ。
それぞれのAPIをみていく。
1. 未決済のインボイスを取得
- Retrieve an upcoming invoice | Stripe API Reference
- Retrieve an upcoming invoice's line items | Stripe API Reference
未決済インボイスのプレビューについてはガイドの以下にかかれている。
取得するのはこんな感じ。
import Stripe from "stripe"; const stripe = new Stripe('your_api_secret_key'); async function main() { const customerId = "cus_XXXXXXXXX"; const res = await stripe.invoices.retrieveUpcoming({ customer: customerId }); console.info(`res=${JSON.stringify(res, null, 2)}`); } main().then();
必要なところだけに絞っているけど、以下のレスポンスが返ってくる。
{ "object": "invoice", "billing_reason": "upcoming", "created": 1637300842, "customer": "cus_XXXXXXXXX", "discount": null, "discounts": [], "lines": { ... }, "next_payment_attempt": 1637304442, "paid": false, "status": "draft", "subscription": "sub_1JusbeGjCKgy1AJ9ZucGSMHO", "subtotal": 100, "tax": null, "total": 100, "total_discount_amounts": [], "total_tax_amounts": [], }
total
: 請求金額next_payment_attempt
: 次回の支払い予定日時discount
: 適用されている割引情報
クーポンが適用されているとこんな感じ。
total
が割引後の金額になるdiscount
: 適用されている割引情報total_discount_amounts
: 割引情報と割引金額
{ "object": "invoice", "billing_reason": "upcoming", "created": 1637300842, "customer": "cus_XXXXXXXXX", "discount": { "id": "di_1JxPXIGjCKgy1AJ9UXJqykPS", "object": "discount", "coupon": { "id": "X9gdG0V8", }, "customer": "cus_XXXXXXXXX", "end": null, "invoice": null, "invoice_item": null, "promotion_code": null, "start": 1637299279, "subscription": "sub_1JusbeGjCKgy1AJ9ZucGSMHO" }, "discounts": ["di_1JxPXIGjCKgy1AJ9UXJqykPS"], "lines": { ... }, "next_payment_attempt": 1637304442, "paid": false, "status": "draft", "subscription": "sub_1JusbeGjCKgy1AJ9ZucGSMHO", "subtotal": 100, "tax": null, "total": 0, "total_discount_amounts": [ { "amount": 100, "discount": "di_1JxPXIGjCKgy1AJ9UXJqykPS" } ], }
2. 入力されたプロモーションコードからプロモーションコードIDを取得
クーポンから発行したプロモーションコードは、そのままAPI使えないので、
顧客に提供したプロモーションコードからプロモーションコードIDを取得する。
const promoCodes = await stripe.promotionCodes.list({ code: code, // プロモーションコードの指定 active: true, // 有効なもののみ }); console.info(`res=${JSON.stringify(promoCodes, null, 2)}`); const promoCodeId = promoCodes.data.length > 0 ? promoCodes.data[0].id : null;
プロモーションコードに該当するものがあれば、以下のようなレスポンスが返ってくる。
{ "object": "list", "data": [ { "id": "promo_1JxSFqGjCKgy1AJ91YuTZNBh", "object": "promotion_code", "active": true, "code": "CMSC3HBE", "coupon": { ... }, "created": 1637309730, "customer": null, "expires_at": null, "livemode": false, "max_redemptions": null, "metadata": {}, "restrictions": { ... }, "times_redeemed": 0 } ], "has_more": false, "url": "/v1/promotion_codes" }
該当するものがないと、以下のようなレスポンス。
{ "object": "list", "data": [], "has_more": false, "url": "/v1/promotion_codes" }
3. 取得したプロモーションコードIDをサブスクリプションに適用
プロモーションコードIDが取得できたので、サブスクリプションに適用する。
const subscriptionId = "...サブスクリプションのID" const promoCodeId = "...プロモーションコードのID" await stripe.subscriptions.update(subscriptionId, { promotion_code: promoCodeId });
もちろん、プロモーションコードじゃなくてクーポンも適用できる。
const subscriptionId = "...サブスクリプションのID" const couponId = "...クーポンのID" await stripe.subscriptions.update(subscriptionId, { coupon: couponId });
クーポン適用後に別のクーポンを利用したい
最後にクーポンの変更。
基本的には、上記のサブスクリプションの更新APIでOK。
更新なので、支払いが完了するまでは自由に変更できる。
ただ、クーポンを削除する場合は、別のAPIを利用する必要がある。
const subscriptionId = "...サブスクリプションのID" await stripe.subscriptions.deleteDiscount(subscriptionId);
おまけ: Firebase Extensionsでの動き
Firebase ExtensionsにもStripeを便利に使うものが用意されている。
・Run Payments with Stripe
これを使うと、
・Stripe Checkoutを使った支払いを簡単にするFunctionsを追加してくれたり
・Webhookを使って、サブスクの情報をFirestoreと同期してくれたり
する。
インボイス情報も同期してくれるので、それを見る方法もある。
Webhookのイベントは、invoice.upcoming
・Stripe API reference – Types of events
ただ、このイベントが発火するのは、
「設定>サブスクリプションおよびメール」
の「次回の更新イベント」で設定した日付になる。
ここで何日前に新しいインボイスを作成するかを指定でき、
・指定日になったら、インボイスが作成されて、
・イベントが発火したあとに、Firestoreに保存される
感じ。。
なので、好きなのタイミングで取得するには、
やっぱり、以下のAPIを呼ぶ必要がある。
・ Retrieve an upcoming invoice | Stripe API Reference
Firebase Extensionsに関しては、こちらの記事をどうぞ!
まとめ
これで、やりたかったことは全部できるように(´ω`)!!
クーポンに関しては情報が少ないので参考になれば
Stripeを使ったSSSAPIも、ぜひよろしくおねがいします!!
GoogleスプレッドシートのAPI化サービス 『SSSAPI』
\㊗️正式リリース㊗️/#SSSAPI の正式版を公開しました🎉
— 【公式】SSSAPI / GoogleスプレッドシートのAPI化サービス (@sssapi_app) November 30, 2021
開発開始から9ヶ月...無事リリースを迎えられました😭
β版でのご協力、ありがとうございます🙇♂️
まだまだ不便なところもございますが、
より一層便利になるよう改善がんばります💪💪💪#拡散希望RTお願いします https://t.co/znp6CJGZEs