stripe-react-native
stripe-react-native copied to clipboard
confirmSetupIntent with PayPal paymentMethodType
Describe the bug I created a SetupIntent on the server side for off-session usage with the PayPal payment method and encountered an error when attempting to confirm the intent. My goal is to collect customer details from PayPal (payer_id, payer_email) similar to the confirmPlatformPaySetupIntent.
To Reproduce server side:
const stripe = new Stripe(
'sk_test_....',
{
apiVersion: '2023-10-16',
typescript: true,
}
);
app.post('/create-setup-intent', async (req, res) => {
let setupIntent = null;
try {
const { offSession, type, userAgent, ipAddress } = req.body;
const customers = await stripe.customers.list();
const customer = customers.data.find(
(customer) => customer.id === 'cus_Opt76NQecXwbL1' //? just mock
);
if (!customer) {
throw new Error('Customer not found');
}
if (type === 'paypal') {
setupIntent = await stripe.setupIntents.create({
usage: offSession ? 'off_session' : 'on_session',
payment_method_types: ['paypal'],
payment_method_data: {
type: 'paypal',
},
customer: customer.id,
confirm: true,
return_url: 'example://stripe/paypal',
mandate_data: {
customer_acceptance: {
type: 'online',
online: {
ip_address: ipAddress,
user_agent: userAgent,
},
},
},
});
} else {
setupIntent = await stripe.setupIntents.create({
usage: offSession ? 'off_session' : 'on_session',
});
}
res.status(200).json({
paymentIntent: setupIntent.client_secret,
paymentIntentId: setupIntent.id,
type,
});
} catch (error: any) {
console.error(
error?.raw?.message ?? error?.message ?? error.toString().toUpperCase()
);
res.json({
error: 'Create intent for off-session Platform payment failed.',
});
}
});
client side:
const createSetupIntent = async () => {
try {
const res = await fetch('http://localhost:8000/create-setup-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
offSession: true,
type: 'paypal',
userAgent,
ipAddress: netInfo.ipAddress,
}),
});
const { paymentIntent, paymentIntentId, type } = await res.json();
if (!paymentIntentId || !paymentIntent) {
throw new Error('Invalid payment intent');
}
setOffSessionPayment((prev) => ({
...prev,
started: true,
completed: false,
type,
paymentIntent: {
...prev.paymentIntent,
status: 'created',
id: paymentIntentId,
secret: paymentIntent,
},
}));
} catch (err: any) {
console.error(err);
const error =
err?.raw?.message ?? err?.message ?? err ?? 'Something went wrong';
setOffSessionPayment((prev) => ({
...prev,
error,
started: false,
completed: false,
}));
}
};
const handleConfirmSetupIntent = async () => {
const { paymentIntent, type } = offSessionPayment;
if (!paymentIntent || !paymentIntent?.secret) return;
if (type === 'platform_pay') {
try {
const { error, setupIntent } = await confirmPlatformPaySetupIntent(
paymentIntent.secret,
{
applePay: {
currencyCode: 'usd',
merchantCountryCode: 'DE',
cartItems: [
{
label: 'Test',
amount: '10.00',
paymentType: 'Immediate' as any,
},
],
requiredBillingContactFields: [
PlatformPay.ContactField.Name,
PlatformPay.ContactField.PostalAddress,
PlatformPay.ContactField.EmailAddress,
PlatformPay.ContactField.PhoneNumber,
],
requiredShippingAddressFields: [
PlatformPay.ContactField.Name,
PlatformPay.ContactField.PostalAddress,
PlatformPay.ContactField.EmailAddress,
PlatformPay.ContactField.PhoneNumber,
],
},
googlePay: {
testEnv: true,
currencyCode: 'usd',
merchantCountryCode: 'DE',
amount: 10,
label: 'Test',
allowCreditCards: true,
billingAddressConfig: {
format: PlatformPay.BillingAddressFormat.Full,
isRequired: true,
},
},
}
);
if (error) {
console.error(error);
Alert.alert('Error', error.message);
} else {
console.log(setupIntent);
const { paymentMethodId, paymentMethodTypes, usage, status } =
setupIntent ?? {};
if (!paymentMethodId) throw new Error('Invalid payment method id');
setOffSessionPayment((prev) => ({
...prev,
paymentMethod: {
id: paymentMethodId,
types: paymentMethodTypes,
status,
usage,
},
}));
createPaymentMethod(paymentMethodId);
}
} catch (error) {
console.error(error);
}
} else {
const { error, setupIntent } = await confirmSetupIntent(
paymentIntent.secret,
{
paymentMethodType: 'PayPal',
paymentMethodData: {
mandateData: {
customerAcceptance: {
online: {
ipAddress: netInfo.ipAddress,
userAgent: userAgent!,
},
},
},
},
},
{
setupFutureUsage: 'OffSession',
}
);
if (error) {
console.error(error);
} else {
console.log(setupIntent);
}
}
};
I encountered an error while attempting to confirm the SetupIntent:
{"code": "Failed", "declineCode": null, "localizedMessage": "There was an unexpected error -- try again in a few seconds", "message": "To confirm the SetupIntent with the PayPal payment method type, you need to provide a 'return_url' address.", "stripeErrorCode": "", "type": "invalid_request_error"}
Expected behavior I want to confirm the SetupIntent and retrieve customer PayPal details for future use as a PaymentMethod.
Smartphone (please complete the following information):
- Device: [iPhone 14 Pro]
- OS: [iOS 17]
- Browser [Simulator]
- Versions: "stripe": "^14.1.0" "@stripe/stripe-react-native": "^0.35.0"
Additional context This is simply a mock app for a proof of concept, hence the use of 'any' in various places.
+1, this is only happening with Paypal right now.
I am facing this issue for iDeal & Paypal. On test environments, payments were working fine. On production environment I am facing this issue.
I have the same issue. In development mode, the integration works fine. However, when switching to the live mode, I face the issue.
@iranaahsanali I was able to get it working. After connecting PayPal as a payment method to your Stripe account, it is mandatory to configure recurring payments for it. The following link explains how to do that. You typically get feedback within 5 days. I got feedback, and everything is working fine now.
@dtn1999 Thanks for detailed steps, I did the same thing and everything worked for me as well.