wxcaptain's avatar

Cashier new subscription using Coupon Promotional Code

Hi,

The documentation shows how to create a new subscription using the method withCoupon(). With Stripe, however, a coupon, can have multiple promotional codes, so I'd like the user to apply the promotional code, rather than simple the coupon id.

I'm using 6.x. It doesn't look like cashier has the method to apply promotional codes, so I was wondering how to add on to the newSubscription() and add a withPromoCode() method.

Thanks for any suggestions.

0 likes
5 replies
wxcaptain's avatar

This is how the call currently looks with applying the coupon code:

            $return['success'] = ($this->user->newSubscription('pro', $planId)
                ->trialDays(config('services.subscription.trial_days'))
                ->withCoupon($request->couponCode)
                ->create($request->payment_method)) ? true : false;

the newSubscription method is from the Billable trait:

    public function newSubscription($subscription, $plan)
    {
        return new SubscriptionBuilder($this, $subscription, $plan);
    }

The SubscriptionBuilder() is on SubscriptionBuilder class.

It looks like I just need to add a property on to the SubscriptionBuilder create():

    public function create($paymentMethod = null, array $options = [])
    {
        $customer = $this->getStripeCustomer($paymentMethod, $options);

        /** @var \Stripe\Subscription $stripeSubscription */
        $stripeSubscription = $customer->subscriptions->create($this->buildPayload());

        if ($this->skipTrial) {
            $trialEndsAt = null;
        } else {
            $trialEndsAt = $this->trialExpires;
        }

        /** @var \Laravel\Cashier\Subscription $subscription */
        $subscription = $this->owner->subscriptions()->create([
            'name' => $this->name,
            'stripe_id' => $stripeSubscription->id,
            'stripe_status' => $stripeSubscription->status,
            'stripe_plan' => $this->plan,
            'quantity' => $this->quantity,
            'trial_ends_at' => $trialEndsAt,
            'ends_at' => null,
        ]);

        if ($subscription->incomplete()) {
            (new Payment(
                $stripeSubscription->latest_invoice->payment_intent
            ))->validate();
        }

        return $subscription;
    }

Via the stripe api docs, I just need to pass the "promotional_code" property. I'm just not sure, how I go about modifying in my app.

wxcaptain's avatar

Looks like answer was at the bottom of the Cashier documentation:

Many of Cashier's objects are wrappers around Stripe SDK objects. If you would like to interact with the Stripe objects directly, you may conveniently retrieve them using the asStripe method:

$stripeSubscription = $subscription->asStripeSubscription();

$stripeSubscription->update($subscription->stripe_id, ['application_fee_percent' => 5]);

Looks like, all i have to do is instead of passing 'application_fee_percent', I can pass 'promotional_code'. Looks easy enough.

martinbean's avatar
Level 80

@wxcaptain You’ll have to do some overriding of classes and methods to support this.

Subscriptions are built using the SubscriptionBuilder class. You’ll need to create a class in your application that extends this class, but also adds a withPromo method:

public function withPromotionCode($promotionCode)
{
    $this->promotionCode = $promotionCode;

    return $this;
}

You’ll then need to override your billable model to use your custom subscription builder class instead when newSubscription is called:

use App\SubscriptionBuilder;

class User extends Authenticatable
{
    use Billable;

    public function newSubscription($name, $plans)
    {
        return new SubscriptionBuilder($this, $name, $plans);
    }
}

Back in your custom SubscriptionBuilder class, you’ll need to override the buildPayload method to include the promotion code if one was supplied:

public function buildPayload()
{
    return array_merge(parent::buildPayload(), array_filter([
        'promotion_code' => $this->promotionCode,
    ]));
}

You can now use the withPromotionCode method when subscribing a user to a plan:

$subscription = $user
    ->newSubscription('pro', $planId)
    ->withPromotionCode('foo')
    ->create();

That will create the subscription up front with the promotion_code applied, rather than retrospectively updating the subscription, as the user will be charged the full price of the plan if you don’t apply the promotion code at the point of creation.

wxcaptain's avatar

Thank you for this! Since, may subscriptions have 14 day trial periods, I think retroactively updating will be fine in my case. However, your right, if there wasn't the trial period, I would have to override the classes.

thisrahul's avatar

Just add the allowPromotionCodes()

Route::get('/subscription-checkout', function (Request $request) { return $request->user() ->newSubscription('default', 'price_basic_monthly') ->trialDays(5) ->allowPromotionCodes() ->checkout([ 'success_url' => route('your-success-route'), 'cancel_url' => route('your-cancel-route'), ]); });

Please or to participate in this conversation.