skip to Main Content

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


  1. Chosen as BEST ANSWER

    Thanks to Vanya from the Stripe Discord team ! She found the problem: in the initPaymentSheet

    await Stripe.instance.initPaymentSheet(paymentSheetParameters: SetupPaymentSheetParameters(
      paymentIntentClientSecret: jsonResponse["paymentIntent"],
      merchantDisplayName: nomOrga,
      customerId: jsonResponse["customer"],
      customerEphemeralKeySecret: jsonResponse["ephemeralKey"],
      customFlow: true,
    ));
    

    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 call confirmPaymentSheetPayment on my own.


  2. 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:

    final jsonResponse = jsonDecode(response.body);
    print(jsonResponse);
    

    b) You are using PaymentSheet so you don’t need this confirmPayment function

    c) 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 full req.body and perform a null check before calling the Retrieve Customer API

        //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,
        });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search