Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Kryptonit3's avatar

Question about Stripe and Cashier

On this sites subscription page you have an option for a coupon code.

@JeffreyWay I am curious as to the logic you use for this page.

If a user is subscribed (not canceled [graceperiod]) you just perform the swap function but looking at the code for Cashier the swap function doesn't accept a coupon code. So is that field only for a user who has canceled and wants to resubscribe?

Trying to figure out how to set this up properly with the logic using the subscribed check and when to use swap and resume and which accepts a coupon code.

If it is remotely what I think then shouldn't the coupon code field only be visible to a user who has cancelled and has fallen out of the grace period? Would I then use $user->subscription('new-subscription') or $user->resume('new-subscription').

Not much documentation of general use examples.

Here is what I have so far, not finished nor tested. I am either over thinking this, or underthinking it.

public function postSubscription(Request $request)
{
    // user has subscription, just wants to update
    if($this->auth->user()->subscribed())
    {
        // verify user is not on selected plan
        if ($request->input('subscription-plan') !== $this->auth->user()->getStripePlan()) {
            if ($request->has('coupon-code')) {
                $this->auth->user()->subscription($request->input('subscription-plan'))->withCoupon($request->input('coupon-code'))->swap();
            } else {
                $this->auth->user()->subscription($request->input('subscription-plan'))->swap();
            }
            session()->flash('success_msg', 'Subscription updated successfully!');
            return redirect()->back();
        }
        session()->flash('error_msg', 'You are already on this plan!');
        return redirect()->back();
    }
    // user isn't subscribed, resubscribe and check for coupon code
    if ($request->has('coupon-code')) {
        $this->auth->user()->subscription($request->input('subscription-plan'))->withCoupon($request->input('coupon-code'));
    } else {
        $this->auth->user()->subscription($request->input('subscription-plan'));
    }
    session()->flash('success_msg', 'Subscription updated successfully!');
    return redirect()->back();
}
0 likes
13 replies
theUnforgiven's avatar

Here's an example of how I implemented a coupon input in a recent project, see if this can help you :) taking note of the withCoupon($coupon) method.

public function subscribe($token)
    {
        $id = Auth::id();
        $user = User::find($id);
        $coupon = Input::get('coupon');
        // Check if a coupon is present
        if (isset($coupon))
        {
            $user->subscription('test-sub')->withCoupon($coupon)->create($token,
            ['tax_percent' => 20.0,
                'email' => $user->email,
                'description' => 'Subscription'],
            null);
          
        }
        // If there's no coupon, continue with ordinary subscription
        else {
            $user->subscription('test-sub')->create($token,
                ['tax_percent' => 20.0,
                'email' => $user->email,
                'description' => 'Subscription'],
                null);
       
        }
        // Update the database with the returned StripeToken, that Stripe provide us with, to be used later
        // for resuming subscriptions etc.
        $user->stripe_token = $token;
        $user->update();

        return true;
    }
Kryptonit3's avatar

thanks. I have no issue subscribing my customers, I require them to subscribe before they can have an account (like here). My question pertains to implementing coupons with Cashier to existing customers who have either canceled their subscription and want to renew, or users who want to swap subscriptions. (both of which can be done directly on Stripe.com)

I want to be able to keep using the Cashier implementation due to its ability to update/set/cancel subscriptions.

theUnforgiven's avatar

Yes you could do a similar kinda thing as

$user->subscription('test-sub')->withCoupon($coupon)->create($token,
            ['tax_percent' => 20.0,
                'email' => $user->email,
                'description' => 'Subscription'],
            null);

By showing a "cancelled" customer a form to re-instate their subscription or storing the stripe token in your DB then when a cancelled customer clicks a button for example grab this from the DB and run it the same method to re-activate their account with a coupon. Hope that makes sense :)

Kryptonit3's avatar

what about coupons on a swap. Like a monthly user decides he wants to go yearly and we have a coupon for 15% off yearly while considering the user already has an active subscription. Should I cancel their subscription on the back end and then just create a new one? Will stripe and cashier know to append the new subscription with coupon to the remaining time from the existing monthly (and cancelled on backend) subscription?

When would I use the resume method?

\vendor\laravel\cashier\src\Laravel\Cashier\StripeGateway
theUnforgiven's avatar

You would effectively use the swap method, you can still use Stripe methods within Cashier even if they don't exist, always refer back to Stripe's docs if in doubt.

Kryptonit3's avatar

but from what I can tell the swap method doesn't accept the withCoupon method.

Kryptonit3's avatar

@lstables - I was messing around with stuff and found out you cannot charge the token more than once (the initial card setup or customer creation).

What I would essentially like is

$user->subscription('yearly')->withCoupon('15off')->charge();

but charge wants a token and I don't want to ask the user for their card again.

If a user is expired I want them to be able to choose a plan, and enter a coupon if they have one, and then charge the customer (card on file) not the token (card) because you cannot charge the token twice.

Kryptonit3's avatar

Well, looks like this will be the only way to do that.

// apply the coupon first to the customer on stripe
$user->applyCoupon('15off');
// coupon will then get applied to next action
$user->subscription('yearly')->resume();
1 like
Kryptonit3's avatar

I am hoping this will solve my issue

    public function postSubscription(Request $request)
    {
        // user has subscription, just wants to update
        if($this->auth->user()->subscribed() && ! $this->auth->user()->onGracePeriod())
        {
            // verify user is not on selected plan
            if ($request->input('subscription-plan') !== $this->auth->user()->getStripePlan())
            {
                if ($request->has('coupon-code')) {
                    $this->auth->user()->applyCoupon($request->input('coupon-code'));
                    $this->auth->user()->subscription($request->input('subscription-plan'))->swap();
                } else {
                    $this->auth->user()->subscription($request->input('subscription-plan'))->swap();
                }

                session()->flash('success_msg', 'Subscription updated successfully!');
                return redirect()->back();
            }
            session()->flash('error_msg', 'You are already on this plan! To apply a coupon to your next billing cycle go to the coupon page!');
            return redirect()->back();
        }

        // user isn't subscribed, resubscribe and check for coupon code
        if ($request->has('coupon-code')) {
            $this->auth->user()->applyCoupon($request->input('coupon-code'));
            $this->auth->user()->subscription($request->input('subscription-plan'))->resume();
        } else {
            $this->auth->user()->subscription($request->input('subscription-plan'))->resume();
        }
        session()->flash('success_msg', 'Subscription updated successfully!');
        return redirect()->back();

    }
matism's avatar

Hi there,

@lstables - I just found your way to implement VAT with laravel cashier:

            $user->subscription('test-sub')->withCoupon($coupon)->create($token,
            ['tax_percent' => 20.0,
                'email' => $user->email,
                'description' => 'Subscription'],
            null);

I tried that by myself but I wasn't able to get it working. Currently I am in Stripes test mode and I am using the demo-cc with the number 4242 4242 4242 4242. Do you have any idea what I am doing wrong?

I would appreciate any help! Thanks mat

WKMG's avatar

What about checking if the coupon is valid or not?

1 like
psaunders's avatar

@WKMG

    try {
        $coupon = \Stripe\Coupon::retrieve($code);
        if($coupon->amount_off) {
            $price =  "$".($coupon->amount_off / 100).' '.$coupon->duration;
        } else {
            if( $coupon->duration == 'repeating' ) {
                $duration = '(' . $coupon->duration_in_months . ' months)';
            } else {
                $duration = '(forever)';
            }
            $price = $coupon->percent_off . '% for ' . $duration;
        }
        
        return ['valid'=>'true','message'=>$price];
        
    } catch(\Exception $e) {        
        return ['valid'=>'false'];
    }

Please or to participate in this conversation.