Flutter In-App Payments crashes on open in Production build

After our latest release users found that the integrated Flutter In-app Payments portion of the app would cause the app to crash upon attempting to open the card form.

The error pulled from a production install:

Showing Pixel 3 logs:
E/AndroidRuntime(28191): FATAL EXCEPTION: main
E/AndroidRuntime(28191): Process: com.app.name, PID: 28191
E/AndroidRuntime(28191): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.app.name/sqip.internal.CardEntryActivity}: java.lang.RuntimeException: Failed to find the generated JsonAdapter class for class sqip.internal.nonce.j
E/AndroidRuntime(28191):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3639)
E/AndroidRuntime(28191):        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3796)
E/AndroidRuntime(28191):        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
E/AndroidRuntime(28191):        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
E/AndroidRuntime(28191):        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
E/AndroidRuntime(28191):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
E/AndroidRuntime(28191):        at android.os.Handler.dispatchMessage(Handler.java:106)
E/AndroidRuntime(28191):        at android.os.Looper.loopOnce(Looper.java:201)
E/AndroidRuntime(28191):        at android.os.Looper.loop(Looper.java:288)
E/AndroidRuntime(28191):        at android.app.ActivityThread.main(ActivityThread.java:7842)
E/AndroidRuntime(28191):        at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(28191):        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/AndroidRuntime(28191):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
E/AndroidRuntime(28191): Caused by: java.lang.RuntimeException: Failed to find the generated JsonAdapter class for class sqip.internal.nonce.j
E/AndroidRuntime(28191):        at com.squareup.moshi.v.b.d(Unknown Source:271)
E/AndroidRuntime(28191):        at com.squareup.moshi.t$c.a(Unknown Source:145)
E/AndroidRuntime(28191):        at com.squareup.moshi.s.f(Unknown Source:80)
E/AndroidRuntime(28191):        at com.squareup.moshi.s.e(Unknown Source:1)
E/AndroidRuntime(28191):        at com.squareup.moshi.s.c(Unknown Source:2)
E/AndroidRuntime(28191):        at sqip.internal.c1.b(Unknown Source:7)
E/AndroidRuntime(28191):        at sqip.internal.e1.b(Unknown Source:2)
E/AndroidRuntime(28191):        at sqip.internal.e1.c(Unknown Source:8)
E/AndroidRuntime(28191):        at sqip.internal.e1.get(Unknown Source:0)
E/AndroidRuntime(28191):        at dagger.internal.a.get(Unknown Source:15)
E/AndroidRuntime(28191):        at sqip.internal.nonce.e0.b(Unknown Source:2)
E/AndroidRuntime(28191):        at sqip.internal.nonce.e0.get(Unknown Source:0)
E/AndroidRuntime(28191):        at dagger.internal.a.get(Unknown Source:15)
E/AndroidRuntime(28191):        at sqip.internal.nonce.b.c(Unknown Source:10)
E/AndroidRuntime(28191):        at sqip.internal.nonce.b.get(Unknown Source:0)
E/AndroidRuntime(28191):        at dagger.internal.a.get(Unknown Source:15)
E/AndroidRuntime(28191):        at sqip.internal.nonce.t.a(Unknown Source:2)
E/AndroidRuntime(28191):        at sqip.internal.BaseCardEntryActivity.onCreate(Unknown Source:163)
E/AndroidRuntime(28191):        at sqip.internal.CardEntryActivity.onCreate(Unknown Source:0)
E/AndroidRuntime(28191):        at android.app.Activity.performCreate(Activity.java:8051)
E/AndroidRuntime(28191):        at android.app.Activity.performCreate(Activity.java:8031)
E/AndroidRuntime(28191):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
E/AndroidRuntime(28191):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3612)
E/AndroidRuntime(28191):        ... 12 more
E/AndroidRuntime(28191): Caused by: java.lang.ClassNotFoundException: sqip.internal.nonce.jJsonAdapter
E/AndroidRuntime(28191):        at java.lang.Class.classForName(Native Method)
E/AndroidRuntime(28191):        at java.lang.Class.forName(Class.java:454)
E/AndroidRuntime(28191):        at com.squareup.moshi.v.b.d(Unknown Source:34)
E/AndroidRuntime(28191):        ... 34 more
E/AndroidRuntime(28191): Caused by: java.lang.ClassNotFoundException: sqip.internal.nonce.jJsonAdapter
E/AndroidRuntime(28191):        ... 37 more

This feature previously worked as expected. When running locally in IDE(vscode) there is no problem. This only seems to be an issue for Android devices, iOS users have not reported this problem. Once the app has been built as an app bundle and distributed via Google Play this problem shows. At this point I’m not sure what direction to go in. It feels like its an issue where Google is stripping out necessary files required by Moshi, but I am not sure what kind of changes I need to make. Help would be appreciated.

square_in_app_payments: ^1.7.2

Flutter Code:

_addCard() async {
    await _bloc.setupInAppPayments();
    await InAppPayments.startCardEntryFlow(
      onCardNonceRequestSuccess: (iap.CardDetails details) async {
        CreditCard cc = await _bloc.createCard(details.nonce);

        InAppPayments.completeCardEntry(onCardEntryComplete: () async {
          setState(() {
            _customer.cards.add(cc);
          });
        });
      },
      onCardEntryCancel: () {},
    );
  }

build.gradle

    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

ProGuard.pro


## Flutter wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }
-dontwarn io.flutter.embedding.**

## Gson rules
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

## flutter_local_notification plugin rules
-keep class com.dexterous.** { *; }

:wave: What’s your application Id that your app is using?

App Id: sq0idp-2HH-B2p7aGZdn7_-UUNJ-g

I have gotten it to work correctly.

My solution

Updated to 1.7.3
square_in_app_payments: ^1.7.3

Add to gradle.properties - This was required as 1.7.3 prevented me from building all together
android.jetifier.blacklist=moshi-1.13.0

Added to build.gradle - Originally I only had the signingConfig line

    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            useProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

Added to proguard.pro
-keep class sqip.** { *; }

In the end I don’t know what changed between the two versions of my app. Both used 1.7.1 but the later version didn’t work. But the above seems to have resolved the issue for now.

:wave: Glad to hear you got it to work. :slightly_smiling_face: