datatrans / android-sdk

Accept payments on your Android apps: Our mobile SDKs support your entire payment and registration process and simplify the integration of any payment method in your mobile apps.
Other
6 stars 0 forks source link

[3.0.2] Crash with GooglePayCustomerInfo & R8 #10

Closed Kevinrob closed 1 year ago

Kevinrob commented 1 year ago

We have a crash with the last version (3.0.2) when we pay with Google Pay with R8 enabled.

GSON can't find the no-arg constructor because it was shrinked by R8. The workaround is to add this to our proguard configuration.

-keep class ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo {
  public <init>(...);
}

Can you please add this rule to the SDK?

Stacktrace:

Fatal Exception: java.lang.RuntimeException: Unable to create instance of class ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo. Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args constructor may fix this problem.
       at com.google.gson.internal.ConstructorConstructor$16.construct(ConstructorConstructor.java:275)
       at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:211)
       at com.google.gson.Gson.fromJson(Gson.java:991)
       at com.google.gson.Gson.fromJson(Gson.java:956)
       at com.google.gson.Gson.fromJson(Gson.java:905)
       at com.google.gson.Gson.fromJson(Gson.java:876)
       at a.a.a.p.b.e.a(SourceFile:490)
       at a.a.a.p.b.c$b.invokeSuspend(SourceFile:7)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.internal.DispatchedContinuation.resumeUndispatchedWith(DispatchedContinuation.java:254)
       at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:280)
       at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
       at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
       at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
       at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
       at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
       at kotlinx.coroutines.BuildersKt.launch(Builders.kt:1)
       at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
       at kotlinx.coroutines.BuildersKt.launch$default(Builders.kt:1)
       at a.a.a.p.b.c.onActivityResult(SourceFile:1)
       at ch.datatrans.payment.bottomsheet.TransactionSheetActivity.onActivityResult(SourceFile:19)
       at android.app.Activity.dispatchActivityResult(Activity.java:8664)
       at android.app.ActivityThread.deliverResults(ActivityThread.java:5328)
       at android.app.ActivityThread.handleSendResult(ActivityThread.java:5374)
       at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:67)
       at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:138)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2303)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:7884)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
luiscosta commented 1 year ago

Hello,

Can you tell me more about this problem?

The thing is, we are using Android Gradle Plugin 4.1.3 with minify enabled and starting on Android Gradle Plugin 3.4.0 R8 is enabled by default.

Are you using the full mode or any other setting?

Kevinrob commented 1 year ago

Hello @luiscosta

We use Gradle 8.1.1 with AGP 8.0.2. Yes, R8 full mode is enabled by default.

As no-arg constructor of GooglePayCustomerInfo is only use by GSON with reflection, R8 don't see it and you need to explicitly tell him to keep it with the rule:

-keep class ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo {
  public <init>(...);
}

You can add this to the SDK Proguard file rules.


For reference, this is all the thing that R8 remove from GooglePayCustomerInfo when we build:

ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo:
    private java.lang.Integer a
    private java.lang.Integer b
    private ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData c
    private ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress d
    private java.lang.String e
    public void <init>()
    public void <init>(java.lang.Integer,java.lang.Integer,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress,java.lang.String)
    public synthetic void <init>(java.lang.Integer,java.lang.Integer,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress,java.lang.String,int,kotlin.jvm.internal.DefaultConstructorMarker)
    public final java.lang.Integer component1()
    public final java.lang.Integer component2()
    public final ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData component3()
    public final ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress component4()
    public final java.lang.String component5()
    public final ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo copy(java.lang.Integer,java.lang.Integer,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress,java.lang.String)
    public static synthetic ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo copy$default(ch.datatrans.payment.paymentmethods.GooglePayCustomerInfo,java.lang.Integer,java.lang.Integer,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData,ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress,java.lang.String,int,java.lang.Object)
    public boolean equals(java.lang.Object)
    public final java.lang.Integer getApiVersion()
    public final java.lang.Integer getApiVersionMinor()
    public final java.lang.String getEmail()
    public final ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData getPaymentMethodData()
    public final ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress getShippingAddress()
    public int hashCode()
    public final void setApiVersion(java.lang.Integer)
    public final void setApiVersionMinor(java.lang.Integer)
    public final void setEmail(java.lang.String)
    public final void setPaymentMethodData(ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoPaymentMethodData)
    public final void setShippingAddress(ch.datatrans.payment.paymentmethods.GooglePayCustomerInfoShippingAddress)
    public java.lang.String toString()

This is because we don't call any of this. It's OK except for the no-arg constructor public void <init>() that is used by you with GSON.

luiscosta commented 1 year ago

Hey again @Kevinrob,

Thanks for your reply!

This is going to be added on the next release.

luiscosta commented 1 year ago

Hello @Kevinrob,

This rule was added in version 3.1.0.

Kevinrob commented 1 year ago

Thank you! It's works 👌