triniwiz / nativescript-stripe

Apache License 2.0
49 stars 34 forks source link

Issue with "authenticate and charge on the UI" #112

Closed Stanteq closed 4 years ago

Stanteq commented 4 years ago

Hi devs. I have an issue (or maybe a question) about authenticate and charge on the UI.

So, I trying to set up the cards for future payments. I using this test card: 4000002760003184 which should ask every time to authenticate. First time (with stripe.confirmSetupIntent()) works fine.

After, when I try to charge:

on server:

// Clone a payment method
const pm: any = await stripe.paymentMethods.create(
    { customer, payment_method: paymentMethod },
    {
      stripeAccount,
    }
  );

const pi = stripe.paymentIntents.create({
      amount,
      application_fee_amount: fee,
      currency: "eur",
      payment_method: pm.id,
      off_session: true,
      confirm: true,
      expand: ["charges.data.balance_transaction"],
    },
    { stripeAccount })

which return "authentication_required". I send to the client: error.payment_intent.client_secret and error.payment_intent.last_payment_error.payment_method.id:

// Client
 console.log(paymentMethodId, clientSecret); // to ensure that they are present: pm_1GehuAEZ7PcnewTdkgJtwVAc pi_1GehuBEZ7PcnewTdeyPM7wKF_secret_5fsTVdJoinGENGDUHSAOlIJW9

 const piParams = new StripePaymentIntentParams();
    piParams.paymentMethodId = paymentMethodId;
    piParams.clientSecret = clientSecret;

    stripe.confirmPaymentIntent(piParams, (piError: any, si: any) => {
      if (piError) {
        console.log("piError", piError);
        console.log("piError message", piError.message);
        console.log("piError stack", piError.stack);
        return;
      }
       ...
 });

Error on Android

JS: piError Error
JS: piError message 
JS: piError stack Error
JS:     at new ZoneAwareError (file: node_modules/@nativescript/angular/zone-js/dist/zone-nativescript.js:1298:0)
JS:     at Object.onError (file: node_modules/nativescript-stripe/stripe.android.js:197:0)
JS:     at Object.activity.onActivityResult (file: node_modules/nativescript-stripe/stripe.android.js:201:0)
System.err: An uncaught Exception occurred on "main" thread.
System.err: Unable to resume activity {com.mobelup.app/com.tns.AppActivity}: java.lang.IllegalArgumentException: No view found for id 0x8 (unknown) for fragment fragment4[3]<Page(265)>
System.err: 
System.err: StackTrace:
System.err: java.lang.RuntimeException: Unable to resume activity {com.mobelup.app/com.tns.AppActivity}: java.lang.IllegalArgumentException: No view found for id 0x8 (unknown) for fragment fragment4[3]<Page(265)>
System.err:     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4411)
System.err:     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4443)
System.err:     at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
System.err:     at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
System.err:     at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2147)
System.err:     at android.os.Handler.dispatchMessage(Handler.java:107)
System.err:     at android.os.Looper.loop(Looper.java:237)
System.err:     at android.app.ActivityThread.main(ActivityThread.java:7811)
System.err:     at java.lang.reflect.Method.invoke(Native Method)
System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076)
System.err: Caused by: java.lang.IllegalArgumentException: No view found for id 0x8 (unknown) for fragment fragment4[3]<Page(265)>
System.err:     at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:875)
System.err:     at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
System.err:     at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
System.err:     at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
System.err:     at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
System.err:     at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
System.err:     at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
System.err:     at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
System.err:     at androidx.fragment.app.FragmentController.execPendingActions(FragmentController.java:446)
System.err:     at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:459)
System.err:     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1454)
System.err:     at android.app.Activity.performResume(Activity.java:8103)
System.err:     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4401)

Error on IOS (here is written: No such payment_intent, very strange, I just created)

CONSOLE LOG file: src/app/pages/pages/reservation-details/reservation-details.component.ts:115:16: pm_1GeholEZ7PcnewTdwyxxx pi_1GehomEZ7PcnewTdT4xxx_secret_U7naXFios3RITrcqgdiCxxx
CONSOLE LOG file: node_modules/@nativescript/core/ui/frame/frame-common.js:581:0: topmost() is deprecated. Use Frame.topmost() instead.
CONSOLE LOG file: src/app/pages/pages/reservation-details/reservation-details.component.ts:123:20: piError Error: Error Domain=com.stripe.lib Code=50 "No such payment_intent: pi_1GehomEZ7PcnewTdT4xxx" UserInfo={com.stripe.lib:ErrorMessageKey=No such payment_intent: pi_1GehomEZ7PcnewTdT4xxx, com.stripe.lib:StripeErrorCodeKey=resource_missing, com.stripe.lib:StripeErrorTypeKey=invalid_request_error, com.stripe.lib:ErrorParameterKey=intent, NSLocalizedDescription=No such payment_intent: pi_1GehomEZ7PcnewTdT4xxx}
CONSOLE LOG file: src/app/pages/pages/reservation-details/reservation-details.component.ts:124:20: piError message Error Domain=com.stripe.lib Code=50 "No such payment_intent: pi_1GehomEZ7PcnewTdT4xxx" UserInfo={com.stripe.lib:ErrorMessageKey=No such payment_intent: pi_1GehomEZ7PcnewTdT4xxx, com.stripe.lib:StripeErrorCodeKey=resource_missing, com.stripe.lib:StripeErrorTypeKey=invalid_request_error, com.stripe.lib:ErrorParameterKey=intent, NSLocalizedDescription=No such payment_intent: pi_1GehomEZ7PcnewTdT4xxx}
CONSOLE LOG file:///app/6.js:494:32: piError stack ZoneAwareError(file: node_modules/@nativescript/angular/zone-js/dist/zone-nativescript.js:1298:0)
at file: node_modules/nativescript-stripe/stripe.ios.js:116:0
at UIApplicationMain([native code])
at run(file: node_modules/@nativescript/core/application/application.ios.js:312:0)
at file: node_modules/@nativescript/angular/platform-common.js:210:0
at file: node_modules/@nativescript/angular/platform-common.js:111:0
at file: node_modules/@nativescript/angular/platform-common.js:91:0
at file:///app/bundle.js:8222:145
at ./main.ts(file:///app/bundle.js:8227:34)
at __webpack_require__(file: src/webpack/bootstrap:750:0)
at checkDeferredModules(file: src/webpack/bootstrap:43:0)
at webpackJsonpCallback(file: src/webpack/bootstrap:30:0)
at anonymous(file:///app/bundle.js:2:61)
at evaluate([native code])
at moduleEvaluation([native code])
at [native code]
at asyncFunctionResume([native code])
at [native code]
at promiseReactionJob([native code])
RobertGardner commented 4 years ago

In my own app, I don't work with cards that require multi-step interactions, so I'm just guessing from looking at the Stripe documentation.

It appears that you are handling the error return from the server incorrectly. The Stripe documentation says that for the error authentication_required "the customer should contact their card issuer for more information, or retry with a different form of payment" and "this means that a customer should retry the payment using 3D Secure authentication".

Since paymentIntents.create on the server returned an error, I suspect the PaymentIntent did not actually get created, which is why you are getting that error on the client.

Your description makes it sound like you are trying to collect payment later (such as when you ship a physical good). This Stripe page discusses that use case.

Stanteq commented 4 years ago

@RobertGardner, first thx for the hints and help even if the issue (apparently) is not generated by the plugin.

The only part that I figured out is that paymentIntents.create get created but under connect account and not under stripe platform. Now I pass also the connect account to authenticate the card but i get another error:

The provided PaymentMethod was previously used with a PaymentIntent without Customer attachment, shared with a connected account without Customer attachment, or was detached from a Customer. It may not be used again. To use a PaymentMethod multiple times, you must attach it to a Customer first.

because I just cloning the paymentMethod of the client which is saved under stripe platform.

RobertGardner commented 4 years ago

As I understand the documentation (though I haven't done this myself), you want to ask the customer to authenticate in the response to confirmSetupIntent. If the card still needs (or again needs?) authentication when charging the card, you need to create a "recovery flow" where you can ask the customer to re-authenticate. This is described in step 5 of the Setup future payments document. In the "Let your customer try again" section, it gives instructions on what to do if payment failed because it requires authentication. This seems to fit your scenario, based on the error message you are getting.

Stanteq commented 4 years ago

The answer is here

It needs to re-clone again the failed payment method and pass it to the frontend for authentication

This one can be closed because is a off topic (has nothing with the plugin)

Thanks anyway.