StripeでCoupon/Promocodeをつかった割引を実現する（Laravel/Cashier）

[運営しているサービス](https://www.docswell.com/)に、割引を実装したのですが、海外のフォーラムでもあまり実装方法が載っていなかったのでシェアします。正しいかどうかはわからないので各自検証してください。

# 環境

 - Laravel 6系（lts）
 - Cashier 12系
 - Stripe sdk（Cashierから依存　7.70）

# Stripeで割引をするとはどういうことか

Stripeでユーザーに対し割引などプロモーションを実施する方法は主に2つ、FreeTrialとCouponです。FreeTrialは契約開始からN日間無料にするというもので、Couponは〇円引き、〇％引きが実現できるというものです。こちらのQiita記事でも解説されています。

[決済代行サービスのStripeでサブスクプランにクーポンを適用する方法【typescript, react, stripe, cloud functions】](https://qiita.com/rpyn/items/2b2bfdb1cbdf378d44ec)

こちらの記事では、Couponを利用されていましたが、Promocodeの例が見つからなかったので本記事はPromocodeについて解説します。

# Promotion Codeとは

Promotion codeとは、Coupon1つに対してN個発行可能な、ヒューマンリーダブルなコードです。Couponのままだと、原則ランダムなIDとなってしまいますが、Promocodeに関しては任意の名称を付与することが可能です（ **QIITA90OFF** のような ）

割引条件などはCouponに紐づいているのですが、Promocodeは利用可能な枚数と期間を任意に（Coupon自体の制限を超えない範囲で）設定できるので、たとえば以下のような構成が可能です

 - Coupon 90%オフ 2022/01/31まで　枚数上限なし
    - QIITA90　2022/01/31まで（設定なしの場合Couponの値が入る）　枚数上限なし
    - SUMMER90　2021/08/31まで　枚数上限10枚

このように、配布する場所と条件を決めることで、そのクーポンに色を付けることが可能です。Twitter限定！7日間限定50％オフクーポン！みたいな感じですね。では早速実装してみましょう。

# STEP1 Stripeの管理画面でクーポンを発行

こちらからクーポンをセットできます。（すでに一通り割引のない決済ができる前提で書きます）

https://dashboard.stripe.com/test/coupons

各種条件をセットしたあと一番下に「顧客に表示されるクーポンコード」という欄があるので、ここから、好きなクーポンコード（=Promocode）を発行します。余談ですがこのクーポンオブジェクト自体のIDはおすすめの通り空欄にしておきます。

![20210523-b67kv クーポン – Docswell – Stripe [テスト] - Google Chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8727/45d0bfda-1349-23f3-9f2b-f006d0b7ea88.png)

発行後はこのようなかんじで、Couponが20％割引、それに紐づくPromocodeがFRIENDS20となります。あとはこれを入力して受け入れるような仕組みを作ります。

# Promocodeが有効か調べるメソッドを作る

さて、Laravel/Cashierには、Subscriptionをcreateするときに、`withPromotionCode($code)` というメソッドをたたくだけでプロモコードが適用できるのですが、ここにセットできるのは人間用のFRIENDS20 **ではなく** `promo_xxxxxxx` のAPI用IDなのです。これをStripeと自前のアプリケーションで2重管理したくないのと、できればPromocodeが有効かどうか調べられた方が親切です。

そこで、以下のようなメソッドを作成し、プロモコードが存在するか調べるのとついでに存在したらAPI-IDを返してみました。

```php

// クーポンが有効か調べる
private function validatePromocode($code)
{
    try {
        $promoCodes = Cache::remember('stripe-cache-promocodes', 600, function () {
            \Stripe\Stripe::setApiKey(ここにStripeのシークレット);
            return \Stripe\PromotionCode::all(["active"=>true]);
        });
        foreach ($promoCodes->data as $promoCode) {
            if ($code==$promoCode->code) {
                // valid
                return $promoCode->id;
            }
        }
        return false;
    } catch (\Exception $e) {
        return false;
    }
}

```

ようは、すべての有効な、期限の切れていないPromocodeを取得し（10分間キャッシュし）人間用のcodeが一致したらStripe用のIDを返しています。

# 適用したサブスクリプションを作成

これであとは帰ってきたIDをもとにサブスクリプションを作成すればOKです。

```php
$user->newSubscription('default', Stripeの商品価格ID )
        ->withPromotionCode($promoCodeId)
        ->create();
```

# プロモーションが適用された結果をどう通知するか

今回はあまり工数をかけたくなかったので、決済後トライアル期間中にStripeが用意するCustomer Portalを見に行ってもらうことにしました。そこに飛ばしておけば、実際のトライアル終了後に割引価格での請求が予定されていることが見れますので安心ですね。

さらに親切にするのであれば、クーポン適用前に先ほどのPromocodeをAPIで返すようにしておき「クーポンが適用されました！」（実際はまだなにもしてない）とか表示してあげれば親切ですね。

# まとめ

- StripeのPromocodeは、Couponの割引条件に基づき枚数や期限など適用条件をかえてバンバン発行できるマーケ支援機能

- Stripeのカスタマーポータル便利で助かる

でした！よかったら本物の90％コード置いておくので試してみてください笑　初回7日間はトライアルで請求されません。 **QIITA90** 入力して申し込み、ポータルを試したうえでセルフ解約可能です。7日を超えると課金されちゃうので注意してください。

https://www.docswell.com/subscription