#rubens_card-3ds

1 messages · Page 1 of 1 (latest)

silver galeBOT
#

👋 Welcome to your new thread!

⏲️ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question. Thank you for your patience!

⏱️ We automatically close idle threads, which makes them read-only. Make sure you stick around to chat in realtime! If this thread is closed and you have another question you'll need to start a new thread.

🔗 This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1213228141406068776

📝 Have more to share? You can add more detail below, including code, screenshots, videos, etc.

pulsar roverBOT
#

Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

heady stump
#

Sorry not sure I understand the question

#

Already looks like you're checking the funding type in your code snippet

swift galleon
#

yes, I am.
but today i got this weird error, saying card funding is not 'credit'.
Then when I check this subscription on dashboard. it says it is.

#

can you check if this
"pm_1OpcdEChHlHBFg3wG3Bw6RZZ"

is related to this subscription?
sub_1OpcdKChHlHBFg3wstdIzTxq

#

cus is from same customer.
But I'm not sure if from a previous failed payment, or this active one.

#

did it make sense for you now @heady stump ?

heady stump
#

Not really

#

That's a credit card

swift galleon
#

pm_1OpcdEChHlHBFg3wG3Bw6RZZ

this?

heady stump
#

yes

#

you can retrieve the pm yourself

#

and see the funding type via the api

swift galleon
#

i have a errorLog function:

if(paymentMethod.card?.funding !== 'credit'){
error_log(
JSON.stringify(paymentMethod),
'_createCardToPay - NOT CREDIT',
JSON.stringify({
user_email: user.profile.profile_email,
host: hostData.hostCode,
seat: hostData.hostCode == 'AEFT' ? user.profile.ae_seat : user.profile.ppro_seat
})
);

  }
#

and this function was triggered. By this PM

#

cus I got this Log into my database in backend

#

Makes no sense for me too.

heady stump
#

You should add the funding type to the log

#

That way in the future you see what it says

#

But that pm_ has a credit funding type

swift galleon
#

Yes, but instead of the whole paymentMethod object (as in other cases), is returning only the pm_code.

My code is JSON.stringify(paymentMethod)

#

my function that create a card to pay is have only this kind of return:

return result.setupIntent.payment_method as PaymentMethod | null;

heady stump
#

Wait if you don't have the whole object how are you able to access the funding param?

#

How are you checking the funding param here: if(paymentMethod.card?.funding !== 'credit'){ if you don't have the whole object?

swift galleon
#

more details:

const setupIntentResponse = await createSetupIntent({ customerId });

const result = await stripe.confirmCardSetup(setupIntentResponse.client_secret, {
payment_method: {
card: cardElement,
billing_details: {
email: email,
name: name.toUpperCase(),
},
},
});

if (result.error) {
console.log('Erro na confirmação do SetupIntent:', result.error);
error_log(
JSON.stringify(result.error),
'_createCardToPay Erro na confirmação do SetupIntent',
JSON.stringify({
user_email: email,
customerId: customerId,
})
);
return null;
}

return result.setupIntent.payment_method as PaymentMethod | null;

heady stump
#

That doesn't answer my question. That's just a different code block entirely

swift galleon
#

What I'm trying to say is...
If you try to use a debit card in my code...
it triggers the error and show the whole object.
But only at this time.
I got the pm_code instead.

#

It's weird for me too.

heady stump
#

So you're saying it almost always works (ie accepts credit cards and rejects debit cards) but in this one case, the paymentMethod variable was a payment method id instead of an object?

swift galleon
#

I'll check my whole code line by line again.

#

YES, exactly!

#

I don't know why.
This is the first time the error_log trigger this condition.

heady stump
#

Got it

#

Where do you set the paymentMethod variable in your code? Hard for me to debug this since I don't have access to your env

swift galleon
#

I Know... sorry to bother you.
I'll check it again line by line.
If this is a valid transaction... I'm calm now.

#

Just try to reach support as fast as I can, because. I lost a lot of subscriptions from debit cards from banks with different currency than my default.
That's why I was upset.

#

Can you please, only confirm that this subscription:

sub_1OpcdKChHlHBFg3wstdIzTxq

Is correctly setup for charging after trial period ends ??

#

and let me know how to check and confirm this from dashboard if possible. So I don't bother Support for this in future?

heady stump
#

You need to see if a payment method was collected

#

So check if one is set as default on the Subscription or Customer. It will be default_payment_method on the subscription or invoice_settings.default_payment_method on the customer

#

Looks like one is set on the customer, so you're fine

#

You should collect payment method via the subscription's setup intent if possible

#

Or just any setupintent

swift galleon
#

like here?

heady stump
#

Directly creating a payment method increases chances of declines/3ds authentication later

#

I'm refering to the api

swift galleon
#

I'm creating a setupIntent from my server side.

heady stump
#

I'm referring to the payment method set as default on that sub specifically

#

See the request I linked above

#

It was not created via setupintent

swift galleon
#

mmmm

here's my current code:

`const handlerSetupIntent = async (req, res) => {
await NextCors(req, res, {
methods: ['POST'],
origin: '*',
optionsSuccessStatus: 200,
});

console.log("Requisição recebida para criar um SetupIntent.");

const { error, value } = schema.validate(req.body);

if (error) {
console.error("Erro de validação:", error.details[0].message);
res.json({ error: Validation error: ${error.details[0].message} });
return;
}

console.log("Dados validados:", value);

try {
// Crie o SetupIntent
const setupIntent = await stripe.setupIntents.create({
automatic_payment_methods: { enabled: true },
});

console.log("SetupIntent criado com sucesso:", setupIntent);

res.status(200).json(setupIntent);

} catch (error) {
console.error("Erro ao criar SetupIntent:", error.message);
res.status(400).json({ error: error.message });
}
};`

heady stump
#

That code did not create that payment method

#

Please see the link I shared

pulsar roverBOT
swift galleon
#

i call this back end and then i confirmCardSetup()

`
const setupIntentResponse = await createSetupIntent({ customerId });

// console.log('setupIntentResponse.client_secret', setupIntentResponse.client_secret);

//@ts-ignore
const result = await stripe.confirmCardSetup(setupIntentResponse.client_secret, {
payment_method: {
card: cardElement,
billing_details: {
email: email,
name: name.toUpperCase(),
},
},
});`

heady stump
#

Again, see the request i linked

#

The payment method in question was not created via a setupintent

#

It was created in the frontend with the card element

swift galleon
#

ok.
So the problem is that the current pm created with cardelement is not defining as the 'default' payment method ?

"preferred": null ?

heady stump
#

No

#

As I stated previously, it is set as default on the customer under invoice_settings.default_payment_method

#

The issue is because it wasn't created via a setupintent, payment has a higher liklihood of failing or requiring 3ds authentication in the future

swift galleon
#

ok.
So whats Wrong? Sorry If I'm dumb.

let me try to explain if I understand.

currently 'I think' I'm creating the pm_here

const { paymentMethod, error: paymentMethodError } = await stripe.createPaymentMethod({
type: 'card',
card: cardElement,
billing_details: {
name: name,
email: email,
},
});

then I'm confirming from here.

const setupIntentResponse = await createSetupIntent({ customerId });

// console.log('setupIntentResponse.client_secret', setupIntentResponse.client_secret);

//@ts-ignore
const result = await stripe.confirmCardSetup(setupIntentResponse.client_secret, {
payment_method: {
card: cardElement,
billing_details: {
email: email,
name: name.toUpperCase(),
},
},
});

#

in the moment of PAY
I'm using the PM that came from.

return result.setupIntent.payment_method as PaymentMethod | null;

#

I know it looks confuse,

#

for me too...

graceful ivy
#

Can you give us the exact SetupIntent id seti_123 where you collected card card with

#

rubens_card-3ds

graceful ivy
#

looking

#

Okay so here's what I think you are doing

  1. Collect card details client-side
  2. Create a PaymentMethod pm_123 client-side
  3. Server-side explicitly call the Attach PaymentMethod API to attach that pm_123 to a Customer cus_123
  4. Server-side create a Subscription
#

Can you confirm that's what you do?

swift galleon
#

yes.... 99,9% sure is exactly this.

#

This is a bad approach?

graceful ivy
#

yep!

swift galleon
#

O M G

graceful ivy
#

follow that doc instead the flow will be this

  1. (client-side): Collect card details in PaymentElement
  2. (client-side) Create a PaymentMethod pm_123
  3. (server-side) Create a Customer
  4. (server-side) Create a Subscription with payment_behavior: 'default_incomplete' and properly expand pending_setup_intent and latest_invoice.payment_intent
  5. (server-side) Based on whether your Subscription needs a payment upfront or not, confirm the SetupIntent/PaymentIntent that was created with the pm_123 from step #1
  6. (client-side) if any "next action" is needed such as doing 3DS, finish this client-side
swift galleon
#

My customers is already created at this point.

#

I think the only difference is that I'm

const result = await stripe.confirmCardSetup(setupIntentResponse.client_secret, {
payment_method: {
card: cardElement,
billing_details: {
email: email,
name: name.toUpperCase(),
},
},
});

on client-side.

#

Should I do this right after createSetupIntent on server-side?

#

currently.
I'm only doing this from setupIntent in server side:

try {
const setupIntent = await stripe.setupIntents.create({
automatic_payment_methods: { enabled: true },
});
res.status(200).json(setupIntent);
} catch (error) {
console.error("Erro ao criar SetupIntent:", error.message);
res.status(400).json({ error: error.message });
}

Shoud I do the confirmCardSetup right after setupIntent is available?
in this same endpoint?

graceful ivy
#

yeah sorry you're going too fst and mixing everything up

#
  1. do not call the Attach PaymentMethod API
  2. understand that you are not creating the SetupIntent yourself, it comes from your Subscription right?
swift galleon
#

no, i'm confuse

#

I'm doing this from my server side.
only in the moment the subscription is created

paymentMethod = await stripe.paymentMethods.attach(payment_method.id, { customer: customer })

#

sorry my dumbness

graceful ivy
#

Do not call that method, that's wht I said. Take 10 minutes, carefully read the doc I shared earlier and start fresh and implement what I described

swift galleon
#

ok. I'll do this.

#

How it's possible to reach this chat after we close it?

graceful ivy
#

it's not. You start a new question and provide enough details to help you further

swift galleon
#

That's no good.
Sometimes a problem takes time to be solved

#

It's a lot to eat

graceful ivy
#

Really right now the thread is open. The first step is for you to pause, read the doc, and implement what's in the doc. That should take 10/15 minutes!

swift galleon
#

I'm doing this right now... but is the end of my day. you know...
maybe it's better to start this with a fresh mind.
My app is already in production....
And I can;t break anything.
Cus it's working right now.
Only failing with debit subscriptions after trial period ends.

#

The rest is working

graceful ivy
#

Gotcha! So yeah totally fine to come back next week and ask follow up questions if needed!

swift galleon
#

But you'll find this same conversation?

#

or another support member?

graceful ivy
#

new conversation, new person. Which is totally fine. We're all developers, you come back with a fresh mind and a clear new question. We don't start from this thread as you were quite thoroughly lost and have to first do the work to read the doc I shared and understand it end to end

swift galleon
#

Yes... you're right thaks

#

thanks

#

Just because this is the most afraid part of the code for me... (maybe for most)

#

But I'll take a deep breath... have a nice sleep night... and restart from scratch.
Thank you.

#

Have a nice weekend too!