Get next billing date from Laravel Cashier - laravel

I have subscribed a user to a subscription plan through Laravel's Cashier package. I now want to display the date the user will next be billed, however this doesn't appear to be an available through the Billable trait.
How do I get the next billing date?
Thanks!

The solution is to use the asStripeCustomer method:
// Retrieve the timestamp from Stripe
$timestamp = $user->asStripeCustomer()["subscriptions"]->data[0]["current_period_end"];
// Cast to Carbon instance and return
return \Carbon\Carbon::createFromTimeStamp($timestamp)->toFormattedDateString();
Note that I've only tested this with a user who has a single subscription - data[0].
You may need to alter this code for multiple subscriptions or if the user has cancelled and started another subscription.

Building on previous answers, here's what's working for me:
private function getSubscriptionRenewDate($plan)
{
$sub = Auth::user()->subscription($plan)->asStripeSubscription();
return Carbon::createFromTimeStamp($sub->current_period_end)->format('F jS, Y');
}

Subscriptions also have a ->asStripeSubscription() method that gives you access to the values just for that subscription. So you could do:
// Retrieve the timestamp from Stripe
$timestamp = $subscription->current_period_end;
// Cast to Carbon instance and return
return \Carbon\Carbon::createFromTimeStamp($timestamp)->toFormattedDateString();

Related

How to Cancel the Braintree Subscription by Braintree_id or subscription_id using Laravel Cashier

We are using Laravel Cashier (Braintree) with Laravel version 5.8. We have a case where a user is subscribed to same plan with same name multiple times for different orders.
We want to give the ability to user to cancel their subscription.
we tried below statement to cancel the subscription with subscription name as suggested by manual here https://laravel.com/docs/5.8/braintree#cancelling-subscriptions.
$user->subscription('main')->cancel();
$user->subscription('main')->cancelNow();
We are passing the subscription name. It works fine as expected and also updating the date in "ends_at" column of subscription table.
The problem here is that as we have same name for the subscriptions where user is subscribed to. So in our case it returns the last subscribed order here and cancel that. It's fine as what it is suppose to do.
But we want to cancel the subscription based on braintree_id stored in subscriptions table. Can we do that ?
As of now we tried it like below:-
use Braintree\Subscription;
$subcriptionObj = Subscription::find($subscription); //where $subscription is braintree_id from subscriptions table.
if ($subcriptionObj->status == 'Canceled')
abort(400, 'Subscription Not Active');
Subscription::cancel($subscription);
This however cancel the subscription at Braintree but not updating the column "ends_at" in subscriptions table.
Can anyone suggests a workaround for this ? Any help would be appreciated.
Since the Laravel Braintree Cashier module is internally using Braintree Subscription library. So I thought to use the same directly into my controller.
I used namespace into my controller for subscription to call the Braintree subscription class directly. the below is the code to cancel the subscription by subscription ID.
use Braintree\Subscription;
public function cancelsubscription(User $user, $subscriptionId)
{
$subcriptionObj = Subscription::find($subscriptionId);
if(is_null($subcriptionObj)){
abort(400, 'Subscription is not found.');
}
if ($subcriptionObj->status == 'Canceled')
abort(400, 'Subscription is not Active.');
// In below line we are finding the Subscription DB Obj using cashier module here to update the ends_at date column
$subsDbObj = $user->subscriptions->filter(function($sub) use ($user,$subscriptionId){
return $sub->braintree_id == $subscriptionId && $sub->user_id == $user->id;})->values();
Subscription::cancel($subscriptionId);
if(! is_null($subsDbObj[0])){
//Internally cashier module doing the same to update the subscription table
$subsDbObj[0]->ends_at = Carbon::now();
$subsDbObj[0]->save();
}
return 'Cancelled';
}

Laravel Cashier - Cancel a subscription using the stripe id

I'm using Laravel Cashier with stripe payment. One user can have multiple subscriptions. User should able to cancel particular subscription. Is there anyway to cancel subscription by stripe id or plan id?
You can use the PHP Stripe library to do it.
To cancel immediately
$sub = Stripe\Subscription::retrieve($subscription_id);
$sub->cancel();
To cancel after current period end
$sub = Stripe\Subscription::update($subscription_id, [
'cancel_at_period_end' => true
]);
You can use it with Laravel Cashier using subscription id .
In Laravel
You can find the subscription id in the subscriptions table in your database.(column name is stripe_id)
$subscription = \Stripe\Subscription::retrieve($subscription_id);
If you pass the correct subscription id , you will get the subscription details
To cancel the subscription
$sub->cancel();
Update your subscriptions table for the particular subscription id (again column name is stripe_id). In my
\Stripe\Subscription::where('stripe_id', $sub->id)
->update([
'trial_ends_at' => Carbon::now()->toDateTimeString(),
]);
I'm sure this is documented somewhere, but for the life of me I can't find it, but, if you pass the stripe subscription id in as the 2nd parameter you can cancel any subscription the Laravel way...
$user->subscription('default', 'price_abcdefghi12345')->cancel();
You can just call the cancel() method directly on your subscription:
$subscription->cancel();
As you can see here (Github Reference), the method $this->subscription called from your User model just gets a Subscription instance filtering by name:
/**
* Get a subscription instance by name.
*
* #param string $name
* #return \Laravel\Cashier\Subscription|null
*/
public function subscription($name = 'default')
{
return $this->subscriptions->where('name', $name)->first();
}
And, as you can see (Github Reference), the Subscription model has its own cancel method:
/**
* Cancel the subscription at the end of the billing period.
*
* #return $this
*/
public function cancel()
{
// ...
}
So, you can always call it directly on the Subscription instance.
You don't need to have the id to cancel.
You can just call
$subscription->cancel();
and it will work. If you have multiple subscriptions which you would like to cancel at the same time. You can do this. Let's find all the active subscriptions. You can find all the scopes here
$subscriptions = $user->subscriptions()->active()->get(); // getting all the active subscriptions
$subscriptions->map(function($subscription) {
$subscription->cancel(); // cancelling each of the active subscription
});
You can do this by using the cashier Subscription model as like Laravel model
Cancel Subscription
$subscription = Subscription::where('name', 'default')->where('user_id', auth()->id())->first();
$subscription->cancel();
Resume Subscription
$subscription = Subscription::where('name', 'default')->where('user_id', auth()->id())->first();
$subscription->resume();

Laravel cashier `$user->subscribed()` returning true when no subscription exists

My code is simple:
if($user->subscribed('main')){
$user->subscription('main')->cancel();
}
I get this response:
Stripe \ Error \ InvalidRequest
No such subscription: sub_EQTvxKjit2Ak6i
The subscription was in fact previously cancelled, so it shouldn't be giving a true response.
I tried returning $user->subscribed('main') and it came back as true.
This user has other subscriptions, but 'main' is not active.
Am I missing something?
Update Cashier v13.4
You can first sync the database entry with Stripe using:
optional($user->subscription('main'))->syncStripeStatus();
Then call
if ($user->subscribed('main')) { ... }
$user->subscribed() only checks the database for subscription status--it doesn't query the Stripe API at all. But when you try to cancel that subscription, then it queries the API.
So that could produce an error like this if your database is out of sync with your data in Stripe. Maybe your database has subscription from Stripe test mode, but you're querying the Stripe production API? Or vice versa?
If you read Billable trait then you will find this function there
public function subscribed($subscription = 'default', $plan = null)
{
$subscription = $this->subscription($subscription);
if (is_null($subscription)) {
return false;
}
if (is_null($plan)) {
return $subscription->valid();
}
return $subscription->valid() &&
$subscription->stripe_plan === $plan;
}
This function only checks subscription record exists in subscriptions table or not.
According to above function if subscription does not exist then it will return false.
Make sure you are not doing any mistake.

Laravel Cashier Re-attempt Pending Invoice after Card Updates

I am using Laravel 5.3 with Cashier. If a customer updates their card details, how can I check if there is a pending invoice and ask Stripe to re-attempt the charge on the new card? At the moment, I have set the settings for attempts in Stripe dashboard. But from what I understand, Stripe does not automatically attempt to charge the customer if they updated their card details and it waits for the next attempt date to try again. Thats why I want to manually attempt to charge the customer on pending invoice as soon as they update their card. I read the Cashier documentation and Github page but this case is not covered there.
$user->updateCard($token);
// Next charge customer if there is a pending invoice
Can someone help me out please.
After testing and talking with Stripe support, I found out the problem with the current updateCard() method used in Laravel Cashier.
With the current updateCard() method, the card is added to the sources list and then sets the new card as the default_source. the result of this method has 2 outcomes:
Multiple cards gets added to the list although the recent one is set as default_source
When updating the card using this method, if there are any unpaid invoices (i.e. invoices in past_due state), they are not automatically charged.
In order for stripe to re-attempt charging customer on all invoices in past_due state, the source parameter needs to be passed. So I have created a new method something like this:
public function replaceCard($token)
{
$customer = $this->asStripeCustomer();
$token = StripeToken::retrieve($token, ['api_key' => $this->getStripeKey()]);
// If the given token already has the card as their default source, we can just
// bail out of the method now. We don't need to keep adding the same card to
// a model's account every time we go through this particular method call.
if ($token->card->id === $customer->default_source) {
return;
}
// Just pass `source: tok_xxx` in order for the previous default source
// to be deleted and any unpaid invoices to be retried
$customer->source = $token;
$customer->save();
// Next we will get the default source for this model so we can update the last
// four digits and the card brand on the record in the database. This allows
// us to display the information on the front-end when updating the cards.
$source = $customer->default_source
? $customer->sources->retrieve($customer->default_source)
: null;
$this->fillCardDetails($source);
$this->save();
}
I have created a Pull request for this addition. Since editing the Billable file directly for any changes is not a good idea, if this doesnt get added to Cashier, then you can use the following in the Controller file to do it directly from there:
$user = Auth::User();
$customer = $user->asStripeCustomer();
$token = StripeToken::retrieve($token, ['api_key' => config('services.stripe.secret')]);
if (!($token->card->id === $customer->default_source)) {
$customer->source = $token;
$customer->save();
// Next synchronise user's card details and update the database
$user->updateCardFromStripe();
}

Laravel Cashier Webhook: Get Amount and Invoice Details

I am using Laravel 5.3 with Cashier for Stripe. I have set-up a custom controller extending the CashierController for handling Webhooks as per the Docs. In this Webhook, how can I get the amount that was charged and invoice details from $payload?
public function handleInvoicePaymentFailed($payload)
{
// Handle The Event
$customer = $this->getBillable($payload['data']['object']['customer']);
// NEXT - HOW TO GET THE AMOUNT THAT WAS CHARGED AND INVOICE NUMBER??
return new Response('Webhook Handled', 200);
}
I want to get the following info from that:
Amount
Invoice Number
You should be able to use $payload['data']['object']['amount_due'] to get the amount due and $payload['data']['object']['id'] to get the stripe invoice id.
Go to https://dashboard.stripe.com/test/events, click on the event and look at the Event Data there.

Resources