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
Stripe throws incomplete payment after first subscription with SCA #743
Comments
Normally after you've created the subscription it's already in your DB and in Stripe and the payment should be "incomplete" until you've confirmed 3D Secure. Couple of things:
|
The first 3D Secure check is because of the setup intent. You're verifying the card for later recurring use so you don't need to do the 3D Secure check when your subscription renews. But it is weird that you're prompted again. In theory this could happen when your card is declined. If your submit the payment conformation screen is the subscription then marked as active? Can you maybe also check the payment details there from your last screenshot. There should be a "A new payment pi_xxxx for xxx was created" event at the bottom. I'm especially looking to see what the value of the "status" column is. |
Yes if I submit the payment in the confirmation screen the payment succeeds and the subscription becomes active. Here are the events (sorry it's written in french)
We can see that the payment failed the first time while the 3D secure step was successful. |
Can you open the |
Sure. The status is Here is the full event: {
"object": {
"id": "pi_1F6ygiLwhwXHyztNzlfaKx2Y",
"object": "payment_intent",
"amount": 20280,
"amount_capturable": 0,
"amount_received": 0,
"application": null,
"application_fee_amount": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/charges?payment_intent=pi_1F6ygiLwhwXHyztNzlfaKx2Y"
},
"client_secret": "pi_1F6ygiLwhwXHyztNzlfaKx2Y_secret_Tg0mDa1wOZmqRHA8rVy8tN8dZ",
"confirmation_method": "automatic",
"created": 1565697676,
"currency": "eur",
"customer": "cus_FcD60jlH4ByrrM",
"description": "Payment for invoice 098EAC47-0001",
"invoice": "in_1F6ygiLwhwXHyztNFyuSbWhd",
"last_payment_error": null,
"livemode": false,
"metadata": {
},
"next_action": null,
"on_behalf_of": null,
"payment_method": null,
"payment_method_options": {
"card": {
"request_three_d_secure": "automatic"
}
},
"payment_method_types": [
"card"
],
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "requires_payment_method",
"transfer_data": null,
"transfer_group": null
}
} |
Thanks. That's really odd. You're sure What happens behind the scenes is that this card is used as your default card. If you've previously already set a default card with the setup intent you shouldn't need to pass this. If this if your first setup then you'll need to pass it. There's a default payment method parameter when creating subscriptions which says the following:
So your customer's default payment method should be used (which was set right before the creation). |
I just dumped I'm using this code in a signup form so the user actually doesn't even exist before creating the subscription. When I submit the form, I create the user then their subscriptions so I'm sure they can't have a default payment method already. |
@marcbelletre The default payment method is definitely set because you're passing the payment method is to the |
I don't understand. I meant that there couldn't be a default payment method before the user registers because they didn't exist. What I'm doing is setting up the payment method with the JS API. I get a payment method ID which I append to the form and then I submit the form. Then, my controller creates the user and the subscription using the payment method ID. I can see in Stripe that my customer has a default payment method but the first payment is declined and needs additional action. I just tried creating the Stripe customer and attaching the payment method before creating the subscription with the same result. // Create user and attach payment method
$user->createAsStripeCustomer();
$user->updateDefaultPaymentMethod($request->input('stripePaymentMethod'));
// Create subscription
$subscription = $user->newSubscription($planType, $planId);
$subscription->create(null, [
'email' => $user->email
]); |
The subscription builder calls the method below right before creating the subscription and will create a customer in stripe and set the payment method you provided as the default payment method: |
Yes I saw that already, just wanted to double check. I still can't figure out why the first payment gets declined. Correct me if I'm wrong but it looks like when Stripe creates the By the way, the test card I'm using is
|
Ah, but you're not off-session, remember? You're on-session here. So that might be it. If you use a testing card then it's probably normal that it triggers 3D Secure because it's built in to trigger it. Remember that 3D Secure can still happen by an off-chance in every single situation when you perform a payment. It's rare when you use the setup intent but it can still happen. |
I know I'm not off-session but I'm worried about the fact that users could have to provide their payment details and go through the 3D secure process twice in live mode right after subscribing. While I agree that this could happen at any time for the next payments, this scenario should never happen right after the first payment because we already validated it with 3D secure. If you have a look at this scenario described in the Stripe guide, which is similar to the one I'm trying to build, the first payment is always authorized after a single 3D secure check. Also the testing card I'm using is the one that should trigger 3D secure for the first payment only. I tried with The second payment form works because it has a payment intent ID and uses the As per the Stripe documentation, SetupIntents are used to "set up your customer's card without immediately collecting a payment". In my case and I guess I am not the only one, we also need to immediately collect the first payment without asking the user to fill the credit card input twice. |
I really think this is because of the testing cards and that this won't happen in a live environment but until someone actually tests this out in a live environment we won't know for sure.
No this is handled for you when creating a new subscription with the Stripe API. We need the setup intent to make sure you don't get asked for 3D Secure confirmations every time on your recurring billing. I really don't think it's necessary to change anything on the current flow. We'll need confirmation of this happening on a live environment or someone from Stripe confirming before we make changes. |
I'm battling the same issue. From reading the Stripe documentation on this page they specifically state:
Whereas the Setup Intents:
This suggests that depending on how Cashier is being used depends on which Intent fits. SetupIntent makes sense for trial accounts which will charge at a future date whereas PaymentIntents work for the scenario where the user is charged their first payment as soon as card details are given which sounds like the scenario @marcbelletre and I are trying to implement. |
I'm seeing the same behaviour that @albofish is pointing out. Works for users that create a subscription during trial (with keeping their trial and being charged later) and has to be authorized two times when creating a subscription without a trial period. We're not yet on production though. |
Have any of you actually tested this on a real production app yet? Because I still believe you won't get the second action requirement when you try to subscribe with a card that was set up using the setup intent token. |
Btw, nothing is stopping you from using a different flow. You could use the different flow there but if you don't use the setup intent token then I suspect you'll run into issue later when you customer is prompted for secondary payment actions when recurring billing occurs. |
I can reproduce the exact same issue. It looks like the root cause of it is using
While on the other hand
So what I did is instead of creating a |
@TitasGailius you'll run into problems when you swap plans I think. Then again it should throw the incomplete payment exception in that case. I'll try to investigate this a bit more soon. |
I'm facing the same problem. I'm using the Payment Intents API to charge a card immediately and saving the details for future payments, but I have to insert credit card data twice. Thank you in advantage for any further info. :) |
Hi! I have the same issue. When the user has no trial period I create a PaymentIntent, then I go throw the handleCardPayment on js that prompts the 3d secure, but when I create a new subscription the IncompletePayment exception is thrown and the user must insert the credit card data again. |
Guys just to help you out. Current implementation takes elements as a second argument. Unless there's no payment method attached to payment intent this is not correct. Again if there's a payment method call no-element handleCardPayment, reference https://stripe.com/docs/stripe-js/reference#stripe-handle-card-payment-no-element |
I have exactly the same issues as you @marcbelletre. |
Same here. After providing card details and on submission handleCardSetup is prompting for 3DS.
Then form gets submitted. New subscription is created using setupIntent.payment_method, it throws IncompletePayment $exception and redirects to 'cashier.payment' asking for name, card and 3DS once again.
All webhooks are properly set up and working, $user->createSetupIntent(); passed to view with payment form to get client_secret. Battling with this since few days. Would be grateful for help. As @driesvints asked - did anyone tried it on a live environment? |
As I said before: note that you're all testing with a card number that's specifically meant to always throw the 3D Secure error. That's why I'm asking to actually try this on a live environment. |
@driesvints Actually Stripe provides these two credit cards specifically to test 2 different behaviours:
Unfortunately both cards have the same behaviour! |
@losa90 aha, that's good to know. Thanks for verifying that. I'm a bit out of time today but I'll allocate some time to look into this tomorrow. |
Hello, I've tried to implement the new Cashier version, and I have the same issue as @marcbelletre, did you find a way to be sure that users in live version won't have to fill the card informations twice @driesvints ? Thanks! |
No not yet. Sorry people, I'm a bit swamped with Laracon and all this week. If anyone can help figuring a better way for this code or docs wise than that's much appreciated. |
I have the same issue. |
For what it might be worth. I have this occurring when a trial period of a product ends. Setting up the payment Intent then using stripe.js v3 (elements) with card 4000002500003155 (ask on first, but not after) The subscription creates just fine, but when the Trial period comes to an end, it then triggers 3DS - because it is off-session. an email should be sent out by stripe (if configured to do so in the Stripe Dasboard) -- The email does not get sent Further to this, Ending the trial via the Stripe Dashboard doesn't work. The Stripe dasbhoard returning 'subscription_payment_intent_requires_action' when talking to it's own API - I have opened a ticket with Stripe about this. If I attempt this with a product without a trial on it, it creates the subscription but fails to take payment ('subscription_payment_intent_requires_action' as as everyone else is experiencing, and the stripe_status is 'incomplete') So I'm not sure how much of this is Cashiers fault... It's almost as if the 3DS trigged by |
It feels like creating a I might be completely wrong because it's just a random guess based on intuition 😅 |
FYI today stripe is going to have a livestream on youtube about saving and reusing cards with SCA. The livestream starts at 3pm GMT. |
Just to clear up any possible confusion created by @TitasGailius 's comment about the differences between
For a SetupIntent, Stripe automatically handles the creation of a mandate to allow future processing, (although you should update your legals and terms). They also automatically apply for exemptions for future SCA checks, however that doesn't mean they will be granted. Stripe automatically handles the initial SCA if you use For off-session SCA you either need to listen to the webhooks and get the user back on session to get them to complete the SCA check, or configure Stripe via your Stripe Dashboard to send your Customer an email on your behalf and Stripe will complete the SCA check. At least that is how it should work, The SetupIntent work flow doesn't seem to be working for those in this thread (myself included). The initial SCA check is still occurs and works, but then the attempt to take payment triggers SCA again. -- this seems to be a Stripe issue. but could be an issue with how the Intent is setup via Cashier.. ? |
You can definitely save payment methods with both. The difference is just the flow. With |
After the "Confirm Authorize Screen" am getting the error
Function used to trigger subscription is below
It's getting created as an Incomplete subscription. JS side
The card is "4000002500003155" - Soecificaly 3D secure required and it's asking for confirmation |
From the Developer Office Hours: Saving and Reusing Cards (SCA) Stripe YouTube Live Stream: David Gray -- Is it to be expected that when using the 3155 card for a subscription that on first payment it asks for 3DS auth again? even when it was done on the initial handleCardSetup call? Matus F Marc Bellêtre Karl Reid [Stripe] |
@SlyDave I just saw this as well. I think we have to add |
Yeah, Agreed, it's a bit odd. if we used PaymentIntent I'd expect that to be on-session by default, but if we use SetupIntent I would expect that to be off-session by default. and as we're extremely likely to use SetupIntent with a subscription (as Cashier expects)... alas Stripe defaults everything to on-session, so Cashier will need to tell it to use off-session as per your suggestion! :) |
Okay guys I just tested setting |
I can confirm what @marcbelletre says -- adding |
Should also note that with the change applied and Stripe configured to send out emails for off-session authentication, Stripe actually does send the email now, and their hosted work flow for authing the payment works as expected. -- which it wasn't doing before (so one assumes they only do this if the PaymentIntent they auto generate is generated from a SetupIntent is configured as off-session to begin with) |
OMG! A needle in a haystack would be easier to find. |
Tested it as well, it works. Great job figuring this out! |
Add 'off_session' => true, into subscription buildPayload when using stripe.handleCardSetup() |
Actually isnt that a stripe api bug? |
The setup intent was flagged as off session and was handled as off session. The subscription wasn't flagged as off session and so wasn't handled as off session. They're two independent calls to the stripe api. As counter intuitive as that might seem. |
Hey everyone. As so many people reported the issue as fixed because of the change I'm closing this. Thanks to everyone helped figuring this out. I'll be tagging a new release in a few moments. |
I'm working on updating my project with Cashier
v10.0.0-beta.2
and the new Payment Intents API for a few days now and I'm still having issues with my customers having to provide their card details and 3D secure twice. I can't say if I did something wrong or if this is a Cashier related issue so I'm asking here.I updated my integration following these instruction.
My new customers have to fill a form with their personal information and select one or many subscription plans. At the end of the form there is a Stripe card element. Before the user submits the form, I use Stripe's
handleCardSetup
method to verify the payment and then submit the form to my controller which creates the subscription(s).The form contains a SetupIntent key which is generated by the controller before returning the view.
This is my jQuery code for handling Stripe API.
I get the Stripe's 3D secure popin everytime I submit the form, but when I confirm the payment using the Complete authentication button, the SubscriptionBuilder's
create()
method throws anIncompletePayment
exception and I have to provide all the card details again. This seems wrong as I already verified the payment with 3D secure.Here is the code which is handling the new subscription in my controller:
The text was updated successfully, but these errors were encountered: