capacitor-community / stripe

Stripe Mobile SDK wrapper for Capacitor
https://capacitor-community-stripe.netlify.app/
MIT License
196 stars 77 forks source link

Payment flow never cancel #193

Closed cyrilfr closed 2 years ago

cyrilfr commented 2 years ago

Platform

Describe the bug When you call Stripe.presentPaymentFlow() it opens a closable modal sheet. Unfortunately, the return of this function is inconsistent as it's not able to distinguish between the modal sheet being closed or a credit card being selected. Actually if you close it with the cross, you get the cardNumber of the default selected credit card and the PaymentFlowEventsEnum.Completed event is triggered instead of PaymentFlowEventsEnum.Canceled. This is not what's expected.

To Reproduce Steps to reproduce the behavior:

  1. Call Stripe.presentPaymentFlow()
  2. Press the cross to close the modal
  3. See the PaymentFlowEventsEnum.Completed being triggered and the function return the default selected card.

Expected behavior The PaymentFlowEventsEnum.Canceled being triggered on cancel.

Edit I tested with the iOS Simulator and that doesn't work either. The default selected payment method is returned.

Here is a screenshot taken right after presentPaymentFlow(). Pressing the cross is equivalent to pressing on Google Pay or on the credit card already registered, depending on the default selected payment method. Screenshot_2022-07-06-08-19-26-14_cd9790fdd8d7837e7a7940c8e2b868ec

rdlabo commented 2 years ago

@cyrilfr Thanks for issue. What is your using framework?? I confirmed work well in Angular. Thanks.

cyrilfr commented 2 years ago

I'm using Ionic 6 with Angular 13. I pass the setupIntentClientSecret for Stripe.createPaymentFlow().

  await Stripe.createPaymentFlow({
    setupIntentClientSecret: setupFlow.setupIntentClientSecret,
    customerEphemeralKeySecret: setupFlow.ephemeralKeySecret,
    customerId: setupFlow.customerId,
    merchantDisplayName: environment.appName,
    enableGooglePay: true,
    enableApplePay: true,
    applePayMerchantId: environment.applePayMerchantId
  });
rdlabo commented 2 years ago

Oh, I see. I misunderstood to your issue. Please give me some time. This is my first time, so I will investigate from 0.

Thanks.

Dydyvicks commented 2 years ago

Stripe.presentPaymentFlow()

cyrilfr commented 2 years ago

@rdlabo let me know if you need extra information :)

rdlabo commented 2 years ago

OK, I see.

rdlabo commented 2 years ago

Stripe.presentPaymentFlow can check only Did customer have payment method(credit card? or Google Pay, or Apple Pay). This is a specification and I have no control over it. ( So if customer registers incorrect credit card information, it will not be known that it is wrong until confirm )

So we have to confirm at the will of the customer. If confirm did not run, customer canceled. If you want to run confirm automatically, you should use PaymentSheet method. This can get cancel.

Thanks.

cyrilfr commented 2 years ago

The confirm is always run because I cannot distinguish between a press on the "close" button and a press on a payment method. On the screenshot in my first post, if I press the cross in the top left corner of the modal, the "Google Pay" payment method will be "confirmed" because presentPaymentFlow() returns 'Google Pay' instead of null, 'cancel' or just triggering the cancel event.

I'm using a setupIntent so the PaymentSheet method cannot be used.

Maybe I'm not understanding it well. I want to save a payment method with a setupIntent but the user should also be able to cancel the process when closing the modal. Do you have any example to do that?

rdlabo commented 2 years ago

The confirm is always run

PaymentFlow method is for separation of payment information and payment actions. Don't run confirm automatically.

I'm using a setupIntent so the PaymentSheet method cannot be used.

I can implement to be able to set setupIntent on PaymentSheet. But, what is the difference between using paymentIntent in this case?

user should also be able to cancel the process

This is right, but I can't find how to implement. I will talk about this with stripe team.

cyrilfr commented 2 years ago

PaymentFlow method is for separation of payment information and payment actions. Don't run confirm automatically.

The issue is related to presentPaymentFlow() because with this code:

    const setupFlow = await this.paymentService.stripeSetupIntent();
    await Stripe.createPaymentFlow({
      setupIntentClientSecret: setupFlow.setupIntentClientSecret,
      customerEphemeralKeySecret: setupFlow.ephemeralKeySecret,
      customerId: setupFlow.customerId,
      merchantDisplayName: environment.app.name,
      enableGooglePay: true,
      enableApplePay: true,
      applePayMerchantId: environment.applePayMerchantId
    });
    const result = await Stripe.presentPaymentFlow();
    console.log(result);

the console shows: { cardNumber: 'Google Pay' } when I press the close button.

I can implement to be able to set setupIntent on PaymentSheet. But, what is the difference between using paymentIntent in this case?

I need to save the payment method for future payment.

This is right, but I can't find how to implement. I will talk about this with stripe team.

I hope we'll be able to distinguish between payment method selection and modal cancelation because that makes the user experience less friendly.

rdlabo commented 2 years ago

@cyrilfr

I need to save the payment method for future payment.

You can use paymentIntent , and use setup_future_usage in backend: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-setup_future_usage

setupIntent is something that only makes you enter your card information and not when you want to make the payment. Please use them well.

cyrilfr commented 2 years ago

I don't perform a payment at this point, so setupIntent is exactly what I need.

rdlabo commented 2 years ago

perform a payment at this point

Ok, I understand completely. But looking at the Android logs, there doesn't seem to be a difference between submit and close. Please give me some time to discuss this with the stripe team.

rdlabo commented 2 years ago

Memo: I've hit on a solution. let's enable setupIntent in the PaymentSheet method. The critical problem this time is trying to confirm automatically in PaymentFlow.

rdlabo commented 2 years ago

I released v3.9.1-0. Please try in PaymentSheet method. Thanks.

@cyrilfr

cyrilfr commented 2 years ago

Thank you @rdlabo, it works!

But Google Pay isn't available as a selectable payment method anymore and the countryCode param is ignored.

rdlabo commented 2 years ago

I checked paymentConfiguration at run presentWithSetupIntent, but Google Pay and the countryCode set rightly:

Configuration(merchantDisplayName=rdlabo, customer=CustomerConfiguration(id=cus_M61MyTPlV23vJW, ephemeralKeySecret=ek_test_YWNjdF8xS0ZEa3NLUkc5UFJjcnp6LDJCM1VPbWt6enlwVzBYeElFaTQ1bDRnUG5yYUk3c0U_00HlV8esrk), googlePay=GooglePayConfiguration(environment=Test, countryCode=US, currencyCode=null), primaryButtonColor=null, defaultBillingDetails=null, allowsDelayedPaymentMethods=false)

Apparently this seems to be a specification. Can using Google Pay method help you? https://stripe.capacitorjs.jp/docs/google-pay

Thanks.

rdlabo commented 2 years ago

I may be able to solve this problem by increasing the version of the Stripe library, but since the targetSDK is different, it is not possible to increase the version in Capacitor3 any further. In the future, Capacitor4 may solve this problem.

cyrilfr commented 2 years ago

I checked paymentConfiguration at run presentWithSetupIntent, but Google Pay and the countryCode set rightly:

I specifically set countryCode to 'CH'(Switzerland) but "France" is selected be default instead. The UI is in French because this is the language of the OS but this isn't the value of the parameter.

Apparently this seems to be a specification. Can using Google Pay method help you?

I'll go with Google Pay method then.

rdlabo commented 2 years ago

@cyrilfr https://github.com/capacitor-community/stripe/issues/181#issuecomment-1126668615 will help you?

countryCode is only use on Google Pay method. So if google pay is disabled, countryCode don't set in plugin: https://github.com/capacitor-community/stripe/blob/master/android/src/main/java/com/getcapacitor/community/stripe/paymentsheet/PaymentSheetExecutor.java#L85

Thanks

cyrilfr commented 2 years ago

countryCode is only use on Google Pay method. So if google pay is disabled, countryCode don't set in plugin: https://github.com/capacitor-community/stripe/blob/master/android/src/main/java/com/getcapacitor/community/stripe/paymentsheet/PaymentSheetExecutor.java#L85

OK I though it was used to preset the country when entering credit card detail. My bad.

Thanks.

rdlabo commented 2 years ago

@cyrilfr I'm glad you got it sorted out! Did you use this plugin for production? If yes, please tell us what app. This help development this plugin.

👉 https://github.com/capacitor-community/stripe/issues/145

Thanks.