I'm trying to implement a 3D Secure payment method in the stripe payment gateway in react native.
The code is working fine for the normal 4242 xxxx series. But this is throwing an error when I put a 3D Secure card.
const PaymentCompleteScreen = () => {
const publishableKey ="";
const handlePayment = async () => {
setLoading(true);
try {
const response = await createPaymentMethod({
type: "Card",
card: cardDetails,
cvc: cardDetails.cvc,
billingDetails: billingDetails,
}) //Creating a normal payment method to extract paymentMethodId
.then(async (result) => {
if (result.error) {
setLoading(false);
Alert.alert("Error", result.error.message);
} else {
const paymentMethodId = result.paymentMethod
? result.paymentMethod.id
: null;
const createSub = await createSubscription(
paymentMethodId,
user?.stripeId,
selectedGroups[0].priceId
); //Passing the paymentMethodId to complete the payment.
if (createSub.status) {
await deviceStorage.saveItem("cart", JSON.stringify([]));
Alert.alert(
"Payment Successful",
"Your payment was successful. Login again to access the premium group."
); //Success
setLoading(false);
} else if (createSub.requires_action) //3D Secure
{
const { error, paymentIntent } = await confirmPayment(
createSub.payment_intent_client_secret, //I get this from my backend
{
type: "card",
}
);
console.log(error);
if (error) {
setLoading(false);
Alert.alert("Error", error.code + " " + error.message);
} else if (paymentIntent) {
Alert.alert(
"Success",
"Your payment was successful. Login again to access the premium
group."
);
setLoading(false);
}
}
}
})
.catch((error) => {
console.log(error);
});
setLoading(false);
} catch (err) {
console.log(err.message);
}
};
return (
<View style={{ flex: 1, padding: 20 }}>
<Text
style={{
fontFamily: "Roboto-Regular",
fontSize: themeStyles.FONT_SIZE_MEDIUM,
marginTop: 5,
}}
>
Enter your card details to finish the payment
</Text>
<StripeProvider publishableKey={publishableKey}>
<CardField
onCardChange={setCardDetails}
postalCodeEnabled={false}
autofocus={true}
cardStyle={{
backgroundColor: "#FFFFFF",
textColor: "#000000",
}}
style={{
width: "100%",
height: 50,
marginVertical: 30,
}}
/>
<StripeButton
variant="primary"
loading={loading}
title="Pay"
disabled={!cardDetails.complete}
onPress={() => handlePayment()}
/>
</StripeProvider>
</View>
);
};
In the handlePayment() method I'm doing two things.
Object { "data": Object { "invoice_id": "in_zzz", "subscription_id": "sub_zzz", }, "payment_intent_client_secret": "pi_zzz", "requires_action": true, }
Any help to solve this will be a great improvement in this project ๐
The actual issue here is that type
in the confirmPayment()
method is case-sensitive. Instead of type: "card"
you are required to use type: "Card"
. You can see this is case-specific in the SDK here.
Edit: Below is the initial answer. urlScheme
is not actually required to handle the 3DS redirect, however is is highly recommended otherwise the customer will not be returned to your App or a designated success page.
The issue is that 3DS requires a return URL since the customer will be redirected away from your App to the issuerโs authentication page to handle 3DS authentication and thus you must specify where they should be returned to after completing authentication. You set this return URL via urlScheme
when you initialize StripeProvider
โ you can see an example in the docs here.