I would greatly appreciate your help because I am stuck with the following problem.
In Flutter, I use the Flutter_Stripe package to implement a payment module.
Hereunder you will find my Flutter function :
Future<void> initPayment({required String email, required int montant, required String nomOrga}) async{
try{
//1. Crée un payment intent sur le serveur
final response = await http.post(
Uri.parse("myFirebase URL"),
body: {
"email":email,
"amount": (montant*100).toString(),
},
);
print("email : $email - montant : $montant - organisation : $nomOrga");
final jsonResponse = jsonDecode(response.body);
print(jsonResponse);
//2. Initialise le payment sheet
await Stripe.instance.initPaymentSheet(paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: jsonResponse["paymentIntent"],
merchantDisplayName: nomOrga,
customerId: jsonResponse["customer"],
customerEphemeralKeySecret: jsonResponse["ephemeralKey"],
customFlow: true,
)
);
//3. Présenter le paiement
await Stripe.instance.presentPaymentSheet();
print("on a présenté la paymentsheet");
//4. Attacher la méthode de paiement au PaymentIntent et confirmer le paiement
/*final paymentIntentResult = await Stripe.instance.confirmPayment(
paymentIntentClientSecret: jsonResponse["client_secret"],
);*/
print("on a fini confirmpayment");
//print(paymentIntentResult.status);
/*if(paymentIntentResult.status == PaymentIntentsStatus.Succeeded){
//Paiement réussi
quickConfirm("Paiement réussi", "Le paiement est réussi", context);
}else{
quickAlert("Oups", "Le paiement a échoué", context);
}*/
}
catch(error){
if(error is StripeException){
quickAlert("Oups", "une erreur StripeException s'est produiten${error.error.message}", context);
}else{
quickAlert("Oups", "une erreur brol s'est produiten${error}", context);
}
}
}
and hereunder the server function:
const functions = require("firebase-functions");
const stripe = require("stripe")("sk_test_...");
require("firebase-functions/logger/compat");
exports.stripePaymentIntentRequest = functions.https.onRequest(async (req, res) => {
try{
let customerId;
//Get the customer who's email matches the one sent by the client
const customerList = await stripe.customers.list({
email: req.body.email,
limit: 1,
});
console.log(customerList);
//Check if customer exists, if not create a new customer
if(customerList.data.lenght !== 0){
customerId = customerList.data[0].id;
} else {
const customer = await stripe.customers.create({
email: req.body.email,
});
customerId = customers.id;
}
//Creates a temporarysecret key linked with the customer
const ephemeralKey = await stripe.ephemeralKeys.create(
{customer: customerId},
{apiVersion: '2020-08-27'}
);
//Creates a new payment intent with amount passed from the client
const paymentIntent = await stripe.paymentIntents.create({
amount: parseInt(req.body.amount),
currency: 'eur',
customer: customerId,
automatic_payment_methods: {
enabled: true,
},
});
console.log('on arrive au send');
console.log(res);
res.send({
paymentIntent: paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customerId,
publishableKey:'pk_test...',
success: true,
});
console.log('on a fini 200');
} catch(error) {
console.log('il y a eu une erreur :', error.message);
res.status(404).send({
success: false,
error: error.message,
});
}
});
I have the following problems :
a) As is I don’t receive any error but the payment is always incomplete with the status "requires_payment_method". I use the 4242… test card (note that I live in Belgium and the test card is a US one, is it the cause of the problem?).Note tat the status of the PaymentIntent request is 200. Hereunder, the result of it:
payment_intent.created
Afficher les détails de l'événement
Données de l'événement
{
"id": "pi_...",
"object": "payment_intent",
"last_payment_error": null,
"livemode": false,
"next_action": null,
"status": "requires_payment_method",
"amount": 100,
"amount_capturable": 0,
"amount_details": {
"tip": {
}
},
"amount_received": 0,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": {
"enabled": true
},
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"client_secret": "pi_...",
"confirmation_method": "automatic",
"created": 1677946647,
"currency": "eur",
"customer": "cus_...",
"description": null,
"invoice": null,
"latest_charge": null,
"metadata": {
},
"on_behalf_of": null,
"payment_method": null,
"payment_method_options": {
"bancontact": {
"preferred_language": "en"
},
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
},
"eps": {
},
"giropay": {
},
"ideal": {
}
},
"payment_method_types": [
"card",
"bancontact",
"eps",
"giropay",
"ideal"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"transfer_data": null,
"transfer_group": null
}
b) In the Flutter code, point4 (confirmPayment) is commented in the code sample. If I left it uncommented I receive an error, something like "Null is not a subtype of String"
c) If the customer doesn’t already exist in Stripe I receive an error (it probably comes from the javascript code, do I have to test for a null response?)
Note that I am not a professional (I try to develop my own app) so I would really appreciate your help and, if possible, elaborated answers.
I thank you for your help !
Bernard
2
Answers
Thanks to Vanya from the Stripe Discord team ! She found the problem: in the
initPaymentSheet
I set
customFlow: true
Removing this was the solution. It seems that when setting
customFlow
to true it separates the payment method and the confirmation. If set to true, then I must callconfirmPaymentSheetPayment
on my own.a) That looks like a payment_intent.created webhook event and your PaymentIntent was created successfully. However you would want to verify whether the request from your frontend to your backend has got back 200 status with its data. These 2 lines:
b) You are using PaymentSheet so you don’t need this
confirmPayment
functionc) On your backend you are already creating a new Customer in that case. Probably
req.body.email
was null and you may want to log the fullreq.body
and perform a null check before calling the Retrieve Customer API