#battbot-3ds-returnurl

1 messages · Page 1 of 1 (latest)

small flame
#

hello! Can you give more specific details for me to help debug , like the PI id?

grave surge
#

Hi yes, pi_3LNlzNR4JY1vIb1k0rMeinpP

#

that's the one, it was created at 11:07:33. We called GET on it twice at 11:07:36 and 11:07:39, in the stripe logs it doesn't show what stripe returned as the response, but our own logs showed that the status was still requires_source_action

small flame
#

the successful confirmation happened at 2022-07-20 23:07:43.180 though

grave surge
#

i know

small flame
#

so if you do a GET before that, it wouldn't be successful

#

and you did the GET before that

grave surge
#

so the 3ds redirect came back to our client, and our client gets the payment intent

#

Did you see the return_url on the PI? you guys called back to our return_url, so our client did the GET

small flame
#

so our client did the GET

#

but you didn't

#

if you did the GET I'd see a log for it

grave surge
#

Yeah your browser js client, which our client uses

small flame
#

what does "your browser js client"?

grave surge
#

the stripe.js

small flame
#

Sorry but can you be a lot more specific? Which exact part of our product do you use? PaymentElement? Card Element? Something else? Did you call confirmPayment or confirmCardPayment?

grave surge
#

Our browser based webapp uses stripe.js to do the GETS

#

gimme a sec, let me look up the call

small flame
#

If you can share how you do the GET too in code that will help

#

but I can tell you for sure that you never did a GET after the completion (or rather you did one one hour later, that's all)

grave surge
#

stripe.retrievePaymentIntent(payment_intent_client_secret || intentSecret).then(function(result) {
if (result.error) {

#

it's vue.js

#

not sure if it's understandable

#

The stripe logs shows we did:
req_ZcAJlph9ilQ9bo

#

and req_HVotht7sSPEAG7

small flame
#

sure those are the 2 I mentioned

grave surge
small flame
#

they happened before the completion

grave surge
#

yeah, that's the problem, the redirect came back to us before the completion

#

it was a frictionless 3DS transaction

small flame
#

creation: 2022-07-20 23:07:33
GET1: 2022-07-20 23:07:36
GET2: 2022-07-20 23:07:39
Completion (which happens before our redirect, always): 2022-07-20 23:07:41

grave surge
#

are you sure the redirect happened after?

#

from our logs it seems the redirect happened before

small flame
#

Can you explain in details what you mean, what you see, what code you call, what system you use, etc.

grave surge
#

ok, the flow is like this:

  1. Our web app running on the phone says user wants to pay for an order.
  2. The web app (our client) make a call to our server
#
  1. Our server creates the payment intent, sees that 3ds is involved, responds to our client the stripe redirect url
#
  1. Our client redirects to that url
#
  1. Stripe does its thing, redirects the browser to the return url
small flame
#

So you don't use Stripe.js at all for 3DS, you do a full page redirect yourself or display an iframe or something?

grave surge
#
  1. our client on the return url calls the GET on the payment intent to check its status
#

We display an iframe for the redirect to 3DS

small flame
#

So there's no real redirect if you show this in an iframe yourself?

grave surge
#

but we use Stripe.js to get the payment intent after the return url comes back

small flame
#

Sorry this is quite crucial information since you're not using any of our like 3 top ways of integrating that everyone is using

#

even doing a client-side retrieve doesn't make sense to me. We hit your server first, you should retrieve it server-side, where you can trust the response.

#

But basically you say the 3DS modal does the full redirect before it completed at all?

grave surge
#

the 3DS dialog goes to the redirect url your payment_intent specified

small flame
#

yeah makes sense

grave surge
#

normally when we check on the payment intent, the payment intent status is "succeeded" but some times it shows as "requires_source_action"

small flame
#

I do see "something" that seems to imply a redirect happened at 35, right before your GET

#

trying to make sense of the logs

grave surge
#

The same thing happened later on with another payment intent:
pi_3LNlzNR4JY1vIb1k0rMeinpP

#

we get this quite often recently

#

but before it didn't happen as much

small flame
#

I see

#

can you give me a recent one where it did not happen?

#

I wonder if it's something with 3DS2 specifically

grave surge
#

well, for the exact same user, she ended up paying twice

#

because for the first one we told her payment failed

#

so this is the second payment intent she did, which was ok

#

sec, i need a minute to get the PI id

small flame
#

k

grave surge
#

pi_3LNlzcR4JY1vIb1k1kSH6aYM

small flame
#

Everything internally points to 3DS happening after what you say though, and you have a really strange/strongly discouraged integration
Doesn't mean something isn't broken, but it's hard to grasp what is happening
You're 100% confident it's impossible for your code to have "re-shown" the 3DS iframe?

grave surge
#

that same user, same phone, 2nd payment intent was fine

#

we won't re-show the 3ds iframe

#

since even if we showed it, we'd set the url to your redirect-url

#

we only do GET when our return_url is hit

small flame
#

The timelines are so strange. Why does your code do 3 separate GET to the PI itself in a few seconds

#

like the one that succeeded you did a GET then 3DS success then two more GETs

grave surge
#

i don't know why tbh lol

#

when we debug this flow on the client we only hit that breakpoint once

#

but somehow it is called 3 times

small flame
#

that's part of my problem too I worry that you have maybe parts of your code that you don't fully grasp/understand causing race conditions

#

do you know why you built the integration is such a complicated way?

grave surge
#

maybe because we generate the PI on our server

#

what is the recommended way?

small flame
#

before I get into that, can you give me the second PI where it happened?

#

you said pi_3LNlzNR4JY1vIb1k0rMeinpP both times I think

grave surge
#

the normal or the error case?

#

sorry the normal case was pi_3LNlzcR4JY1vIb1k1kSH6aYM

small flame
#

but that's already the one I was looking at before

grave surge
#

yeah the error case is
pi_3LNlzNR4JY1vIb1k0rMeinpP

#

oh

#

sec

#

pi_3LMx5NR4JY1vIb1k1EVqjuHU

#

it happened a couple of days ago

small flame
#

sounds good that helps

#

to answer your question, the "right" approach is to use PaymentElement or Elements in general and call confirmPayment() and collect card details with it, or confirmCardPayment() if you have already collected card details. This lets us show the modal ourselves and either redirect or complete a promise client-side once it succeeds.
Unless you have a real technical reason for using the iframe/redirect URL yourself, I would recommend switching to our preferred flow really

Also I'd recommend
1/ using webhooks to catch success for all PI via payment_intent.succeeded
2/ Always using the same PI if it's not complete to attempt a second confirmation, which avoids a double charge if it was already confirmed successfully

#

I'd recommend that you contact our support team: https://support.stripe.com/contact
Use the contact form, not chat, mention you talk to me (koopajah) on Discord and to raise this internally (I have already done myself too) and then we can get someone to follow up with you for more details

zinc elm
#

Hi @grave surge I'll take over this thread, let me know if you have any follow-up question

grave surge
#

can we still understand why our current approach isn't working properly? we've been going with it for a few years and only recently we started having all these problems

#

we don't use the client-side payment intent creation for some reasons specific to our business, that's why we create the payment intent server-side

#

But i mean that's the reason you guys have the redirect_url and return_url property in the payment_intent in the first place, for us to do it ourselves

#

if we wanted to

zinc elm
#

You can't create paymentIntent from client-side. what koopajah means is that you need to call stripe.confirmPayment at the client-side and let Stripe.js to handle the 3DS properly.

small flame
#

We don't know what's happening, hence why I asked to write in to follow up

#

Like something is definitely strange, just we have not heard anyone raise anything similar which is really surprising and we need to dig deeper. Writing in will ensure we can speak to you directly and update you and collect more information

#

Just in parallel, I offered to switch your integration if you can to minimize the risk of this happening and simply having a more canonical/better integration. You don't have to, it's just a recommendation

grave surge
#

ok, so you mean once we create the payment intent server side, we return the payment intent to our client and have it call paymentIntent.confirm with it?

#

because our server is the one which already calls the paymentIntent.create . What does the paymentIntent.confirm do when reaches the client?

#

possibly, if the payment didn't require 3ds, the payment would've been successful already on the server

zinc elm
small flame
#

that's not really how it works @zinc elm

grave surge
#

we already have the payment method on the server though..

small flame
#

you can't call confirmPayment without using PaymentElement and collecting car details, and they seem to not want to do it this way

#

What you have to do is send the client_secret for the PaymentIntent client-side and call confirmCardPayment() instead as you already have attached the PaymentMethod to the PaymentIntent

#

If you do that, we will handle 3DS for you and show a modal and on completion it will return from the Promise and you can handle next steps

grave surge
#

ok, we can try it out

#

thanks for the information

small flame
#

I have to run but make sure to email in with all the details so that someone can follow up

grave surge
#

great, thanks