#nikitatint_elements-3ds

1 messages ยท Page 1 of 1 (latest)

sterile berryBOT
#

๐Ÿ‘‹ 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.

keen sphinx
#

The full, more verbose explanation of our implementation.

real saffronBOT
tulip oracle
#

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)

keen sphinx
#

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.

tulip oracle
#

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

keen sphinx
#

Also we are using setupIntents to be able to charge recurringly

#

liability shift?

tulip oracle
keen sphinx
#

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

tulip oracle
#

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?

keen sphinx
#

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?

tulip oracle
#

No that's not possible

keen sphinx
#

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?

tulip oracle
#

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

keen sphinx
#

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.

tulip oracle
#

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.

keen sphinx
#

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.

tulip oracle
#

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)

keen sphinx
#

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.

low lion
keen sphinx
#

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

low lion
#

Sorry but that doesn't make any sense to me. Can you explain why that is a problem?

keen sphinx
#

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.

low lion
#

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?

keen sphinx
#

Correct!

low lion
#

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?

keen sphinx
#

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.

low lion
#

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

keen sphinx
#

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?

low lion
#

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

keen sphinx
#

Understood, and the triggered 3DS for the Setup Intent will propogate itself on the Stripe Elements form that we display to the customer?

low lion
#

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?

keen sphinx
#

I just tried that card but it seems that the payment intent still needs a challenge

keen sphinx
# low lion As for not wanting to confirm the payment intent immediately, can you describe t...

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.

keen sphinx
low lion
#

I can see them in the screenshot but I cannot inspect. Can you copy and paste both intent IDs here?

keen sphinx
#

pi_3PEyDYJmR00JblGD17AljAod
seti_1PEy7nJmR00JblGDbflEjjaf

low lion
#

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

sterile berryBOT
keen sphinx
#

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_session cannot be passed when creating a PaymentIntent unless confirm is set to true.

low lion
#

Pass it when confirming but defintely do not pass setup_future_usage when creating the Intent

keen sphinx
#

Understood, trying that now

low lion
#

I'm stepping away but my colleague @wide kindle will be able to help