Skip to content

Preserve entitlements in Android subscription status - #13

Open
bilck wants to merge 5 commits into
superwall:mainfrom
bilck:fix/serialize-android-subscription-entitlements
Open

bilck wants to merge 5 commits into
superwall:mainfrom
bilck:fix/serialize-android-subscription-entitlements

Conversation

@bilck

@bilck bilck commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The Android bridge serializes SubscriptionStatus.Active as { "type": "active" } and drops
status.entitlements, even though the setter accepts the entitlement array. Reading a status back after
writing Active [premium] therefore returns Active [].

This is still the behaviour on main at 0.3.0:

fun serializeSubscriptionStatus(status: SubscriptionStatus) = JSONObject().apply {
    when (status) {
        is SubscriptionStatus.Active -> put("type", "active")   // entitlements dropped
        is SubscriptionStatus.Inactive -> put("type", "inactive")
        is SubscriptionStatus.Unknown -> put("type", "unknown")
    }
}

The getter and the setter disagree, and iOS does not behave this way. A consumer that reconciles
subscription status against its own backend reads an empty set, computes a union that never matches what it
just wrote, and either pushes the same entitlements repeatedly or concludes the entitlement is missing.

Change

Serialize SubscriptionStatus.Active.entitlements with the existing full entitlement serializer, matching
the iOS bridge. The function moves from companion scope to SuperwallUnityBridge instance scope, which is
where it can reach serializeEntitlementSet. No call sites change; getSubscriptionStatus() is unaffected
apart from the richer payload.

Verification

  • Rebased onto main at 0.3.0; the two files in this PR are the entire diff.
  • Observed on a physical Android device running 0.3.0: Unity logged Active [] at cold launch while the
    native SDK held an active entitlement, and the C# side then saw its own union as a no-op. With this change
    the active entitlement id and its product are present in the getter payload.
  • Android bridge assembleRelease succeeds with AGP 9.0.1 / Gradle 9.1.0 and with AGP 8.5.2 / Gradle 8.7.
  • No iOS code changes.

Tests (added 2026-10-29)

  • New JVM unit tests for the Android bridge, in the CI harness (ci~/android-harness/bridge-tests/, so nothing test-only ships in the package), run by native.yml on both AGP versions after assembleRelease: gradle :SuperwallSDK:testDebugUnitTest.
  • SubscriptionStatusSerializationTest pins the JSON getSubscriptionStatus() returns: active carries every entitlement in the full entitlement shape; inactive and unknown carry no entitlements key.
  • Verified locally (AGP 8.5.2): 3/3 pass; with the put("entitlements", ...) line removed, 2 of 3 fail, so the test guards this regression. The AGP 9.0.1 job has only been run by CI.
  • The harness now writes the androidlib's build outputs to ci~/android-harness/build/ (gitignored) instead of Plugins/Android/SuperwallSDK.androidlib/build/, and accepts -PbuildToolsVersion=<x> for machines without AGP's default build-tools.

@bilck
bilck force-pushed the fix/serialize-android-subscription-entitlements branch from c3ef36b to 34887e8 Compare October 27, 2026 04:52
@bilck

bilck commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main at 0.3.0 and refreshed the description - the previous head predated the 0.3.0 merge and had gone stale. The getter on current main still drops the entitlement set, so the fix still applies; the diff is unchanged in substance (one CHANGELOG line plus the Android bridge).

The Android harness gains a test source set (ci~/android-harness/bridge-tests, nothing test-only ships in
the package) and the native workflow runs it on both AGP versions. The first test pins what
getSubscriptionStatus() hands to C#: an active status carries every entitlement in the full entitlement
shape, and inactive/unknown carry no entitlements key. Removing the entitlements from the serializer fails
two of the three tests.

The harness also keeps the androidlib's build outputs under ci~/android-harness/build instead of inside
Plugins/Android/SuperwallSDK.androidlib, and accepts -PbuildToolsVersion for machines without AGP's default
build-tools.
AGP 9 creates unit-test tasks only for the tested build type (debug) by default, so testReleaseUnitTest did not exist on the AGP 9.0.1 job.
bilck added a commit to bilck/Superwall-Unity that referenced this pull request Oct 29, 2026
… no test ran

On AGP 9 the srcDirs reassignment was ignored and testDebugUnitTest went NO-SOURCE, a green job that ran nothing. srcDir() (plus the Kotlin source set where the DSL has one) works on AGP 8.5.2 and 9.0.1 (7 tests each, checked locally). The CI step now also requires the JUnit result files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant