Skip to content
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

Create Stripe subscription without a payment method #1522

Closed
grazianodev opened this issue Apr 5, 2023 · 11 comments
Closed

Create Stripe subscription without a payment method #1522

grazianodev opened this issue Apr 5, 2023 · 11 comments

Comments

@grazianodev
Copy link

grazianodev commented Apr 5, 2023

I'm trying to create a Stripe subscription integration by following this Stripe guide, which I can see has been referenced in this pull request.

From what I understand, the guide basically instructs to create a customer, then create a subscription before even displaying the form to collect payment information, with the payment_behavior option set to default_incomplete (which sets the subscription's status to incomplete), and the save_default_payment_method option set to on_subscription (which means the payment method will be saved once the payment is confirmed and the subscription activated); then, when the user submits his payment information, the confirmPayment method offered by Stripe's JS SDK is used on the frontend to confirm the payment (which sets the subscription's status to active).

In other words, you create the subscription on the server as incomplete without a payment method; then confirming the payment + activating the subscription + saving the payment method is all performed on the frontend using confirmPayment.

So, I tried to create a subscription using Cashier without providing a payment method, like so:

$request->user()->newSubscription(
   'default', 'price_XXXX'
)->create( null, [], [
   'payment_behavior' => 'default_incomplete',
   'payment_settings' => ['save_default_payment_method' => 'on_subscription'],
   'expand' => ['latest_invoice.payment_intent'],      
]);    

And I can see that the subscription is actually saved to the database as incomplete, however I never get back the subscription object because an IncompletePayment exception is thrown since no payment method was provided.

So my question is, is there a way to create a subscription without providing a payment method?

@driesvints
Copy link
Member

Heya. No this isn't possible with Cashier at the time but it's simply not needed. Cashier internally handles the confirmation for you. You can simply call the newSubscrition and create method and it'll:

  1. Create the customer for you
  2. Create the subscription
  3. Confirms the payment

If there's an error with confirming the payment like a 3D secure check then Cashier will throw an IncompletePayment exception which you can handle like: https://laravel.com/docs/10.x/billing#handling-failed-payments

Hope that helps?

@grazianodev
Copy link
Author

Hi,

yeah I know newSubscription + create does the work for you, which is great; but you have to have collected a payment method before that, right? So what I'm doing currently is (for a decoupled app, Nextjs frontend):

  1. Create a setup intent on the server (using Cashier) as soon as the page loads
  2. When the client secret is returned, display the Payment Element on the frontend and collect the payment info
  3. When the user submits the form, confirm the setup intent on the frontend (using Stripe's JS SDK)
  4. Once the intent is confirmed, create the subscription on the server (using Cashier) via newSubscription + create. At this step, you get the payment method from the confirmed intent and pass it along to the server

What I'd like to do (to follow Stripe's guide) is:

  1. Create an incomplete subscription on the server (using Cashier) as soon as the page loads, without a payment method
  2. Once the subscription was created, display the Payment Element on the frontend and collect the payment info
  3. When the user submits the form, confirm the payment + activate the subscription + save the payment method on the frontend (using Stripe's JS SDK)

This would be easier, as you would have to only do one thing on the server. I was just wondering if this is currently possible with Cashier, otherwise it would be nice to have it in the future :)

@driesvints
Copy link
Member

Heya, thank you for your explanation. This was actually possible before but since we introduced the handling of automatic payment confirmation it's no longer possible. I've sent in a PR to ignore incomplete payments when creating new subscriptions so you can get back the subscription and do the payment handling on the front end side: #1524

@grazianodev
Copy link
Author

Great, thanks!

@grazianodev
Copy link
Author

Hello again,

so I am working on this again after ignoreIncompletePayments was added, and I can now create a subscription without a payment method (thanks again!). One small problem now is that, in order to even display the Payment element on the frontend, I need the intent's client secret, and unfortunately expanding the invoice when creating the subscription is not working for me:

$subscription = $user->newSubscription(
   $request->planSlug, $request->planPrice
)->ignoreIncompletePayments()->create( '', [], [
   'payment_behavior' => 'default_incomplete',
   'payment_settings' => [ 'save_default_payment_method' => 'on_subscription' ], 
   'expand' => [ 'latest_invoice.payment_intent' ],      
]);      

// The resulting $subscription does not have a 'latest_invoice' at all! 

I'm working around this by calling latestPayment() on $subscription and getting the client secret from there, but I was wondering if expanding is not supported or if I'm doing something wrong.

@driesvints
Copy link
Member

@grazianodev so the reason for this is because you're getting back a Cashier subscription, not a Stripe subscription. You can't use expand on that param. You'll always need to call latestPayment unfortunately.

@grazianodev
Copy link
Author

Okay, thanks for the explanation.

@LucasLaurens
Copy link

I have a question about this. Indeed, it allows you to follow Stripe's recommendation in the integration for a subscription. Once the payment has been made, everything is OK on the Stripe Dashboard, but the subscription is not activated in the base because it has not been processed. Am I missing something, or is Cashier taking the decision of not managing the subscription when the payment is successful from this particular case ?

@LucasLaurens
Copy link

I have a question about this. Indeed, it allows you to follow Stripe's recommendation in the integration for a subscription. Once the payment has been made, everything is OK on the Stripe Dashboard, but the subscription is not activated in the base because it has not been processed. Am I missing something, or is Cashier taking the decision of not managing the subscription when the payment is successful from this particular case ?

Or maybe manage the subscription status from the webhook event to the DB

@driesvints
Copy link
Member

@LucasLaurens yes the webhook will handle all state change to the subscription. You always need at all times a working webhook.

@LucasLaurens
Copy link

LucasLaurens commented Sep 28, 2023

@LucasLaurens yes the webhook will handle all state change to the subscription. You always need at all times a working webhook.

Okay, thanks for your answer. I'm going to use the subscription payment method : on_subscription. Then, once the user has made a payment attempt, I'll use the webhook to check the change in subscription status and save the new status in the database

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants