#GiGStartr-3ds-testing
1 messages ยท Page 1 of 1 (latest)
My system uses SetupIntents to capture paymentMethods for future use, and charges (later) on the Server. When using card x3184 in SetupIntent, and successfully passing 3DS, sometime later the PM is used for a PaymentIntent on the Server. The response I'm getting (consistently) is "requires_payment_method", but last_payment_error.code of "authentication_required", and next_action: null
Trying for a couple things: expected status: "requires_action", so it could be used with stripe.handleCardAction.
testing is setting up for presenting a list of payment methods (well, list of "visa x3184" etc) to use in replacing the payment_method
so I'm guessing stripe.handleCardAction({secret}) needs the status to be "requires_action" - as is, I cannot get any result other than the call never returns
As-is, I can setup a responsive paymentElement to capture (successfully) a new card, including re-entering the x3184 and completing the 3DS flow. What I can't seem to do is to re-use the saved x3184 payment method, and just complete the 3DS flow for the server-initiated paymentIntent
so: is there a 3DS test card that responds from a server-initiated confirmPaymentIntent with a "requires_action" status?
Do you mind sharing an example of a Payment Intent that is ending up in a state of requires_payment_method instead of required_action
payment_intent.id pi_3KPdSiAJ38Y8gLJy1l3vNoX3 (event ID coming)
well, req:
req_lVzkM0SXSfORb8
(it's the most recent on my account - been trying a fair number of ways)
can try with new card very very easily
Ah, I believe the issue here is you're doing off_session: true , which will transition the Payment Intent to requires_payment_method . If you didn't set off_session: true then it would transition to requires_action
ah..... sounds backward?
entire POINT is to use off_session: true, as my charges are always disconnected from initial setupIntent
(note that other charges saved exactly the same way do succeeed)
I understand it's a bit confusing - I believe the thought behind this is that using handleCardAction is primarily intended to be used with the server-side confirmation flow where you want confirmation to happen server-side, but still need to be able to handle authentication with the customer on-session.
If your user is off-session, the intention is that they should come on-session to go through the confirmation flow again. Once you have your user on-session you could re-confirm the Payment Intent with the previously used Payment Method (either client or server-side)
yeah, precisely what I have setup - intent is that card is saved for off_session: true - which USUALLY works - and bring customer back online when there's a problem (which is exactly what I'm doing)
i.e. the item the user is being charged for is marked as an issue, along with the PI. The user is presented with an alert that there's an issue, and clicks through to allow them to respond
customer secret is then fetched from the server
Yeah so you're just missing a step - when you bring your customer back online you need to re-confirm the Payment Intent (this is what you're currently missing), either with confirmCardPayment client-side to do both the confirmation and handling of authentication, or confirm server-side with https://stripe.com/docs/api/payment_intents/confirm#confirm_payment_intent-payment_method to get it back to requires_action and then use handleCardAction
...and attached to the element
yeah, which is where it breaks - I am NOT getting back requires_action
but I can try one more way
the paymentsIntent.confirm is how I started the process in the first place...
Let me back up - what you're currently doing is you create a Payment Intent w/ confirm: true and off_session: true . This will never result in a status of requires_action because it's happening off_session.
To make your integration work you need to get it back to a state of requires_action by making an ADDITIONAL follow-up API call to re-confirm the Payment Intent w/o off_session: true. At that point you can then continue on to handle the next actions.
ah. So if I simple change that option/setting when re-trying the attempt, it will get the intended response. The LOGIC of it is off_session: true is BY DEFINITION without use, and off_session: false is BY DEFINITION with customer present
(not called out anywhere, and honestly I kinda disagree - the result would be easier to process if they responded the same way)
Nature of my code, at least: "try to NEVER special-case your code"
Thanks! Explanation makes sense, and easy to try.
If I may, a speculation: My code responded to payment_intent.failed in some modes by trying the next available payment_method. I could generalize this by responding to the above situation by immediately re-trying with off_session: false to setup for the customer action
(easy to detect - status: "requires_payment_method" and last_payment_error.code: "authentication_required"
Yeah, sorry that isn't super clear in our documentation
and nice! You actually already called out what I was going to mention
I've been doing this stuff a fair while...
If you check the reason for the failure and it's because authentication was required then yes, you could generalize it so that in those cases you retry with the same payment method and off_session: false
cool. I'm gonna follow up allowing users to select existing payment_methods for responding to issues. Overall system will remain that most-recent/'default" will be tried first. I may even get snotty-fancy and let them select the order to try. But not yet.
go ahead and close (I assume I can't do that)
will do!
whoops, sneaking in: the payment_method gets removed under off_session: true, so it needs to be added back...
Yes, you can find that under last_payment_error.payment_method (sorry I should've been clearer about that, was more focused on getting us on the same about re-confirmation first)\
oh, yeah, I knew where it was, just leaving the note behind for the next person to find if they search (not that very many people seem to search)
This thread has been archived. If you need help with anything else please ask in #dev-help or contact Stripe Support: https://support.stripe.com/contact
Find help and support for Stripe. Our support center provides answers on all types of situations, including account information, charges and refunds, and subscriptions information. Get your questions answered and find international support for Stripe.
I successfully got tht e payment_intent status to "requires_action", but the response sequence of the client is unclear in my scenario
I am attempting to use "stripe.handleCardAction({secret}), but this call never returns - the documentation (https://stripe.com/docs/js/payment_intents/handle_card_action ) also mentions "...you should disable your form from being resubmitted ..." - what form? If I create a paymentElement, it of course gives me "no card data" errors...
net action is I call my backend for the client secret, then call "stripe.handleCardAction({secret})," asynchronously
but as mentioned, it never returns - documentation seems to indicate to call confirm (whether on client or server) after it returns... but it doesn't...
btw, it also does not show up in the logs...
I'll note <Elements ... /> is not mounted under these conditions
(stepping away for 10 - 20 minutes)
(back)
HI there, catching up...
the net question, I guess, is under what conditions/environment does stripe.handleCardAction() get called? (referring to https://stripe.com/docs/payments/accept-a-payment-synchronously#web-handle-next-actions )
need a code? a req_? a PI_? anything?
Or if I need to return tomorrow...?
Hmm I don't follow the part "what conditions/environment". You simply call it on your frontend code, and when it will trigger the authentication UI
so you would think. I'm in a react environment; stripe is already initialized; I called it and it never returns.
The secret is already fetched from the backend, and the following occurs: ``` logger("payment_method");
(async () => {
await stripe.handleCardAction(secret);
logger("handled");
})();
called as a closure because it's inside a useEffect, which are inhenrently synchronous
the second logger call (a wrapper around console.log()) never occurs
I believe it returns a Promise, which you won't get the result right after it like that
dang... try...catch helped
await waits for the promise, which was all I'm testing. BUT...
caught error: IntegrationError: handleCardAction: The PaymentIntent supplied does not require manual server-side confirmation. Please use confirmCardPayment instead to complete the payment.
the PI is explicitly "requires_action"
pi_3KQixtAJ38Y8gLJy03YDmMix
When you made the additional call like my colleague suggested, did you pass in "confirm: true"?
well, no, I used "stripe_further_usage" on the PI, and connected the PM. Do I needc to update the PM first? I would have thought the PI's "stripe_further_usage" would have over-ridden...
extra call, not the hardest thing in the world. Front end only ever gets id's and client secrets
let me look closer at the Id
(I would update the PM on the server before re-submitting the PI)
(As you've seen, I'm already dealing with less-documented steps, so it wouldn't surprise me)
you should be able to see that next_action is set correctly as well
I can't be the only one using this integration path...
I don't think you would need to update the PM. The error looks weird to me (looking...)
hm. possible my secret fetch... checking
okie I see. When you first create the PaymentIntent
at the very first moment, you should specify confirmation_method to manual
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
(things have changed in response to earlier conversation, and this part may not have caught up)
(a) don't use curl (b) all confirms are server side (c) I use setupIntents to save for off_session usage (90% of paymentIntents/charges occur entirely asynchronous at server, successfully) - just dealing with the exceptsion - hence why the START as off_session, and the ones needing 3DS transition/are re-confirmed as on_session
I'm suspecting my various async's are getting ahead of themselves. Currently, my webhook for paymentFailed is re-submitting the off_session PI that returned "payment_failed"/"requires_payment_method"/"authentication_required" to change it to "requires_action"/"next_action". I suspect I should ONLY update the PI to "on_session" (and re-attached the PM), and let the rest of the process occur when the user takes action
the resubmitting part of your flow is correct as far as I can see. You don't need to change it
It's just in the first place when you created the PI, in this request: req_4WOHcMuTnJMzdE
please add one more parameter: confirmation_method, and set it to "manual"
The very first PI HAS to be off_session - I don't know it needs 3DS until AFTER it is tried.
HAS to start off_session, and be re-tried on_session only if it fails
but I will re-examine the confirmation_method
HAS to start off_session, and be re-tried on_session only if it fails
Understood! That's what I grasped from earlier thread
the re-submit issue is because my earlier code also retried the payment intent before getting the secret. I have to either not do the auto-retry, or remove the later retry
there's some "don't repeat yourself" code/entry-point re-use going on, so have to examine the flow a bit - which means it wil,l wait until tomorrow. But this leaves a "smell -trail" for me to follow in the morning
(one path requires a new card; the other only requires confirmation - the branching is occuring at both client and server at the moment, so that needs to be simplified)
K, I've confused us both enough for one night; you can archive the thread for now)
Thanks!
Sure, good luck!
thx. I'll summarize results from above...
=> Cards are captured as payment_methods using setupIntents.
=> In this case, 3DS-required card x3184 is used, and 3DS passed during setupIntent
=> setupIntent is set as off_session
=> later customer is charged using a paymentIntent
=> this payment intent, as-is, results in a paymentmethod.failed webhook, with status requires_payment_method, and authentication_required
=> I detect this specific condition, add the payment_method back to the paymentIntent (.failed removes the payment_method), and set status_future_usage to on_session
=> this results in a webhook call to paymentMethods.requiresAction.
=> when this occurs, I set (in my database) the charged object with the requires_action status, and a copy of the payment ID
=> at the FRONT end, this status is detected and flagged as an issue for user to fix (there will also be, later, emails and text messages to bring user back to browser).
=> when the user chooses to fix, a call to the server is made to fetch the client_secret, and a flag to differentiate requires_payment_method from requires_action
(wow, and this is the summary)
"summary" ๐
=> when it is a requires_action, I call stripe.handleCardAction with the client secret., which fails with error (caught in try...catch)...
=> IntegrationError: handleCardAction: The PaymentIntent supplied does not require manual server-side confirmation. Please use confirmCardPayment instead to complete the payment.
that's expected, you have to confirm server-side if you use confirmation_method: 'manual'
so I am confused about how to get to requiresAction webhook for a "manual server-side confirmation".... ?
yes, I assumed that - but I hadn't confirmed it; just called handleCardAction
what does a "requiresAction webhook" mean? There's no webhook of that name, and also if you use this flow it's because you don't want webhooks
paymentIntent.requires action - wanna bet?
My advice, which my team likely gave you: stop using this flow. This is extremely hard to do right, it's extremely limited, it's a bad design decision to use it, we have it just for specific users that refuse to build this the right way
I mean there's payment_intent.requires_action yes, which is not what you typed, you say "requiresAction webhook"
But there's no reason for you to use webhooks at all in this flow
My entire business logic requires saved cards from setupIntent later charged server-side with paymentIntent
that's unrelated to manual confirmation at all though
(somewhat-kinda-like a pledging system with later grouped charges)
cool so your PaymentIntents are off session later from previously saved cards. No reason to use confirmation_method: 'manual' in that world
sigh. I never did confirmation_method: 'manual'
I did confirmation_method: 'automatic', but the x3184 card is intended to fail with 3DS
sorry you wrote a wall of text of a summary so I'm barely understanding your flow unfortunately
hooooookay - I'd been using paymentElements (in react), but that always required a new card.
But if you have a PaymentIntent with confirmation_method: 'automatic' (the default) then you would simply call confirmCardPayment()client-side
the idea was to ONLY exec ute the 3DS confirmation client-side
yeah that's not a thing though
I can test this pretty dang fast - I am reading the implication that confirmCardPayment() would not show the card-capture fields, just the 3DS flow?
Really what you need to do is call confirmCardPayment and pass payment_method: 'pm_123' and that will work, you don't have to collect new card details
this can be an element (like when you do the SetupIntent collection) but it can also just be payment_method: 'pm_123'
if the PI already has a PM attached, do I need to include it in the confirmCardPayment() call?
if it does then no you don't have to
It'll take about 5 to 10 minutes to test - front-end-only
confirming: if confirmCardPayment() is used, I do not actually need to mount the <Elements ... /> ?
or does the call need to be in the <Elements ... /> context (i.e. child thereof)
Not sure, I rarely touch React, you need Stripe.js loaded, but not the card element mounted
k. Trying that flow
oh, you're going to love this - my environment updated and ran the change while I was typing here, and I returned to the 3DS flow iFrame. I think we have lift-off
Yelp, my loopy-de-loop of Stripe, Firebase Firestore backend, React front-end with redux and Firestore listeners is working. confirmCardPayment updated the PI, the "succeeded" webhook fired, which updated the "charged item" in my database, which fired a Listener, which updated the front end, removing the "needs work" sub-flow. wheeee!
Happy to hear it's working! ๐
hokay, so you know: tested & working: 3DS card authentication request; card declined removing user's paymentMethod, then selecting customer's next paymentMethod and succeeded; card declined, removing user's paymentMethod, no other paymentMethod available so flagging to front end for new card, entered new card with 3DS required, and worked. A couple more paths to check, but the majority of failure modes completed.
and all with the desired flow - user is NOT held waiting, but flagged later if there is an issue. In this case, it does mean (like all pledge systems, really) there will be fall-out at the charging point, but all possible remediation is done (FYI, users are also kept aware of how many different pledges for how much they have aoutstanding - it would be "nifty" to have some sense of the user's available card limits, but we all live with that)
This rather large thread can be archived for posterity, and to repeat as a scary story around a summer campfire.
๐