#battbot-3ds-returnurl
1 messages · Page 1 of 1 (latest)
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
the successful confirmation happened at 2022-07-20 23:07:43.180 though
i know
so if you do a GET before that, it wouldn't be successful
and you did the GET before that
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
Yeah your browser js client, which our client uses
what does "your browser js client"?
the stripe.js
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?
Our browser based webapp uses stripe.js to do the GETS
gimme a sec, let me look up the call
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)
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
sure those are the 2 I mentioned
they happened before the completion
yeah, that's the problem, the redirect came back to us before the completion
it was a frictionless 3DS transaction
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
are you sure the redirect happened after?
from our logs it seems the redirect happened before
Can you explain in details what you mean, what you see, what code you call, what system you use, etc.
ok, the flow is like this:
- Our web app running on the phone says user wants to pay for an order.
- The web app (our client) make a call to our server
- Our server creates the payment intent, sees that 3ds is involved, responds to our client the stripe redirect url
- Our client redirects to that url
- Stripe does its thing, redirects the browser to the return url
So you don't use Stripe.js at all for 3DS, you do a full page redirect yourself or display an iframe or something?
- 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
So there's no real redirect if you show this in an iframe yourself?
but we use Stripe.js to get the payment intent after the return url comes back
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?
the 3DS dialog goes to the redirect url your payment_intent specified
it basically opens up an iframe and sets the url to the redirect_url in the payment_intent, in this example it was https://hooks.stripe.com/3d_secure_2/hosted?merchant=acct_1KTx6ER4JY1vIb1k&payment_intent=pi_3LNlzNR4JY1vIb1k0rMeinpP&payment_intent_client_secret=pi_3LNlzNR4JY1vIb1k0rMeinpP_secret_oj4dw8NnG4aSx26C6zehC5nD3&publishable_key=pk_live_ppArwjpEtPJBVq0xPuwlC3ro&source=src_1LNlzOR4JY1vIb1kVO1ANhDb&stripe_account=acct_1KTx6ER4JY1vIb1k
so when that iframe comes back to the return_url we specified , in this case it was "https://gosnappy.io/owa/snappy/detail/G2510/2510/3ds_success_handler" our code runs to check on the payment intent
yeah makes sense
normally when we check on the payment intent, the payment intent status is "succeeded" but some times it shows as "requires_source_action"
I do see "something" that seems to imply a redirect happened at 35, right before your GET
trying to make sense of the logs
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
I see
can you give me a recent one where it did not happen?
I wonder if it's something with 3DS2 specifically
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
k
pi_3LNlzcR4JY1vIb1k1kSH6aYM
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?
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
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
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
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?
before I get into that, can you give me the second PI where it happened?
you said pi_3LNlzNR4JY1vIb1k0rMeinpP both times I think
the error case, you said pi_3LNlzNR4JY1vIb1k0rMeinpP here
but that's already the one I was looking at before
yeah the error case is
pi_3LNlzNR4JY1vIb1k0rMeinpP
oh
sec
pi_3LMx5NR4JY1vIb1k1EVqjuHU
it happened a couple of days ago
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
Hi @grave surge I'll take over this thread, let me know if you have any follow-up question
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
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.
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
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
In client-side you should call stripe.confirmPayment, so that Stripe.js will use the payment details from the PaymentElements and create a PaymentMethod for this PaymentIntent https://stripe.com/docs/payments/accept-a-payment?platform=web&ui=elements#web-collect-payment-details
that's not really how it works @zinc elm
we already have the payment method on the server though..
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
https://stripe.com/docs/js/payment_intents/confirm_card_payment except that you don't pass card: cardElement, since there's already one attached on the PaymentIntent
I have to run but make sure to email in with all the details so that someone can follow up
great, thanks