Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Crash when calling PaymentLauncher.confirm(params:) #4739

Closed
reiaz-gafar opened this issue Mar 21, 2022 · 25 comments
Closed

[BUG] Crash when calling PaymentLauncher.confirm(params:) #4739

reiaz-gafar opened this issue Mar 21, 2022 · 25 comments
Labels
Projects

Comments

@reiaz-gafar
Copy link

reiaz-gafar commented Mar 21, 2022

Summary

When trying to use the PaymentLauncher to confirm a payment, it crashes the app.

Code to reproduce

paymentLauncher.confirm(confirmParams)

Android version

Using Android 12, happening on Stripe SDK versions 17+

Impacted devices

Happens on all devices tried on.

Installation method

Gradle

Dependency Versions

kotlin: 1.6.10
stripe-android: 19.3.0
Android Gradle Plugin: 7.1.2
Gradle: 7.3.3

SDK classes

PaymentLauncher

Video

Other information

The payment intent in Stripe dashboard indicates that the payment intent was successfully confirmed, but we crash before we receive the callback.

Stack trace:

02-28 11:54:28.177 7422 7422 E AndroidRuntime: FATAL EXCEPTION: main
02-28 11:54:28.177 7422 7422 E AndroidRuntime: Process: com.lightspeed.xseries.dev, PID: 7422
02-28 11:54:28.177 7422 7422 E AndroidRuntime: java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = kotlinx.coroutines.JobCancellationException)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Parcel.writeSerializable(Parcel.java:1850)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.stripe.android.payments.paymentlauncher.PaymentResult$Failed.writeToParcel(Unknown Source:10)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Parcel.writeParcelable(Parcel.java:1818)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Parcel.writeValue(Parcel.java:1724)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Parcel.writeArrayMapInternal(Parcel.java:945)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Bundle.writeToParcel(Bundle.java:1253)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Parcel.writeBundle(Parcel.java:1014)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.content.Intent.writeToParcel(Intent.java:11155)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.app.IActivityTaskManager$Stub$Proxy.finishActivity(IActivityTaskManager.java:4874)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.app.Activity.finish(Activity.java:6285)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.app.Activity.finish(Activity.java:6309)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.stripe.android.payments.paymentlauncher.PaymentLauncherConfirmationActivity.finish(PaymentLauncherConfirmationActivity.kt:89)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.stripe.android.payments.paymentlauncher.PaymentLauncherConfirmationActivity.finishWithResult(PaymentLauncherConfirmationActivity.kt:108)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.stripe.android.payments.paymentlauncher.PaymentLauncherConfirmationActivity.$r8$lambda$CfpgStE7oOr92cQhCPbhChZ-Eio(Unknown Source:0)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.stripe.android.payments.paymentlauncher.PaymentLauncherConfirmationActivity$$ExternalSyntheticLambda0.onChanged(Unknown Source:4)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:151)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at androidx.lifecycle.LiveData.setValue(LiveData.java:309)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at androidx.lifecycle.LiveData$1.run(LiveData.java:93)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Handler.handleCallback(Handler.java:883)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:100)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:237)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.app.ActivityThread.main(ActivityThread.java:8107)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
02-28 11:54:28.177 7422 7422 E AndroidRuntime: Caused by: java.io.NotSerializableException: kotlinx.coroutines.SupervisorJobImpl
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
02-28 11:54:28.177 7422 7422 E AndroidRuntime:    at android.os.Parcel.writeSerializable(Parcel.java:1845)
@jameswoo-stripe jameswoo-stripe added this to To do in Tracking via automation Mar 21, 2022
@jameswoo-stripe
Copy link
Contributor

Hi @reiaz-gafar, thank you for reporting this issue. Can you provide a little more information:

  1. on what your confirmParams looks like? For example, which payment method you are using
  2. how you constructed paymentLauncher

@reiaz-gafar
Copy link
Author

Hi @jameswoo-stripe.

  1. ConfirmPaymentIntentParams(paymentMethodCreateParams=PaymentMethodCreateParams(type=card, card=Card(number=4242424242424242, expiryMonth=2, expiryYear=2042, cvc=424, token=null, attribution=[CardInputView]), ideal=null, fpx=null, sepaDebit=null, auBecsDebit=null, bacsDebit=null, sofort=null, upi=null, netbanking=null, billingDetails=BillingDetails(address=Address(city=null, country=null, line1=null, line2=null, postalCode=, state=null), email=null, name=null, phone=null), metadata=null, productUsage=[], overrideParamMap=null), paymentMethodId=null, sourceParams=null, sourceId=null, clientSecret=pi_xxxxxxxxxxxxxxxxxx_secret_xxxxxxxxxxxxxxxxxx, returnUrl=null, savePaymentMethod=null, useStripeSdk=false, paymentMethodOptions=null, mandateId=null, mandateData=null, setupFutureUsage=null, shipping=null, receiptEmail=null) (I x'd out the client secret after I pasted here)

  2. Payment launcher is constructed using a fragment like so: PaymentLauncher.Companion.create(fragment, publishableKey, stripeAccountId, callback)

@jameswoo-stripe
Copy link
Contributor

@reiaz-gafar, thanks for the information. Been trying to reproduce this issue without any success, it is hard to find out why the coroutine is cancelling without more information.

I can propose a bandaid solution that will just wrap the JobCancellationException into a Throwable, so that when we try to serialize it, it won't crash.

In the meantime, it would be helpful to understand how you are using PaymentLauncher. Anything will help, even pseudocode to help debug the issue, please be as specific as you can. Questions off the top of my head:

  • Where in the fragment are you creating the PaymentLauncher
  • How are you calling paymentLauncher.confirm(confirmParams)
  • Is your app doing something that might cause a coroutine cancellation?
    • The flow we expect: PaymentLauncher created before fragment is created (can be a lateinit var) > create payment intent > when the payment intent is created get the client secret > use PaymentLauncher to confirm payment > callback should be called with some PaymentResult

@reiaz-gafar
Copy link
Author

reiaz-gafar commented Mar 24, 2022

Thanks for looking into this @jameswoo-stripe. I've created a stripped down sample app where I'm still seeing the issue here: https://github.com/reiaz-gafar/stripe-manual-entry-crash.

It still has the same crash location

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.stripemanualentrysampleapp, PID: 3259
    java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = kotlinx.coroutines.JobCancellationException)

As for our main app where it's crashing to answer your questions:

  1. The PaymentLauncher is being created in the onCreate of the fragment. The launcher itself is being held as a property of the fragment's view model so it's created like so fragmentViewModel.createPaymentLauncher(this). We pass in a reference to the fragment so the view model has the information it needs to create the launcher. This is the function and property in the view model. stripeManualEntry is a service that wraps the SDK calls.
    private var paymentLauncher: PaymentLauncher? = null
    fun createPaymentLauncher(fragment: Fragment) {
        paymentLauncher = stripeManualEntry.createPaymentLauncher(fragment, ::onPaymentResult)
    }
  1. We end up calling paymentLauncher.confirm when the user taps the pay button which we then grab the information from the card widget like so:
        cardInputWidget.paymentMethodCreateParams?.let { paymentMethodCreateParams ->
            applicationScope().launch {
                stripeManualEntry.confirmPayment(paymentMethodCreateParams, paymentIntentService.clientSecret.get(), paymentLauncher!!)
            }
        }
  1. I can't see anything that would cause a cancellation on our end. We're expecting the callback from the payment launcher that we passed in here createPaymentLauncher(fragment, ::onPaymentResult) but the app crashes before we enter the onPaymentResult function. I'm pretty sure that we're following those suggested steps that you listed.

@reiaz-gafar
Copy link
Author

There may also be a reason to believe that Locale has something to do with this, is there something in the SDK that uses the locale to construct anything in this flow?

The reason I'm mentioning this is that we have a colleague in the UK that has the exact same code and does NOT crash, but myself and others in the New York City region ARE crashing.

@jameswoo-stripe
Copy link
Contributor

jameswoo-stripe commented Mar 24, 2022

Hi @reiaz-gafar thanks so much for the sample app. I tried it out and was not able to reproduce the issue. I am based in the Seattle region and I am not able to get a crash. I tested multiple emulators, upgraded my kotlin version, but no luck so far.

My machine:

  • macOS Monterey 12.1
  • Macbook Pro 16-inch, 2019

Android emulator:

  • API level: 31 (Android 12.0)
  • image.sysdir.1: system-images/android-31/google_apis_playstore/x86_64

Here are some questions I have:

  1. Are you running on a M1 machine?
  2. Can you provide details on your emulator or device that you are using?
  3. Are you using a VPN?
  4. Is the request timing out, or are you seeing the crash pretty much right away?

@reiaz-gafar
Copy link
Author

@jameswoo-stripe Thanks for continuing to look at this.

To answer your questions:

  1. My machine is:
  • Running macOS Monterey 12.0

  • MacBook Pro 16 inch, 2021

  • M1 Pro Apple Silicon chip

  1. Android devices - crashes on both 100% of the time:
  • Running it on a physical Samsung Galaxy Tab A, Model SM-5510, Android Version 10

  • Running on an emulator made inside Android Studio, Pixel C, Api 32

  1. No VPN being used

  2. The request does not time out - it actually successfully confirms the payment intent, verfied on the dashboard, but it crashes before we hit the callback.

image

Here's a video of the crash that I'm seeing in the sample app I posted earlier:

crash.mov

@jameswoo-stripe
Copy link
Contributor

@reiaz-gafar Thank you so much for the detailed responses! This was extremely valuable information and we have found the issue. This issue seems to happen on landscape mode and not portrait mode. There is a configuration change which causes the JobCancellationException being thrown. We will take a look into getting a fix out, thanks again!

@reiaz-gafar
Copy link
Author

@jameswoo-stripe Awesome, that's great to hear! Thanks so much for taking the time to look into this.

Any tentative idea on how long it will take to release?

@jameswoo-stripe
Copy link
Contributor

@reiaz-gafar You can subscribe to this PR: #4776. Once merged we can discuss about releasing a patch fix. In the meantime, can you work on this using portrait mode (e.g. using a phone instead of a tablet)?

@reiaz-gafar
Copy link
Author

@jameswoo-stripe Thanks for raising that PR, I have suscribed. Unfortunately our app is locked to landscape mode so we are blocked until this patch is released. Thanks again for looking into this and communicating well!

@jameswoo-stripe
Copy link
Contributor

Hi @reiaz-gafar, the fix is merged and we are scheduling a release for April 4. Once released, feel free to update your dependency. Please let us know if you run into more issues.

@reiaz-gafar
Copy link
Author

@jameswoo-stripe Amazing, thanks so much for the quick resolution!

@reiaz-gafar
Copy link
Author

@jameswoo-stripe Tried out the new release. Unfortunately I'm still getting a crash, not the same one as before though.

2022-04-05 11:07:51.317 3990-4536/? E/Parcel: Class not found when unmarshalling: com.stripe.android.payments.paymentlauncher.PaymentLauncherContract$Args$IntentConfirmationArgs
    java.lang.ClassNotFoundException: com.stripe.android.payments.paymentlauncher.PaymentLauncherContract$Args$IntentConfirmationArgs
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:454)
        at android.os.Parcel.readParcelableCreator(Parcel.java:3031)
        at android.os.Parcel.readParcelable(Parcel.java:2981)
        at android.os.Parcel.readValue(Parcel.java:2883)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:3261)
        at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292)
        at android.os.BaseBundle.unparcel(BaseBundle.java:236)
        at android.os.BaseBundle.getString(BaseBundle.java:1160)
        at android.content.Intent.getStringExtra(Intent.java:8548)
        at com.android.server.wm.ActivityStarter.startActivity(ActivityStarter.java:790)
        at com.android.server.wm.ActivityStarter.startActivity(ActivityStarter.java:694)
        at com.android.server.wm.ActivityStarter.startActivityMayWait(ActivityStarter.java:1932)
        at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:625)
        at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1707)
        at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1603)
        at com.android.server.wm.ActivityTaskManagerService.startActivity(ActivityTaskManagerService.java:1557)
        at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1690)
        at android.os.Binder.execTransactInternal(Binder.java:1056)
        at android.os.Binder.execTransact(Binder.java:1029)
     Caused by: java.lang.ClassNotFoundException: com.stripe.android.payments.paymentlauncher.PaymentLauncherContract$Args$IntentConfirmationArgs
        at java.lang.Class.classForName(Native Method) 
        at java.lang.Class.forName(Class.java:454) 
        at android.os.Parcel.readParcelableCreator(Parcel.java:3031) 
        at android.os.Parcel.readParcelable(Parcel.java:2981) 
        at android.os.Parcel.readValue(Parcel.java:2883) 
        at android.os.Parcel.readArrayMapInternal(Parcel.java:3261) 
        at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:292) 
        at android.os.BaseBundle.unparcel(BaseBundle.java:236) 
        at android.os.BaseBundle.getString(BaseBundle.java:1160) 
        at android.content.Intent.getStringExtra(Intent.java:8548) 
        at com.android.server.wm.ActivityStarter.startActivity(ActivityStarter.java:790) 
        at com.android.server.wm.ActivityStarter.startActivity(ActivityStarter.java:694) 
        at com.android.server.wm.ActivityStarter.startActivityMayWait(ActivityStarter.java:1932) 
        at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:625) 
        at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1707) 
        at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1603) 
        at com.android.server.wm.ActivityTaskManagerService.startActivity(ActivityTaskManagerService.java:1557) 
        at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1690) 
        at android.os.Binder.execTransactInternal(Binder.java:1056) 
        at android.os.Binder.execTransact(Binder.java:1029) 

The payment is still succesfully confirmed on the dashboard as well.

I tried the same exact app in portrait mode and it works fine. Any ideas on why it's still happening?

@jameswoo-stripe
Copy link
Contributor

jameswoo-stripe commented Apr 5, 2022

@reiaz-gafar I have tried reproducing the issue you are seeing with a tablet in landscape mode in both our example app and the app that you posted previously. Unfortunately, I am unable to see the issue you are seeing. Have you tried invalidating and restarting Android Studio, clearing the gradle cache, cleaning and rebuilding?

20.0.0 does include some breaking changes, but I can't see anything that would have changed with IntentConfirmationArgs . See the migration guide, just in case.

@reiaz-gafar
Copy link
Author

@jameswoo-stripe Let me do a bit more investigation and get back, it can totally be something on our side.

@michelleb-stripe michelleb-stripe moved this from To do to Waiting on Asker in Tracking Apr 6, 2022
@anandkumarnyc
Copy link

Hi @jameswoo-stripe, I see below code in PaymentLauncherConfirmationActivity.kt which changes activity orientation to portrait mode. This might explain why we have this issue.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
            // In Oreo, Activities where `android:windowIsTranslucent=true` can't request
            // orientation. See https://stackoverflow.com/a/50832408/11103900
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }

@keithmacklin
Copy link

Hi @jameswoo-stripe I tried the latest SDK in portrait and it does crash the application for me too. However, if I run it on an emulator that is specifically SDK 26 (Android Oreo), it works fine. That would indicate to me that it is this line that is causing the crash.

What would be the ramifications if you were to just remove the orientation line completely?
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT

@reiaz-gafar
Copy link
Author

Hi @jameswoo-stripe, my teammates posted some of their findings above, what do you think of them?

@jameswoo-stripe
Copy link
Contributor

@anandkumarnyc, @keithmacklin, @reiaz-gafar, thanks for the follow up. I am consulting with the team to see if we can remove the force portrait mode.

@jameswoo-stripe
Copy link
Contributor

@anandkumarnyc @keithmacklin @reiaz-gafar here is a potential workaround PR up: #4855

@jameswoo-stripe
Copy link
Contributor

@anandkumarnyc @keithmacklin @reiaz-gafar hi folks, #4855 was merged. Please be on the lookout for a release this Monday.

@jameswoo-stripe
Copy link
Contributor

@reiaz-gafar Please update to 20.1.0 and let us know if this fixes your issue.

@reiaz-gafar
Copy link
Author

@jameswoo-stripe It's looking pretty good now, thanks! Going to do a bit more testing but everything seems to be in order 🤞

@jameswoo-stripe
Copy link
Contributor

Great, thank you for the update! Feel free to open a new issue if you find one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Development

No branches or pull requests

4 participants