After a user registers on my app I want them to add a payment method. Their stripe customer account is created as soon as they register and from there they are transferred to the 'AddPaymentMethod' screen. As soon as the ' AddPaymentMethod' screen appears, I send a request to my server to create a setupIntent.
Creating Setup Intent:
exports.createSetupIntent = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const snapshot = await db
.collection("development")
.doc("development")
.collection("users")
.doc(userId).get();
const customerId = snapshot.data().customer_id;
const setupIntent = await stripe.setupIntents.create({
customer: customerId,
});
const clientSecret = setupIntent.client_secret;
return {
clientsecret: clientSecret,
};
});
Calling the function when the screen appears on my client (This successfully creates the client secret key and stores it in a variable in the frontend):
FirebaseReferenceManager.functions.httpsCallable("createSetupIntent").call(["userId": Auth.auth().currentUser?.uid]) { (response, error) in
if let error = error {
print(error.localizedDescription)
}
if let response = (response?.data as? [String: Any]) {
let clientSecretKey = response["clientsecret"] as! String?
self.clientSecret = clientSecretKey ?? "-"
print("created client secret key: \(clientSecretKey!)")
}
}
Next, the user enters their credit card information and creates a payment method. Here is the function on my server:
exports.createPaymentMethod = functions.https.onCall(async (data, context) => {
const number = data.number;
const expMonth = data.expMonth;
const expYear = data.expYear;
const cvc = data.cvc;
const paymentMethod = await stripe.paymentMethods.create({
type: "card",
card: {
number: number,
exp_month: expMonth,
exp_year: expYear,
cvc: cvc,
},
});
const pmId = paymentMethod.id;
return {
paymentMethodId: pmId,
};
});
I call this function from the frontend when the user presses the "Save payment method" button. This successfully creates a payment method and returns the payment method id which is stored in a variable on the front end.
Lastly, using the client secret id and payment method id that was returned from the previous functions, I call the last function to confirm the setupIntent.
This function is called when a payment method is created successfully:
exports.confirmSetupIntent = functions.https.onCall(async (data, context) => {
const clientSecretKey = data.clientSecretKey;
const paymentMethodId = data.paymentMethodId;
const setupIntent = await stripe.setupIntents.confirm(
clientSecretKey,
{payment_method: paymentMethodId}
);
});
This is how the createPaymentMethod and confirmSetupIntent functions are called from the frontend:
FirebaseReferenceManager.functions.httpsCallable("createPaymentMethod").call(["number": self.cardNumber, "expMonth": self.expMonth, "expYear": "20\(self.expYear)", "cvc": self.cvvCode]) { (response, error) in
if let error = error {
print("error occured when creating payment method: \(error.localizedDescription)")
}
if let response = response?.data as? [String: Any] {
let paymentMethodId = response["paymentMethodId"] as! String?
self.paymentMethodID = paymentMethodId ?? "-"
print(paymentMethodId!)
FirebaseReferenceManager.functions.httpsCallable("confirmSetupIntent").call(["clientSecretKey": self.clientSecret, "paymentMethodId": self.paymentMethodID]) { (response, error) in
if let error = error {
print("error occured when confirming setup intent: \(error.localizedDescription)")
}
print("setup intent confirmed")
}
}
}
In the debug console on the frontend it says that the error from confirming the setupIntent was INTERNAL. When I check the logs on my server I it says: StripeInvalidRequestError: No such setupintent: 'seti_...'
Note that I am using SwiftUI and custom screens/textfields for the stripe integration.
Any help is appreciated!