#nikitatint_elements-3ds
1 messages ยท Page 1 of 1 (latest)
๐ Welcome to your new thread!
โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
๐ This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1238530917727993947
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
The full, more verbose explanation of our implementation.
nikitatint_elements-3ds
sorry that's a lot of words ๐
Why are you doing SetupIntents and then immediately after an off session PaymentIntents? That's highly discouraged
okay I read your screenshot and yeah this is a really bad flow overall and can lead to declines based on a bank's preference
@keen sphinx are you around?
(sorry the server was really busy for a bit but we're caught up now)
I am!
All good, we have a complicated scenario where we need to conditionally capture payments vs just setup payments upon payment form submission. That combined with some requirements to track payment statuses in our own DBs required us to go with this flow.
There are likely alternative ways to approach this that would avoid this issue. It's really a bad idea to do SetupIntent then PaymentIntent. You lose all possibilities of liability shift with 3DS for example since it wasn't part of your payment
Also we are using setupIntents to be able to charge recurringly
That's not really needed. A PaymentIntent lets you do the exact same thing: https://docs.stripe.com/payments/save-during-payment
Right, but we cannot always confirm a payment intent right away, and to save the payment method provided by the customer from the StripeElements form we need to either confirm a setupIntent or confirm a paymentIntent. That goes back to our conditional of sometimes we need to capture payment on payment form submission and sometimes we dont
That's fair but there likely are better approaches than what you're doing. But I guess let's ignore this for now.
So what's your real question? You do a PaymentIntent, sometimes you need to do 3DS, so you email your customer to come back to your app/website to pay that PaymentIntent through 3DS. Does that make sense?
sometimes you need to do 3DS, so you email your customer to come back to your app/website to pay that PaymentIntent through 3DS.
This is the part that's not clear, is there a way to handle this where they can get a challenge link without having to come back to our app? Or otherwise authorize in a way that will let us attempt confirmation again on the server side?
No that's not possible
At the setupIntent confirmation ideally
Ahh ok, its weird to me that the StripeElements handles the 3DS with the client facing modal but then we need additional auth. Is this what you were talking about with the liability shift?
Yeah you're building this the wrong way (sorry to keep beating on that drum). There's no way for us to know you're going to immediately charge the customer off session after a SetupIntent (since it's not a good flow).
There's never a guarantee that doing 3DS now opts you out of it for the next payment. Especially if the next payment is seconds later and "off session" when your customer was on session seconds before.
So ultimately: you create the PaymentIntent server-side, you make sure to pass both off_session: true and confirm: true to attempt the payment but if the bank declines or asks for 3DS you have to get the customer back on your app to pay
That would work great for 1 of our cases, but the other one we cannot confirm the payment intent right away, we have to wait until the beginning of the next month to charge that payment method.
I'm assuming you meant to say that before we display the payment form to the user, so that we can use the client secret generated by that Payment Intent to display the StripeElements form?
Because if its after payment form submission, we also have business logic that requires us to handle the payment_intent created event and confirm separately.
the payment_intent.created Event isn't really useful. Your own code creates that PaymentIntent. You get a synchronous response that the creation worked or not.
At a high level
- If you collect my card now with a SetupIntent, do 3DS, save my card and in a month you charge me off session with a PaymentIntent => totally normal flow, nothing bad here.
- If you collect my card now with a SetupIntent, do 3DS, save my card and seconds later have some code that will charge me off session with a PaymentIntent => abnormal flow, usually discouraged, best to not do that if you can avoid it
Now I get that sometimes you might not know whether to charge me $100 or $200 depending on some quote or something. So you use that second flow, make some calculations, decide to charge me $200 off session and now my bank says "nope sorry I need 3DS again" then that's normal. It's not common but it definitely happens and in that case you get the customer back on your app to pay. It's the same as if they had not enough funds or the card expired.
Understood, that makes sense from the bank perspective, so I can't disagree there.
It all boils down to if there was a way to collect the payment method and attach it to the Customer through the Stripe Elements form without being required to confirm the payment immediately, that would be our preferred route. But from everything we have tried, we can't do that unless we use a setupIntent, which is where we run into that exact issue you mentioned about creating a paymentIntent seconds later.
Yeah what you are asking for doesn't make sense unfortunately in terms of a state machine. SetupIntents are for collecting payment method details without a payment. So really the problem is how you delay the payment a few minutes and really should aim to not do that.
cc @low lion sorry I now have to run so my colleague will take over:
Summary: They use SI to collect card details upfront and then charge off session near immediately after because they need some logic internally to decide how much to charge. Sometimes the bank wants 3DS on that PI (which is expected) and they asked how to handle this (which I explained)
To clarify, when initially implementing this, we tried to just use PaymentIntent, but the problem of the Payment Method not being attached to the Customer arose. That's when we realized you need to confirm the PaymentIntent to attach that payment method to the customer, but that wasn't allowed in 1 of our flows.
but the problem of the Payment Method not being attached to the Customer arose
You can absolutely attach the Payment Method to the Customer with just a Payment Method. We document that here: https://docs.stripe.com/payments/save-during-payment
you need to confirm the PaymentIntent to attach that payment method to the customer
This is where our problems began, and led to the usage of SetupIntent instead
Sorry but that doesn't make any sense to me. Can you explain why that is a problem?
That would work great for 1 of our cases, but the other one we cannot confirm the payment intent right away, we have to wait until the beginning of the next month to charge that payment method.
Quoting myself from the conversation with your colleague earlier in this thread. I also attached a screenshot at the very beginning with our full implementation summary.
Okay so you have some flows where you just need to save the PM. And you want a universal approach (keep the code base as simple as possible). Would you say that is accurate?
Correct!
Okay, glad we are on the same page. So you are saving the PMs but sometimes when you attempt to charge them you wind up in a status of requires_next_action?
With the test card 4000002760003184 yes
We want to minimize issues with 3DS challenges
So the previous suggestion to not use SetupIntent and immediately create PaymentIntent I understand would've helped us here but as capturing a PaymentIntent is required to save the Payment Method, it is not an option for us by itself.
My original intuition was that since we are using a SetupIntent client secret to render the Stripe Elements form, any 3DS challenge that is passed (leading to submission of the form) would transfer over from the SetupIntent to the Payment Intent and we wouldn't have to reauth in most cases
But that doesn't seem to be the case, and I want to weigh all my options to be able to get a saved Payment Method charged without having the customer come back to our app.
That test card will always require 3DS on every single transaction, even when set up for off-session. use
But that isn't necessarily how customer cards will behave
Using the card 4000002500003155 would simulate a card that will trigger 3DS when set up but then not when used with a Payment Intent (if you set it up properly)
Most of the time the Setup Intent will handle your 3DS auth. Then, when you create the Payment Intent, you would pass off_session: true as well as confirm: true, as my colleague recommended
We do pass off_session: true to the PaymentIntent, but there is business logic we need to do in the payment_intent.created event handler on our stripe endpoint that requires us to confirm it there instead of immediately upon paymentIntents.create as suggested.
then not when used with a Payment Intent (if you set it up properly)
Setting it up properly requires both of those aforementioned parameters?
No there I meant that, if you create a Payment Method with the card 4000002500003155 using a Setup Intent, we will trigger 3DS for the Setup Intent confirmation but not for later Payment Intents
The point of that test card is to simulate the most common experiences with 3DS
Where a card requires an initial 3DS auth but then can be used off-session without further authentication
Understood, and the triggered 3DS for the Setup Intent will propogate itself on the Stripe Elements form that we display to the customer?
Yes, if you are confirming the Setup Intent on the client-side
As for not wanting to confirm the payment intent immediately, can you describe the logc you are using there?
I just tried that card but it seems that the payment intent still needs a challenge
We are just creating the payment intent so that we can wait for the payment_intent.created event to be triggered so that we can make some updates in our DB and track the progression of payments more easily. After making those updates we confirm the payment_intent which triggeres either the failed or success webhook where we have additional business logic to either retry or trigger other actions.
And we are confirming the setup intent on the client-side
I can see them in the screenshot but I cannot inspect. Can you copy and paste both intent IDs here?
pi_3PEyDYJmR00JblGD17AljAod
seti_1PEy7nJmR00JblGDbflEjjaf
You passed setup_future_usage: 'off_session' on the Payment Intent. This effectively tells us to trigger 3DS because you are setting up the payment method for future off-session usage
This is entirely different from passing off_session: "true"
which you did not pass
I see, let me try that now.
Ahh but that won't work because we are not confirming the payment right away, just creating it. An error is thrown:
The parameter
off_sessioncannot be passed when creating a PaymentIntent unlessconfirmis set to true.
Pass it when confirming but defintely do not pass setup_future_usage when creating the Intent
Understood, trying that now
I'm stepping away but my colleague @wide kindle will be able to help