#edgar-3ds-redirect
1 messages ยท Page 1 of 1 (latest)
Hello ๐
I wasn't aware of full page redirects for 3DS
I thought they always used a modal
Can you provide more information about where you're seeing this? Is this with a specific test card?
yes, thats what was happening before, i believe it has to do with the fact that we now provide a returnUrl, thats the only change weve seen, but that was required only for cashapp payments
im using 4000 0000 0000 3220
and its redirecting me to this page
I can't seem to reproduce this on https://4242.io/payment-element/
What are you testing with?
using react elements:
express checkout
link auth element
payment element
on the same page
are you setting nvmredirect: always in your code somewhere?
is this page live? can you share the URL?
nop, its local , havent yet merged it to prod
Can you share the code you're using for elements and confirmPayment call?
yes, give me a sec
so we are confirming on the server
the flow is, handleSubmit -> server-> handleServerResponse
the last one was handling all actions required for 3ds
can you share it in codeblocks?
Tall images aren't readable on discord ๐
try this
oh sorry, yes
// Submit handler for all methods
const handleSubmit = useCallback(
async (evt: CheckoutEvent) => {
if (!stripe) {
return handleError(new Error('Stripe.js not loaded'));
}
setIsSubmitting(true);
setErrorMessage('');
if (!elements) {
return handleError(new Error('Elements not loaded'));
}
const { error: submitError } = await elements.submit();
if (submitError) {
const paymentElement = elements.getElement('address');
paymentElement?.focus();
return handleError(submitError);
}
showElementLoadingState(true);
const { error: paymentMethodError, paymentMethod } = await stripe.createPaymentMethod({
elements,
});
if (paymentMethodError) {
return handleError(paymentMethodError);
}
const addressElement = elements.getElement('address');
let addressValue: DefaultValuesOption['billingDetails'] | object = {};
if (addressElement) {
const { value }: { value: DefaultValuesOption['billingDetails'] | object } =
await addressElement.getValue();
addressValue = transformProfileFromStripeData(value as StripeAddressElementData);
}
const { data } = await createStripePurchaseV2({
variables: {
purchaseId,
paymentMethodId: paymentMethod.id,
profile: addressValue,
returnUrl,
},
});
if (!data) {
return null;
}
const trackingProps = extractTrackingProps({ evt, paymentMethod });
return handleServerResponse({ subscription: data.createStripePurchaseV2, trackingProps });
},
[
stripe,
elements,
showElementLoadingState,
createStripePurchaseV2,
purchaseId,
returnUrl,
handleServerResponse,
handleError,
],
);
// 3d secure and/or success handler
const handleServerResponse = useCallback(
async ({
subscription,
trackingProps: incomingTrackingProps,
}: {
subscription: StripeSubscription;
trackingProps: StripeCheckoutEventTrackingProps;
}) => {
const intent = (subscription.latestInvoice?.paymentIntent ||
subscription.pendingSetupIntent) as unknown as StripePaymentIntent | StripeSetupIntent;
if (!intent) {
return handleError(new Error('Unexpected error: no intent found'));
}
const trackingProps = { ...incomingTrackingProps, intent };
if (intent.status === SUCCEEDED) {
return handleSuccess({ subscription, trackingProps });
}
if (intent.status === REQUIRES_ACTION) {
if (!stripe) {
return handleError(new Error('Stripe.js not loaded'));
}
const { error, paymentIntent, setupIntent }: Partial<PaymentIntentOrSetupIntentResult> =
await stripe.handleNextAction({
clientSecret: intent.clientSecret,
});
if (error) {
return handleError(error);
}
if (paymentIntent?.last_payment_error) {
return handleError(new Error(paymentIntent?.last_payment_error?.message));
}
if (setupIntent?.last_setup_error) {
return handleError(new Error(setupIntent?.last_setup_error?.message));
}
if (paymentIntent?.status === SUCCEEDED || setupIntent?.status === SUCCEEDED) {
return handleSuccess({ subscription, trackingProps });
}
}
return handleError(new Error(`Unexpected error occurred for intent ${intent.id}`));
},
[handleError, handleSuccess, stripe],
);
the server it just
with some tracking and local db code
okay, so handleNextAction is what triggers the 3DS flow right?
yess
we're looking, will respond once we find something.
In the meantime, if you're able to upload/setup a page where we can reproduce this then that'd be super helpful
awesome, thanks!
would a direct tunnel to my code work? using ngrok
awesome, let me set it up
edgar-3ds-redirect
Taking over after all @lofty ore I think the first step is to try and extremely simple/basic end to end example, if possible not using React because it makes it much harder for me to pair with you as I know nothing about React
Like right now you're doing something quite complex with React and Subscriptions and all that
It'd be best to "pause" and start with the most basic PaymentIntent creation
I'm trying in parallel too but want to make sure we talk about the same thing
Can you share the exact doc page you are following too if you have that?
yes, the thing is that all was working correctly, we are using this https://stripe.com/docs/payments/finalize-payments-on-the-server
and suddenly, instead of the modal we are getting the redirect page
Yep just trying to get to the exact same state you are, there are between 7 and 2954 ways to integrate so it's so hard to grasp what people are seeing when doing obscure things in the first place
So would be great if we could pair and align on the most basic and simple behaviour first without Billing/Subscriptions
that doc you linked has 20 variations depending on the version and framework, any chance you can tell me which of the many sub-tabs you are on?
Okay so let's ignore Subscription entirely for now, it's likely irrelevant
All I did was
- Create a PaymentIntent + confirm with
pm_card_authenticationRequired+return_url - Call
handleNextAction()client-side which does a redirect.
And you're saying it didn't happen before and it would do the modal "in line"?
yeah okay I can reproduce, this feels like a real bug to me honestly. I don't think there's a way around it unfortunately if you use CashApp and you'll mostly have to deal with the redirect as is
yes, the modal was appearing everytime, i believe it has todo with the return_url param, we added it bc of cashapp
i see
Ah my colleague found something I always forgot which is clearly in the docs you shared, did you miss it maybe?
ah it's because it's not in that tab, ugh
https://stripe.com/docs/api/payment_intents/confirm#confirm_payment_intent-use_stripe_sdk pass this as true and it will work
awesome, ill try that, what does that do?
it explicitly says you plan to use one of our SDK (here Stripe.js).
i see, so should i leave that as true regardless of the payment method type?
(tested it, it works, thanks!)
yeah I completely forgot this exists so all kudos to @cold hill. Makes kind of no sense to me since you are literally calling the Stripe.js function already and it's our SDK but ยฏ_(ใ)_/ยฏ
haha no worries, i learned somehting today ๐