4
votes

According to the Stripe documentation, it appears as though you can actually apply coupons to a customer as a whole, but you can also apply coupons to their subscriptions. I am trying to apply a coupon to the customer's subscription after the subscription has already been created. I am using Laravel Cashier 4.2.

Here is what I have tried:

 $company = Auth::user()->company;
 $customer = $company->subscription()->getStripeCustomer(); // returns a StripeGateway object
 $customer->subscription->applyCoupon($input['coupon_code']);

Here is the error message:

"Call to undefined method Stripe_Subscription::updateSubscription()"

I can use the applyCoupon() method to a customer as a whole, but not an actual subscription... Ideas appreciated.

The Stripe documentation only shows how to remove a discount from a subscription: Discount Object. The only other information I've been able to find in their documentation is:

Coupons and discounts

If you want to provide discounts to certain customers, you can create coupon codes in the Dashboard. Coupons have a discount percentage and a duration, so you could create coupons like a 10% lifetime discount, or a one month 50% discount, etc. Coupons can also have expiration dates attached, after which they can't be used. Here's an example of adding a discount to a user's subscription. In this case, we've already created a coupon called 50OFF1MONTH:

curl https://api.stripe.com/v1/customers/cus_4fdAW5ftNQow1a \ -u sk_test_4LOIhlh19aUz8yFBLwMIxnBY: \ -d coupon=50OFF1MONTH

However, this isn't very helpful. Once again, Laravel's documentation is a little to elegant, and it's missing any info on the topic.

I wonder if I just need to recreate the subscription object entirely with the new coupon... But that's not ideal because you need a card token for that.

Update 1

I can confirm that this does in fact apply a coupon to the subscription itself

$user->subscription('monthly')
 ->withCoupon('code')
 ->create($creditCardToken);

However, once again the question is how can a coupon be added after the fact.

5

5 Answers

3
votes

It doesn't look like what I'm trying to do is possible, so I found another way. All I do is swap the users existing plan to the current plan and use the withCoupon method. This does appear to apply the coupon to the subscription and doesn't seem to charge the user or change their billing period, but I could be wrong...

$company->subscription($company->stripe_plan)
        ->withCoupon($input['coupon_code'])
        ->swap(); 
3
votes

Starting with Cashier v11.* there are new convenience methods to update the underlying Stripe subscription object. To apply a coupon to the user's Subscription model instead of the Customer model, try this in the controller:

$user->subscription('default')->updateStripeSubscription(['coupon' => $couponCode]);

To remove the coupon from the subscription, send an update request and pass an empty coupon string. As described under another question, a coupon set to an empty string removes it, while passing null is ignored.

$user->subscription('default')->updateStripeSubscription(['coupon' => '']);
1
votes

Adding a Coupon to an Existing Subscription. You can also use cashier method as below:

try {
$user->applyCoupon('coupon_code');
return redirect()->back()->withMessage('Coupon Applied');  
} catch (Exception $e) {
return back()->withError($e->getMessage());
}

When applying a coupon, it's best to wrap this in a try / catch block because if the user enters an invalid coupon, Stripe will return an exception with the reason for the failure.

1
votes

For everyone who can't find an easy and convenient way to swap the subscriptions with a coupon, here's what I came up with recently.

First of all, we'll have to create a custom Subscription eloquent model and extend Laravel\Cashier\Subscription class.

Now we're going to create the withCoupon() method there:

public function withCoupon($coupon)
{
    $this->coupon = $coupon;

    return $this;
}

The next thing will be extending the original swap() method and adding the coupon part. Just copy and paste the original method and add this piece of code somewhere before the $subscription->save(); line.

...
// Applying the discount coupon if such exists
if ($this->coupon) {
     $subscription->coupon = $this->coupon;
}
...
$subscription->save();

The last thing you've got to do in order to make things work properly is to create a custom Billable trait and change subscriptions() method to use your custom Subscription model.

After that, you can apply your very own Billable trait to the billable model and apply coupons while swapping plans.

$user
    ->subscription($product)
    ->withCoupon($coupon)
    ->swap($plan);
0
votes

Using Laravel 7.x with Cashier 10 you have the applyCoupon() method on a Billable (User) model.

Something like this in the controller:

 /**
 * Apply Stripe coupon
 */
public function applyCoupon(Request $request){

    $user = $request->user();
    if ($user->subscribedToPlan('plan_name', 'default')) {
        try {
            $user->applyCoupon($request->couponCode);
            return redirect('subscription')->withMessage('Coupon Applied');
        }  catch (\Exception $e) {
            return redirect('subscription')->withError($e->getMessage());
        }
    } 
}