Skip to content

[Bug] R8/ProGuard strips essential methods in ch.datatrans.payment package causing NullPointerException (v3.10.0) #36

@Kevinrob

Description

@Kevinrob

Description:

We are experiencing a critical crash in our release builds when using the Datatrans Android SDK (version 3.10.0). The crash occurs during the payment flow, specifically leading to an AuthenticationException which is then followed by a NullPointerException because essential methods in internal classes are being stripped by R8.

Environment:

  • Datatrans Android SDK version: 3.10.0
  • R8/ProGuard enabled: Yes
  • Android Gradle Plugin: 9.1.0
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
    getDefaultProguardFile("proguard-android-optimize.txt"),
    "proguard-rules.pro",
)

Steps to Reproduce:

  1. Integrate Datatrans SDK 3.10.0.
  2. Build the app in release mode with R8 shrinking enabled.
  3. Start a payment transaction.
  4. The app crashes when the SDK attempts to access internal state.

Stacktrace:

1 ch.datatrans.payment.exception.AuthenticationException: Authentication Error
2     at ch.datatrans.payment.C7$$ExternalSyntheticLambda5.invoke(SourceFile:444)
3     at ch.datatrans.payment.O1$$ExternalSyntheticLambda2.onClick(SourceFile:272)
4     ...
5 Caused by: java.lang.NullPointerException
6     at ch.datatrans.payment.M2.e(Unknown Source:1)
7     at ch.datatrans.payment.E3$$ExternalSyntheticLambda0.invoke(SourceFile:39)
8     at ch.datatrans.payment.Eb.invokeSuspend(SourceFile:1893)
9     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:8)

10 at kotlinx.coroutines.DispatchedTask.run(SourceFile:120)

Analysis:

Using proguard-removed.txt and -whyareyoukeeping class ch.datatrans.payment.M2 { void e(); }, we identified that while the class ch.datatrans.payment.M2 is kept, R8 is aggressively stripping its methods because it doesn't detect direct usage. Specifically, the method e() in ch.datatrans.payment.M2 is removed:

Nothing is keeping void ch.datatrans.payment.M2.e()

This leads to a NullPointerException when the SDK's internal coroutines or lambdas try to access this method at runtime. It seems the SDK relies on reflection that isn't fully covered by the current consumer ProGuard rules.

Current Workaround:

We had to add the following broad rule to our proguard-rules.pro to stop the crashes:

keep class ch.datatrans.payment.** { *; }

Expected Behavior:

The SDK should provide comprehensive consumer ProGuard rules to ensure that its internal orchestration logic and data models (especially those accessed via reflection/lambdas) are not stripped or broken by R8.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions