#adnahalilovic_unexpected

1 messages ¡ Page 1 of 1 (latest)

little oakBOT
#

👋 Welcome to your new thread!

⏱️ We automatically close idle threads, which makes them read-only. Make sure you stick around to chat in realtime!

🔗 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/1212769486726234184

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

⏲️ 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!

chrome cedarBOT
austere mulch
#

Hello

gentle birch
#

Hi

austere mulch
#

So really, you should not be using custom inputs here! That is opening you up to a huge PCI burden.

#

That said, can you show me your code for how you are calling confirmSetupIntent?

gentle birch
#

` import { useConfirmSetupIntent } from '@stripe/stripe-react-native';

const { confirmSetupIntent, loading } = useConfirmSetupIntent();

const createSetupIntent = useMutation(
(requestBody: CreateSetupIntentDto) => {
return apiClient.stripe.createSetupIntent({ requestBody });
},
{
async onSuccess(res) {
const clientSecret = res.data.client_secret;

    const { setupIntent, error } = await confirmSetupIntent(clientSecret, {
      paymentMethodType: 'Card',
      paymentMethodData: {
        billingDetails: { email: (user as User)?.email },
      },
    });
    if (error) {
      if (error.message === 'Card details not complete')
        navigation.goBack();
      else
        showToast({
          type: 'error',
          text: error?.message ?? 'Something went wrong.',
        });
    } else if (setupIntent) {
      navigation.goBack();
    }
  },
  onError(error: { body: BadRequestException }) {
    showToast({
      type: 'error',
      text: error?.body?.message ?? 'Something went wrong.',
    });
  },
}

);
const createToken = useMutation(
(requestBody: CreateTokenDto) => {
return apiClient.stripe.createToken({ requestBody });
},
{
async onSuccess(res) {
createSetupIntent.mutate({ token: res.data.id });
},
onError(error: { body: BadRequestException }) {
showToast({
type: 'error',
text: error?.body?.message ?? 'Something went wrong.',
});
},
}
);`

#

on backend:

` async createSetupIntent(customerId: string, token: string) {
try {
const paymentMethod = await this.stripeClient.paymentMethods.create({
type: 'card',
card: {
token: token,
},
});

  return await this.stripeClient.setupIntents
    .create({
      customer: customerId,
      payment_method: paymentMethod.id,
      confirm: true,
      return_url: 'tonso-client://root/add-new-card',
    })
    .then(async (result) => {
      return result;
    })
    .catch((error) => {
      throw new BadRequestException(
        (error as Error).message || 'Error while creating setup intent'
      );
    });
} catch (e) {
  throw new BadRequestException(e.message || 'An error occurred');
}

}

async createToken(customerId: string, card: CardDto) {
try {
return await this.stripeClient.tokens
.create({
card,
})
.then(async (result) => {
return result;
})
.catch((error) => {
throw new BadRequestException(
(error as Error).message || 'Error while creating token'
);
});
} catch (e) {
throw new BadRequestException(e.message || 'An error occurred');
}
}`

austere mulch
#

Alright first, you can cut out one of your backend requests by just passing the card data directly to paymentMethods.create()

#

You don't need to create a Token in a separate request.

#

That said, the reason you are seeing the error is that you aren't checking what happens with the SetupIntent when you confirm in your backend.

#

If 3DS isn't required, it will succeed in your backend and you don't need to confirm again in your frontend.

#

So you need to look at the status of the SetupIntent in your backend after that request, and only confirm in your frontend if the status is requires_action

gentle birch
#

so you say error wont appear if 3ds isnt required, but this error happens when I try to enter card which requires 3ds , too

austere mulch
#

Can you run a fresh test with a 3DS required card and provide me the SetupIntent ID?

gentle birch
#

{"application": null, "automatic_payment_methods": {"allow_redirects": "always", "enabled": true}, "cancellation_reason": null, "client_secret": "seti_1OpAt1LiTCnJ4Ibr3kawwZeZ_secret_PeTqoMvs7HprDdQhWr5rwnunrTOC9Xq", "created": 1709218023, "customer": "cus_PdHVcPzZQM9Eze", "description": null, "flow_directions": null, "id": "seti_1OpAt1LiTCnJ4Ibr3kawwZeZ", "last_setup_error": null, "latest_attempt": "setatt_1OpAt1LiTCnJ4IbrhWtfwCnX", "livemode": false, "mandate": null, "metadata": {}, "next_action": {"redirect_to_url": {"return_url": "tonso-client://root/add-new-card", "url": "https://hooks.stripe.com/3d_secure_2/hosted?merchant=acct_1NE1ECLiTCnJ4Ibr&publishable_key=pk_test_51NE1ECLiTCnJ4IbrfDsrlpwBZY5zFHuAkeP9paXvfuuzW1N0znimoOrqIetCG7M37ViWX5KUAOGtOXfdyUNfCzks008tsxGY0g&setup_intent=seti_1OpAt1LiTCnJ4Ibr3kawwZeZ&setup_intent_client_secret=seti_1OpAt1LiTCnJ4Ibr3kawwZeZ_secret_PeTqoMvs7HprDdQhWr5rwnunrTOC9Xq&source=src_1OpAt1LiTCnJ4Ibrx7lH6mhK"}, "type": "redirect_to_url"}, "object": "setup_intent", "on_behalf_of": null, "payment_method": "pm_1OpAt1LiTCnJ4IbrGxF8PZWM", "payment_method_configuration_details": {"id": "pmc_1OKWunLiTCnJ4IbrQHYVFkoM", "parent": null}, "payment_method_options": {"card": {"mandate_options": null, "network": null, "request_three_d_secure": "automatic"}}, "payment_method_types": ["card", "bancontact", "ideal", "link"], "single_use_mandate": null, "status": "requires_action", "usage": "off_session"}

#

this is response of create setup intent

#

for 3ds required card

austere mulch
#

Okay sure, but I don't see any frontend confirmation at all

#

Did you add a log to your frontend to ensure it is the same SetupIntent you are confirming on the frontend?

gentle birch
#

I thought that confirmSetupIntent would automtically redirect, because Im doing similar thing for payment

#

const createIntent = useMutation( (requestBody: CreatePaymentIntentDto) => { return apiClient.stripe.createPaymentIntent({ requestBody }); }, { async onSuccess(res) { const clientSecret = res.data.client_secret; const { error, paymentIntent } = await confirmPayment(clientSecret, { paymentMethodType: 'Card', paymentMethodData: { paymentMethodId: selectedCard?.id, }, }); if (error) { showToast({ type: 'error', text: 'Payment failed. Please try again.', }); setLoading(false); setPaymentAuthFailed(true); } else if (paymentIntent.status === 'Succeeded') { updateBooking.mutate({ paid: true, paymentIntentId: paymentIntent.id, percentageFee: totalPrice ? totalPrice * (feeConfig?.tonsoFeePercentage / 100) : 0, flatFee: feeConfig?.tonsoFeePercentage, }); } }, onError(error: { body: BadRequestException }) { setLoading(false); showToast({ type: 'error', text: error?.body?.message ?? 'Something went wrong.', }); }, } );

#

import { confirmPayment } from '@stripe/stripe-react-native';

#

this confirmPayment automatically redirects to 3ds screen if needed

austere mulch
#

Yes, if 3DS is required then confirmSetupIntent() would indeed handle showing the 3DS modal.

#

But what I'm telling you from your example right now is that confirmSetupIntent() never ran on your frontend for that SetupIntent

#

And you can see that the only thing that ever occurred was the backend creation request

#

There was no frontend confirmation request from calling confirmSetupIntent() on your frontend

gentle birch
#

Im trying to call confirmSetupIntent

const { setupIntent, error } = await confirmSetupIntent(clientSecret, { paymentMethodType: 'Card', paymentMethodData: { billingDetails: { email: (user as User)?.email }, }, }); if (error) { if (error.message === 'Card details not complete') navigation.goBack(); else showToast({ type: 'error', text: error?.message ?? 'Something went wrong.', }); } else if (setupIntent) { navigation.goBack(); } },

but I get error {"code": "Failed", "declineCode": null, "localizedMessage": "Card details not complete", "message": "Card details not complete", "stripeErrorCode": null, "type": null}

austere mulch
#

Can you add a log for the clientSecret there?

#

Right before you call confirmSetupIntent()

gentle birch
#

seti_1OpAt1LiTCnJ4Ibr3kawwZeZ_secret_PeTqoMvs7HprDdQhWr5rwnunrTOC9Xq

austere mulch
gentle birch
#

let me try

#

woah, it works!

#

thanks a lot

#

have a nice day

austere mulch
#

You too!

chrome cedarBOT
gentle birch
#

const { setupIntent, error } = await handleNextActionForSetup( clientSecret, 'tonso-client://root/add-new-card' );

after successful auth, it doesnt redirect me back to app, do you have idea why

#

i have to go back to app by myself

#

@austere mulch

austere mulch
#

Sounds like your deep link isn't set up correctly in that case?

gentle birch
#

when I try to enter link manually in browser, it redirects me to right screen

austere mulch
#

What happens if you just put an HTTPS URL in there?

#

Do you get redirected to that URL?

gentle birch
#

I didnt make universal link for this case yet

#

also, this works for confirm payment intent

#

it redirects me automatically

#

const { error, paymentIntent } = await confirmPayment(clientSecret, { paymentMethodType: 'Card', paymentMethodData: { paymentMethodId: selectedCard?.id, }, });

#

`await this.stripeClient.paymentIntents
.create({
...data,
confirmation_method: 'automatic',
confirm: true,
return_url: 'tonso-client://root/payment',
use_stripe_sdk: true,
})

await this.stripeClient.setupIntents
.create({
customer: customerId,
payment_method: paymentMethod.id,
confirm: true,
return_url: 'tonso-client://root/add-new-card',
})`

#

i defined return url in both stripe api calls

#

let me try adding use_stripe_sdk: true,

austere mulch
#

Can you just test that the returnURL works at all with handleNextActionForSetup() like I asked above?

#

Ah, good call.

#

I forgot about that

gentle birch
#

yep, works now

#

sorry for false alarm

#

haha