-
Notifications
You must be signed in to change notification settings - Fork 684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[10.0] Implement Stripe Checkout #652
Conversation
26a6992
to
03b39c2
Compare
While working on the webhook after a session was completed I was doubting between choosing the subscription created webhook or checkout session completed. Will need to investigate further. Also thinking about maybe creating the subscription already in the database when creating the session since we have a subscription ID normally: https://stripe.com/docs/api/checkout/sessions/create |
7f8a751
to
aa4fd0b
Compare
For what it's worth, passing an existing customer while creating a subscription with Checkout still isn't available. :( https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-customer |
Since Stripe still hasn't updated their API with the necessary changes in order to let this move forward we're taking this out of the next release. We'll have to wait and see when Stripe makes the necessary changes listed above. |
I'm going to close this for now until we decide to revisit it. |
@taylorotwell sure thing 👍 |
Just wanted to add here that seems you can now link existing customers https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-customer |
@smartsyncio nice. Maybe we can pick it up for a future version. |
@driesvints Would you kindly be able to give up pointers on how to implement that ourselves? Not sure what is missing from your current PR. |
@NSpehler see the concerns above |
@driesvints thanks for raising these issues. I reached out to Stripe and this is what they responded: "there is the ability with New Checkout to use existing customers for subscriptions, this just has to be done through the API. See here: https://stripe.com/docs/payments/checkout/server#using-existing-customers" Is/Will Cashier be updated ? Any info greatly appreciated :) If you could help prioritize this it would be greatly appreciated. Ricardo |
I still want to get this in but probably won't have the time in the upcoming months sorry. We're still open to pull requests. |
@driesvints is it still planned to implement this? I recently implemented subscriptions with cashier and the stripe checkout based on your code without too much pain. The only thing that had to be added was a What exactly were the main issues for not implementing and closing this? <?php
namespace App\Http\Controllers;
use App\Services\Stripe;
use Illuminate\Support\Carbon;
use Laravel\Cashier\Http\Controllers\WebhookController as Controller;
use Symfony\Component\HttpFoundation\Response;
class WebhookController extends Controller
{
private $stripe;
public function __construct(Stripe $stripe)
{
parent::__construct();
$this->stripe = $stripe;
}
protected function handleCheckoutSessionCompleted(array $payload)
{
// Setup Intent...
$setupIntent = $this->stripe->retrieveSetupIntent($payload['data']['object']['setup_intent']);
// Payment Method...
$paymentMethod = $this->stripe->retrievePaymentMethod($setupIntent->payment_method);
// Customer...
if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
// Update Default Payment Method...
$user->updateDefaultPaymentMethod($paymentMethod);
// Update Billing Address...
$address = $paymentMethod->billing_details->address;
$user->forceFill([
'billing_address' => $address->line1,
'billing_address_line_2' => $address->line2,
'billing_city' => $address->city,
'billing_state' => $address->state,
'billing_zip' => $address->postal_code,
'billing_country' => $address->country,
])->save();
}
return $this->successMethod();
}
protected function handleCustomerSubscriptionCreated(array $payload)
{
$user = $this->getUserByStripeId($payload['data']['object']['customer']);
file_put_contents(storage_path('test.json'), json_encode($user->toArray()));
if ($user) {
$data = $payload['data']['object'];
if (! $user->subscriptions->contains('stripe_id', $data['id'])) {
if (isset($data['trial_end'])) {
$trialEndsAt = Carbon::createFromTimestamp($data['trial_end']);
} else {
$trialEndsAt = null;
}
$user->subscriptions()->create([
'name' => $data['metadata']['name'],
'stripe_id' => $data['id'],
'stripe_plan' => $data['plan']['id'],
'quantity' => $data['quantity'],
'trial_ends_at' => $trialEndsAt,
'ends_at' => null,
]);
}
}
return new Response('Webhook Handled', 200);
}
} |
@faustbrian the concerns listed on this PR in the first comment weren't all resolved at the time of making this PR. They could be by now. |
@faustbrian were you able to migrate to the new stripe version ? I am still using the legacy one, concerning is that the legacy one isn't going to be updated anymore. It is also out of compliance in Europe. @driesvints is this going to be released in Q1 / Q2 ? or at this stage there are no plans for updating cashier to support the new version of Stripe ? Thanks |
ok thanks @driesvints |
Hey Sorry, was a bit too quick to reply. We'd still welcome prs for this but aren't planning to be working on this ourselves at least not at this time. |
I got Stripe Checkout working without any problems with the code I posted above. Have a full test-suite for it with dusk, integration and unit tests and everything works as expected so should be fine to integrate it into Cashier directly. |
@faustbrian Would you be so nice and also add a code snippet of your session creation? AFAIK Stripe Checkout expects a session token, which differs from a setup intent. |
<?php
namespace App\Services;
use App\Models\Plan;
use App\Models\Team;
use Carbon\Carbon;
use Laravel\Cashier\Cashier;
use Stripe\Checkout\Session;
use Stripe\PaymentMethod;
use Stripe\SetupIntent;
class Stripe
{
public function createSession(Team $team, Plan $plan): Session
{
return Session::create([
'customer' => $team->stripe_id,
'payment_method_types' => ['card'],
'billing_address_collection' => 'required',
'subscription_data' => [
'items' => [
[
'plan' => $plan->stripe_id,
'quantity' => 1,
],
],
'metadata' => [
'name' => 'default',
],
'trial_end' => Carbon::now()->addDays($plan->trial_days)->getTimestamp(),
],
'success_url' => url('/'), // todo
'cancel_url' => url('/'), // todo
], Cashier::stripeOptions());
}
} @cihantas Hope that helps. |
@faustbrian I tried implementing your code, and it works as expected. Except for when stripe sends the "customer.subscription.updated" webhook before the "customer.subscription.created" webhook. The subscription gets created in the database, but the stripe_status column gets stuck at incomplete. Do you have a fix for this? Thanks. |
@faustbrian I'm in the process of implementing this but I'm missing |
@larslommen I have since then switched to paddle so would have to take a look but looking at my Stripe webhook dashboard it sends the right data. @tonjohn |
@tonjohn I've also used @faustbrian method and it's working so far. Maybe it's my stripe settings or something, but I'm always getting NULL from the Also needed to add Would I be correct in guessing that Cashier has largely ignored a lot of the new Checkout stuff until things seem to stabilize? Ive spun up a few apps with Stripe and Cashier over the last few years and each time I go to implement it its almost completely different lol |
@tylerwiegand if you do anything Checkout related with Cashier you are on your own with the Stripe SDK like in my example. When it gets integrated into a Cashier is a question mark as it doesn't seem to be high priority which isn't that big of an issue since the implementation with the SDK is still fairly simple and quick. |
Like I said on this pr a couple of times already: anyone is welcome to continue to work on a PR for this. |
I've picked up work on this in a new PR here: #1007 All concerns from listed on the original comment from this PR have been resolved ever since. |
This PR adds functionality for Stripe checkout. At the moment it'll initiate a new server side session so we can add the checkout to an existing user. Unfortunately I just discovered that this isn't implemented by Stripe yet: https://stripe.com/docs/payments/checkout/server#span-classstepoptionalspan-using-existing-customers
We'll have to wait until Stripe implements this before continuing with subscriptions. We can already see at implementing checkout for single charges though.
Todo
Concerns
Below I'll try to list all concerns I encounter during development.
Existing customers can't yet be passed along when subscribing to a plan ✅
This is vital for Cashier to work so until this gets implemented on Stripe's end this PR is basically blocked.
https://stripe.com/docs/payments/checkout/migration#client-subscriptions
Update 02/10/20: Stripe has since provided support for this.
Trial days seem to work differently for Checkout sessions ✅
Current api for subscriptions: https://stripe.com/docs/api/subscriptions/create#create_subscription-trial_end
Api when creating Checkout sessions: https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-subscription_data-trial_end
This basically means that you're always required to give either a 48h period or 1 day period when subscribing through Checkout. This seems unwanted and weird. I'll check this over with Stripe.
Update 30/04: Stripe confirmed that the 48h limitation is in place for the following reason:
I've implemented this here: ff9946d
Unclear how to upgrade or downgrade using Checkout ✅
The migration guide covers creating new subscriptions but no mention is made on how to upgrade or downgrade plans. We'll need this before support for plan swapping can be given.
Update 29/04: Stripe has confirmed that this isn't included yet.
Update 02/10/20: Stripe has since released their new customer portal that takes care of swapping plans.
Unclear how one-off invoices work ✅
At the moment it's unclear how the
invoice
method on the billable entity would work with Checkout (or payment intents). I've asked this question to Stripe on Twitter and awaiting their feedback.Update 02/10/20: I'm not sure why I brought this up back when I was working on Stripe Checkout support. This isn't needed at all for Checkout.