stripe-android
                                
                                 stripe-android copied to clipboard
                                
                                    stripe-android copied to clipboard
                            
                            
                            
                        [BUG] IllegalStateException when launching 3DS 1 challenge flow
Summary
We're facing this random crash when testing the 3DS 1 challenge flow (WebView) in testing mode, using the test credit card 4000 0000 0000 3063:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mudflap.mudflap.staging/com.stripe.android.payments.StripeBrowserLauncherActivity}: java.lang.IllegalStateException: PaymentConfiguration was not initialized. Call PaymentConfiguration.init().
It's not consistently replicable. For example, it only occurs in 3 out of 15 attempts, which is weird. We don't get this crash when testing the other challenge flows (3DS2).
Code to reproduce
We're setting up the 3DS settings according to the docs.
paymentLauncher = PaymentLauncher.create(
            fragment = this,
            publishableKey = getString(R.string.stripe_publishable_key)
        ) { paymentResult ->
            log("PaymentIntent confirmation result = $paymentResult")
        }
//
val uiCustomization = PaymentAuthConfig.Stripe3ds2UiCustomization.Builder()
      .setLabelCustomization(
          PaymentAuthConfig.Stripe3ds2LabelCustomization.Builder()
              .setTextFontSize(12)
              .build()
      )
      .build()
  PaymentAuthConfig.init(
      PaymentAuthConfig.Builder()
          .set3ds2Config(
              PaymentAuthConfig.Stripe3ds2Config.Builder()
                  .setTimeout(5)
                  .setUiCustomization(uiCustomization)
                  .build()
          )
          .build()
  )
The PaymentLauncher instance is used to confirm the PaymentIntent, and this action ultimately triggers the challenge flow.
paymentLauncher.confirm(
            ConfirmPaymentIntentParams.createWithPaymentMethodId(
                paymentMethodId = params.paymentToken,
                clientSecret = params.clientSecret
            )
        )
Android version
Android 11, 12 and 13
Installation method
Gradle
Dependency Versions
stripe-android: 18.2.0 Android Gradle Plugin: 7.4.2 Gradle: 7.5
SDK classes
StripeBrowserLauncherActivity
Hi @hjdealba96, this issue sounds like a threading problem assuming you have properly called PaymentConfiguration.init() from your Application.
Can you share your code on how you've called PaymentConfiguration.init()?
Hey @jameswoo-stripe, we're not explicitly calling PaymentConfiguration.init() either from theApplication or the screen where we prompt the 3ds challenge. Although there are some other places in our app where we call this method, they are screens where we use the legacy PaymentMethodsActivityStarter to allow users to attach their credit cards, it's called just before CustomerSession.initCustomerSession() as shown in the example:
fragment.context?.let { context ->
            PaymentConfiguration.init(context, fragment.getString(R.string.stripe_publishable_key))
            CustomerSession.initCustomerSession(
                context,
                PaymentCredentialsToEphemeralKeyProvider.mapFrom(credentials)
            )
        }
        PaymentMethodsActivityStarter(fragment).startForResult(
            PaymentMethodsActivityStarter.Args.Builder().apply {
                setInitialPaymentMethodId(initialPaymentMethodId)
            }.build()
        )
Should PaymentConfiguration.init() be called before the 3DS initialization stuff? If so, how has the 3DS stuff been working without this call on our end?
@hjdealba96 Few things here:
- We recommend getting off of 3DS1. Major card brands no longer support 3D Secure 1. To continue using 3D Secure, adopt the Payment Intents API or Setup Intents API. This integration:
- Leverages benefits from Dynamic 3D Secure.
- Supports 3D Secure 2.
- Complies with Strong Customer Authentication regulation in Europe.
 
- If possible, use PaymentSheetfor 3DS2 PaymentIntent/SetupIntent support out of the box.
- You have a couple of options for calling PaymentConfiguration.init()- You call it when your application initializes. Since the publishable key can be visible, it is safe to store it in your app.
- Otherwise, if you have a use case where you need to retrieve your publishable key from your backend, you should do that when the Activityis created. See the example here. The example calls the backend in theActivity'sonCreatemethod.
 
@jameswoo-stripe Thanks for replying. I want to point some things out according to your response:
- Actually, it's the Stripe SDK that makes the decision of what challenge flow should be displayed to the user. We're just testing this flow in testing mode to double-check how our app behaves under this scenario. We know that this version of the challenge flow might not be prompted on production very often, since as you said major brand cards no longer support 3DS 1, but we considered it was worth checking it anyway.
- We evaluated adopting PaymentSheetin our checkout flow, but I think it doesn't fit our use case since our users enter the specific amount of products they want to purchase on a different interface (something like a "cashiers" web page)
- We'll try setting up this call in the Application.onCreate()method to ensure it's properly initialized before any component that requires it attempts to access it. Hope this change can help to mitigate this exception.
Thanks for your assistance on this.