英文:
Unable to create active subscription (recurring) with Stripe 3D Secure, react native
问题
我无法创建一个使用React Native前端和Node后端的Stripe 3D Secure的活动订阅。订阅已经成功创建,并且对于非3D Secure的Stripe支付是活动的。但是对于3D Secure,支付已经验证并在支付仪表板上更新,但是订阅并没有被更新为活动状态。
我已经按照这个Stack Overflow回答的工作流程进行了操作,但仍然无法正常工作。
前端代码:
const createSubscription = async () => {
try {
if (!cardFormData?.complete) {
alert('Please input your card data!');
setSaving(false);
return;
}
const {firstName, lastName} = userData;
const name = `${firstName} ${lastName}`;
const billing_details = {
email,
name,
description: name,
};
// making payment method
const paymentMethod = await createPaymentMethod({
paymentMethodType: 'Card',
card: cardFormData,
billing_details,
});
const body = JSON.stringify({
paymentMethod: paymentMethod?.paymentMethod?.id,
name,
email,
});
console.log('going to make the api call');
// call the backend to create subscription / customer / client secret
const response = await fetch(`${APIURL}/pay/makePayment`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body,
});
const respJson = await response?.json();
const {
clientSecret,
error: apiError,
customerId,
subscriptionId,
subscriptionPaymentMethod,
subscriptionStart,
subscriptionEnd,
} = respJson;
if (apiError) {
alert(`Payment Error 1 ` + apiError);
setSaving(false);
} else {
// confirm the payment by the user
console.log('confirming payment');
const {paymentIntent, error} = await confirmPayment(clientSecret, {
type: 'Card',
paymentMethodType: 'Card',
paymentMethodData: {
billing_details,
payment_method: subscriptionPaymentMethod,
},
});
console.log('clientSecret', clientSecret);
console.log('Object', {
type: 'Card',
paymentMethodType: 'Card',
paymentMethodData: {
billing_details,
payment_method: subscriptionPaymentMethod,
},
});
console.log('paymentIntent', paymentIntent);
if (paymentIntent) {
createUser(
customerId,
subscriptionId,
subscriptionStart,
subscriptionEnd,
);
} else if (error) {
alert(`Payment Error`);
setSaving(false);
}
}
} catch (e) {
alert(`Payment Error 2 ` + e?.message);
setSaving(false);
}
};
后端代码:
router.post("/makePayment", async (req, res) => {
try {
const { email, name, paymentMethod } = req.body;
if (email === "" || name === "" || !email || !name || !paymentMethod)
throw "";
// making new customer
const customer = await addNewCustomer(email, name, paymentMethod);
// making new subscription
const subscription = await addSubscription(customer?.id);
// getting stripe secret
const { error, clientSecret } = await getStripeSecret(
subscription?.latest_invoice?.payment_intent?.payment_method,
customer?.id
);
// saving data in session
req.session.customerId = customer?.id;
req.session.subscriptionId = subscription?.id;
res
.send({
clientSecret,
error,
customerId: customer?.id,
subscriptionId: subscription?.id,
subscriptionPaymentMethod:
subscription?.latest_invoice?.payment_intent?.payment_method,
subscriptionStart: new Date(subscription.current_period_start * 1000),
subscriptionEnd: new Date(subscription.current_period_end * 1000),
})
.status(!error ? 200 : 500);
} catch (e) {
res.send({ error: "Internal Server Error" }).status(500);
}
});
后端函数:
import Stripe from "stripe";
const { STRIPE_SECRET_KEY, STRIPE_API_VERSION } = process?.env;
const stripe = Stripe(STRIPE_SECRET_KEY, { apiVersion: STRIPE_API_VERSION });
export const getStripeSecret = async (paymentMethod, customerId) => {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: 28000, //lowest denomination of particular currency
currency: "aud",
payment_method_types: ["card"], //by default
payment_method: paymentMethod,
customer: customerId,
});
const clientSecret = paymentIntent?.client_secret;
return { clientSecret };
} catch (e) {
return { error: e?.message };
}
};
export const addNewCustomer = async (email, name, paymentMethod) => {
try {
const customer = await stripe.customers.create({
email,
description: name,
payment_method: paymentMethod,
invoice_settings: {
default_payment_method: paymentMethod,
},
});
return customer;
} catch (e) {
return { error: e?.message };
}
};
export const addSubscription = async (customerId) => {
try {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: process.env.STRIPE_PRICE_ID }],
payment_settings: {
payment_method_options: {
card: {
request_three_d_secure: "automatic",
},
},
payment_method_types: ["card"],
save_default_payment_method: "on_subscription",
},
expand: ["latest_invoice.payment_intent"],
});
return subscription;
} catch (e) {
return { error: e?.message };
}
};
我尝试按照上面分享的代码进行操作。我希望我的订阅能够像没有3D Secure一样处于活动状态。
英文:
I am unable to create an active subscription with Stripe 3D secure using react-native frontend, node backend. The subscription has been created fine and is active for non 3D Secure Stripe payments. For 3D secure, the payment is varified and updated on payment dashboard, but the subscription is not being updated to be active at all.
Screenshots of the subscription (recurring):
I have followed the workflow of this stackoverflow answer, but it still does not work.
Frontend Code:
const createSubscription = async () => {
try {
if (!cardFormData?.complete) {
alert('Please input your card data!');
setSaving(false);
return;
}
const {firstName, lastName} = userData;
const name = `${firstName} ${lastName}`;
const billing_details = {
email,
name,
description: name,
};
// making payment method
const paymentMethod = await createPaymentMethod({
paymentMethodType: 'Card',
card: cardFormData,
billing_details,
});
const body = JSON.stringify({
paymentMethod: paymentMethod?.paymentMethod?.id,
name,
email,
});
console.log('going to make the api call');
// call the backend to create subscription / customer / client secret
const response = await fetch(`${APIURL}/pay/makePayment`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body,
});
const respJson = await response?.json();
const {
clientSecret,
error: apiError,
customerId,
subscriptionId,
subscriptionPaymentMethod,
subscriptionStart,
subscriptionEnd,
} = respJson;
if (apiError) {
alert(`Payment Error 1 ` + apiError);
setSaving(false);
} else {
// confirm the payment by the user
console.log('confirming payment');
const {paymentIntent, error} = await confirmPayment(clientSecret, {
type: 'Card',
paymentMethodType: 'Card',
paymentMethodData: {
billing_details,
payment_method: subscriptionPaymentMethod,
},
});
console.log('clientSecret', clientSecret);
console.log('Object', {
type: 'Card',
paymentMethodType: 'Card',
paymentMethodData: {
billing_details,
payment_method: subscriptionPaymentMethod,
},
});
console.log('paymentIntent', paymentIntent);
if (paymentIntent) {
createUser(
customerId,
subscriptionId,
subscriptionStart,
subscriptionEnd,
);
} else if (error) {
alert(`Payment Error`);
setSaving(false);
}
}
} catch (e) {
alert(`Payment Error 2 ` + e?.message);
setSaving(false);
}
};
Backend Code:
router.post("/makePayment", async (req, res) => {
try {
const { email, name, paymentMethod } = req.body;
if (email === "" || name === "" || !email || !name || !paymentMethod)
throw "";
// making new customer
const customer = await addNewCustomer(email, name, paymentMethod);
// making new subscription
const subscription = await addSubscription(customer?.id);
// getting stripe secret
const { error, clientSecret } = await getStripeSecret(
subscription?.latest_invoice?.payment_intent?.payment_method,
customer?.id
);
// saving data in session
req.session.customerId = customer?.id;
req.session.subscriptionId = subscription?.id;
res
.send({
clientSecret,
error,
customerId: customer?.id,
subscriptionId: subscription?.id,
subscriptionPaymentMethod:
subscription?.latest_invoice?.payment_intent?.payment_method,
subscriptionStart: new Date(subscription.current_period_start * 1000),
subscriptionEnd: new Date(subscription.current_period_end * 1000),
})
.status(!error ? 200 : 500);
} catch (e) {
res.send({ error: "Internal Server Error" }).status(500);
}
});
Backend Functions:
import Stripe from "stripe";
const { STRIPE_SECRET_KEY, STRIPE_API_VERSION } = process?.env;
const stripe = Stripe(STRIPE_SECRET_KEY, { apiVersion: STRIPE_API_VERSION });
export const getStripeSecret = async (paymentMethod, customerId) => {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: 28000, //lowest denomination of particular currency
currency: "aud",
payment_method_types: ["card"], //by default
payment_method: paymentMethod,
customer: customerId,
});
const clientSecret = paymentIntent?.client_secret;
return { clientSecret };
} catch (e) {
return { error: e?.message };
}
};
export const addNewCustomer = async (email, name, paymentMethod) => {
try {
const customer = await stripe.customers.create({
email,
description: name,
payment_method: paymentMethod,
invoice_settings: {
default_payment_method: paymentMethod,
},
});
return customer;
} catch (e) {
return { error: e?.message };
}
};
export const addSubscription = async (customerId) => {
try {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: process.env.STRIPE_PRICE_ID }],
payment_settings: {
payment_method_options: {
card: {
request_three_d_secure: "automatic",
},
},
payment_method_types: ["card"],
save_default_payment_method: "on_subscription",
},
expand: ["latest_invoice.payment_intent"],
});
return subscription;
} catch (e) {
return { error: e?.message };
}
};
I tried following the above-shared code. I would want to have my subscription active, just like without 3D Secure.
答案1
得分: 1
你不应该在getStripeSecret
函数中创建和使用新的PaymentIntent。你已经有一个PaymentIntent,就是subscription.latest_invoice.payment_intent
。你应该获取并返回该PaymentIntent的client_secret,并在前端使用它。根据你当前的代码,你正在处理两笔独立的支付(因此会出现重复),而你在前端处理的那笔支付与订阅没有任何关联(因此它保持不活跃)。你应该仔细阅读并准确实现以下指南:
https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements
但是作为一个开始,你的代码应该更像这样:
// 创建新的订阅
const subscription = await addSubscription(customer?.id);
// 获取 stripe secret
const clientSecret = subscription.latest_invoice.payment_intent.client_secret;
...
英文:
You shouldn't be creating and using a new PaymentIntent in getStripeSecret. You already have a PaymentIntent — it's the one available in subscription.latest_invoice.payment_intent
. You should be getting and returning the client_secret from that one and using it on the frontend. With your current code you are processing two separate payments(hence the duplicates) and the one you process on the frontend is not connected to the Subscription at all(hence why it remains inactive). You should carefully read and exactly implement the guide at
https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements
But as a start your code should be more like this:
// making new subscription
const subscription = await addSubscription(customer?.id);
// getting stripe secret
const clientSecret = subscription.latest_invoice.payment_intent.client_secret;
...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论