#Brendan-3DS1

1 messages · Page 1 of 1 (latest)

hollow pilot
#

Hi there!

#

Do you have an example PaymentIntent I can look at where you tested this?

topaz egret
#

Did you make the changes I mentioned earlier?

#

3DS1 is being deprecated soon anyway by banks

inland birch
#

no I haven't made any changes

topaz egret
#

Then you should look into the recommendations I gave you earlier instead

inland birch
#

but 3DS2 works fine the way we have it setup, we are using subscriptionDetails.paymentBehavior = 'allow_incomplete';

topaz egret
#

I'm sorry I don't really follow. Can I ask you to try and be super descriptive about the issue? All you gave is 2 PaymentIntents, both of them happened on Subscription creation, both Subscription creations worked as expected and didn't error with the Subscription in incomplete

#

what exactly is your question about those 2 PaymentIntents?

inland birch
#

sorry, let me try and clarify! My main question is, when I test our checkout with the 3DS2 card everything works properly. A 3DS auth modal is launched on the client and if that modal is completed successfully our paymentIntent is completed, invoice is paid etc. When I try to use the 3DS1 card, the payment is declined and no 3DS auth modal is launched. Here is the 3DS auth modal that I am referring to:

topaz egret
#

Unfortunately this is still really vague I'm sorry

#

What happens when you do that 3DS1? What do you see? Which code is called? What do your logs show? What is the error? What is the Charge id ch_123 for the attempt?

inland birch
#

here's a failed charge

topaz egret
#

but it's not though that's the thing

#

that's on Subscription creation

#

that's expected to fail since you can't do 3DS (1 or 2) server-side. You need to go client-side and get your customer to go through 3DS

#

so at that point, your Subscription is incomplete and you have to confirm the PaymentIntent client-side

inland birch
#

here are the webhook logs:

#

022-08-01 15:07:59 --> payment_intent.succeeded [evt_2LS3y4oomZdVFM251MLa3man]
2022-08-01 15:07:59 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_2LS3y4oomZdVFM251MLa3man]
2022-08-01 15:07:59 --> payment_intent.created [evt_2LS3y4oomZdVFM251c3Xlx7U]
2022-08-01 15:07:59 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_2LS3y4oomZdVFM251c3Xlx7U]
2022-08-01 15:07:59 --> charge.succeeded [evt_2LS3y4oomZdVFM251gHbD3iN]
2022-08-01 15:07:59 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_2LS3y4oomZdVFM251gHbD3iN]
2022-08-01 15:08:00 --> invoice.updated [evt_0LS3y7oomZdVFM25dNDQ22Nw]
2022-08-01 15:08:00 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_0LS3y7oomZdVFM25dNDQ22Nw]
2022-08-01 15:08:00 --> invoice.paid [evt_0LS3y8oomZdVFM25oWriHaBx]
2022-08-01 15:08:00 <-- [404] POST http://localhost:3000/api/v0/webhooks/stripe [evt_0LS3y8oomZdVFM25oWriHaBx]
2022-08-01 15:08:00 --> invoice.payment_succeeded [evt_0LS3y8oomZdVFM25E4RT1md3]
2022-08-01 15:08:00 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_0LS3y8oomZdVFM25E4RT1md3]
2022-08-01 15:08:01 --> invoice.finalized [evt_0LS3y8oomZdVFM25Pig8zQKz]
2022-08-01 15:08:01 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_0LS3y8oomZdVFM25Pig8zQKz]
2022-08-01 15:08:35 --> setup_intent.setup_failed [evt_0LS3yhoomZdVFM25lLPn16vX]
2022-08-01 15:08:35 <-- [200] POST http://localhost:3000/api/v0/webhooks/stripe [evt_0LS3yhoomZdVFM25lLPn16vX]

topaz egret
#

not entirely sure what you want me to do with those sorry

#

it's a mix of many things, including a successful payment

inland birch
#

sorry I'm multitasking, in a meeting give me a few mins to give you my full attention, busy day!

topaz egret
#

totally fine take your time

inland birch
topaz egret
#

The first one you created the subscription, payment failed as 3DS was required, then client-side you went and confirmed the PaymentIntent again and did 3DS

#

the second one, you created the subscription, payment failed as 3DS was required, then you did nothing with it. Never tried to confirm the PaymentIntent client-side

#

I think that is breaking your logic/code is the PaymentIntent's status. The first one is in requires_action and the second one is in requires_payment_method and that's likely throwing you off

#

all you need to do is confirm the PI again client-side with the same pm_123456 and it will do 3DS as expected

inland birch
#

yes, I think you are right

#

it looks like we are treating requires_payment_method status as a failed payment now

#

how do we know that requires_payment_method is related to a 3DS auth?

topaz egret
#

You mostly don't, you'd treat it as a decline and try again on session

#

it could be anything like generic decline. insufficient funds, etc, but at the end of the day you'd tell that to your customer and have them try again

#

Also you should flip the integration like I recommended earlier and you wouldn't hit this problem!

inland birch
#

so that's expected behavior for 3DS1 to return the requires_payment_method status?

#

so if we try the payment again, won't it just return the same status, how do we trigger the 3DS auth modal?

#

and apparently my team consulted with you guys(stripe support) when we were designing our new checkout solution. I guess we needed to do this so we could display the last4 of the card before we confirm the payment.

topaz egret
#

you confirm client-side

#

If you confirm client-side, it will behave the right way, exactly what happens with the 3DS2 card

inland birch
#

gotcha

#

so we should just retry any declined cards client side

#

that have status requires_payment_method

topaz egret
#

well no, you should change your integration like I said a few times

#

sorry to nitpick but that's really the right way to build this

inland birch
#

one of my teammates, Zach is going to join the conversation as he has more context than I do

#

but thank you so much for your help

#

and sorry I wasn't giving you the info you needed from the start!

topaz egret
#

sure thing! definitely agree the behaviour is a bit confusing and ultimately not really a big deal if you switch your integration

gusty meadow
#

Hey 👋 I'm going to jump in here - I'm a dev on Brendan's team and I've spoken a lot with customer support when planning this work

Stripe customer support (via email) suggested that we integrate the way we currently do due to a request made by our design team. I'll be super specific so I'm sorry if this is a bit verbose!

In our checkout flow, our design team had the requirement that we allow the customer to enter their card details before actually paying for the subscription. A "card summary" (last 4 digits, expiration, etc) would then be displayed, along with the amount they were going to be charged, and we'd display a "confirm payment" button which would actually make the payment.

What Stripe email support suggested is to:

  1. Capture the customer's credit card info using a Setup Intent. This would attach the payment method to the customer and allow us to get the last 4 digits, etc
  2. When the user hits "confirm", we use stripe.subscriptions.create or stripe.subscriptions.update as part of Stripe Billing to create/update the subscription
  3. We enable Stripe 3DS failure emails so that if step 2 fails, the user will receive an email to verify the payment

We're looking into alternatives for step 3. Instead of just having Stripe send the email, we want to take the payment intent from step 2 and re-confirm it on the front end so that the user can verify 3DS

I see that it's a bit backwards but it was originally Stripe support who OK'd this idea so I'm hoping we can figure out how to do this

topaz egret
#

@knotty ravine is taking over but I have context so at the end of the day really all you need to change is step #2 and pass default_incomplete and always confirm client-side. That's what I'd recommend as it will work with all payment methods and not just cards.

#

Otherwise, handle a decline (like the 3DS1) and try to confirm client-side again which also solves the problem

gusty meadow
#

Otherwise, handle a decline (like the 3DS1) and try to confirm client-side again which also solves the problem

Thanks, I think this is what we're trying to do. We were originally confused because documentation for payment intent statuses say that 3DS will be requires_action, not just for 3DS2 (at least as far as I can tell)

You mentioned that 3DS1 is being deprecated. Do you happen to have any information about the ratio of 3DS1 vs 3DS2 verifications Stripe currently processes? Or how many 3DS1s we should expect?

knotty ravine
#

A Payment Intent will only have a status of requires_action when confirmed client-side. The requires_action status indicates to the client that an action needs to be taken and information is provided to the client to take that action. When you confirm server-side there is no client, so whatever action is required is not possible (because your server can't go through 3D Secure, for example) so the action being required is treated as a decline, the Payment Method is removed from the Payment Intent, and the status of the Payment Intent thus goes back to requires_payment_method.

#

After that happens you can associate the removed Payment Method with the Payment Intent again and attempt to confirm it client-side, but really what @topaz egret described is best; you should avoid server-side confirmation all together and only confirm client-side.

topaz egret
#

3DS1 is being blocked in September in Europe by most banks last I heard

gusty meadow
#

@knotty ravine thanks for the message. Respectfully, that is not what we're seeing. We confirm server-side with stripe.subscriptions.create() with one of your 3DS dev cards such as 4000003800000446 and we receive subscription.latest_invoice.payment_intent.status: 'requires_action'

#

3DS1 is being blocked in September in Europe by most banks last I heard
great, thanks :)

knotty ravine
#

What is the code you're using to confirm the Payment Intent server-side?

topaz egret
#

I mean it's just sub creation

#

it looks like our 3DS1 test card is just hard declined instead of next action

gusty meadow
#

We're doing stripe.subscriptions.create() or stripe.subscriptions.update(). We don't have a separate step to confirm

knotty ravine
#

Oh, I misunderstood.

topaz egret
#

pi_2LS48foomZdVFM251nmoHRAL goes to requires_payment_method (3DS1 card) while pi_2LS136oomZdVFM251WNzDfOM goes to requires_action (3DS2 card)

#

my point is overall, just avoid this, don't confirm server-side, always confirm client-side

gusty meadow
#

How would we go about allowing the user to save their card prior to confirming the payment so that we can show the last 4 digits?

#

Since we store the card on the customer via Setup Intent, can we confirm using confirmCardPayment? It seems that the documentation for confirmCardPayment all talk about Payment Intents. Would it also work with Setup Intents?

topaz egret
#

that's not the point I am making

#

you already seem to have built all of that

#

You already collect card details, already save them, already create a subscription

gusty meadow
#

Ahh I see. You're saying create the subscription as incomplete and always try to confirm on the client

topaz egret
#

what you need to change is this:
Do not confirm the payment server-side on Subscription creation. Pass payment_behaviour: 'default_incomplete' instead of payment_behavior: 'allow_incomplete'. This means no payment attempt happens on Subscription creation (what was your Step #2)
After that, always confirm the underlying PaymentIntent client-side. Exactluy what you already do for 3DS2 requirement, just always

#

doing that will allow you to support more payment methods in the future

gusty meadow
#

Okay, thank you. We'll take a look at that

#

It's my understanding that a subscription with status: incomplete cannot be modified (updated quantity, billing interval, etc). Is that correct?

knotty ravine
#

The Subscription itself can be modified, but the Invoice that was already generated and finalized cannot.

#

Why do you ask? What flow are you trying to accommodate?

gusty meadow
#

If a customer tries to pay for a subscription and their payment fails, we want to give them the option to change their subscription details before re-confirming. Maybe they only have funds for 4 members but they accidentally tried to pay for 5

I could've sworn when we tried default_incomplete, when we tried to do a subscription.update() with a new quantity, we got an error saying that incomplete subscriptions cannot be updated

knotty ravine
#

If you want to do that I recommend you cancel the existing Subscription and create a new one.

#

If they make changes I mean.

gusty meadow
#

That's what we currently do on the server side. I was wondering how we'd do that on the client side if

  1. Server side creates subscription with default_incomplete
  2. Client side tries to confirm the payment
  3. Confirmation fails
  4. Then I guess we'd make an API endpoint in our server to cancel a subscription and the client would call it?
knotty ravine
#

You'd create an API endpoint to make changes to the Subscription, which it sounds like you already have? But the logic on the server would not update the existing Subscription, it would cancel it and create a new one with the changes.

gusty meadow
#

So the endpoint would check the subscription's status before updating? If it's incomplete, delete and create a new one. If it's not, just update?

knotty ravine
#

Maybe I'm misunderstanding, but wouldn't they always be incomplete in this scenario?

gusty meadow
#

In this scenario, yes. But what if a customer already has an active subscription and wants to update their quantity? Right now that's what we use the subscription update endpoint for, but it always assumes a subscription is active

#

(though maybe I'm misunderstanding haha)

knotty ravine
#

What specific updates are you making to the Subscription? I just tested an update to an incomplete Subscription and it succeeded. You said something about quantity?

gusty meadow
#

We pass

items: [
      {
        id: subscriptionItem.id,
        price: stripeValues.plan,
        quantity: stripeValues.quantity,
      },
    ]

though in our system id will never change

knotty ravine
#

Oh, here we go. Is this the error you're talking about?

You cannot update a subscription in `incomplete` status in a way that results in a new invoice or invoice items. Only minor attributes, like `metadata` or `default_payment_method`, can be updated on such subscriptions.```
gusty meadow
#

Yeah!

knotty ravine
#

Yeah, the error message is correct. If a Subscription is incomplete you can't modify it such that it will produce a new Invoice as there's an outstanding one that needs to be resolved to get the Subscription into the next state.

gusty meadow
#

Okay, makes sense. Thanks we'll take a look

knotty ravine
#

You can think of incomplete Subscriptions as unactivated Subscriptions... they're not "activated" yet until the first outstanding Invoice resolves.

gusty meadow
#

Thanks, that makes sense :)

knotty ravine
topaz egret
#

Anything else we can help with?