Fix SubscriptionStatus property dropping entitlements in both directions#5
Open
bilck wants to merge 1 commit into
Open
Fix SubscriptionStatus property dropping entitlements in both directions#5bilck wants to merge 1 commit into
bilck wants to merge 1 commit into
Conversation
The C# SubscriptionStatus property lost the entitlement list on both
sides of the bridge:
- The setter serialized only {"type": "Active"}. Both native bridges
(SuperwallUnityBridge.swift / SuperwallUnityBridge.kt) already parse an
"entitlements" array from this payload, but C# never sent it, so native
received .active([]) - which the native SDKs normalize to inactive and
answer with a subscriptionStatusDidChange(to: inactive) echo.
- The getter ignored the "entitlements" array native serializes and
always returned CreateActive with an empty list.
Impact: any app that re-asserts entitlements on
subscriptionStatusDidChange (e.g. the documented client-side
cross-platform entitlement merge) enters an infinite set -> inactive ->
set feedback loop right after a purchase, and the entitlement natively
granted by the paywall purchase is wiped. Reproduced on iOS
sandbox/StoreKit with SDK 0.2.4.
Fix:
- Setter serializes the active entitlement ids (the shape both native
parsers already read) and lowercases the type to match the native
serializers.
- Getter reuses BridgeCallbackHandler.DeserializeSubscriptionStatus
(private -> internal, same assembly) so entitlements survive the
round trip.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The C#
SubscriptionStatusproperty (Runtime/Superwall.cs) loses the entitlement list on both sides of the bridge:{"type": "Active"}— it never sends the entitlements. Both native bridges (SuperwallUnityBridge.swiftandSuperwallUnityBridge.kt) already parse an"entitlements"array from this payload; C# just doesn't include it."entitlements"array native serializes and always returnsCreateActivewith an empty list.Either defect alone breaks any code that compares or re-asserts entitlements. Together they produce an infinite feedback loop right after a purchase (order of events below).
Order of events causing the bug
Setup: the app keeps a backend (cross-platform) entitlement source and merges it into Superwall client-side, as recommended when a backend is the source of truth — i.e. on every
subscriptionStatusDidChangeit unions backend entitlements into the SDK's current status and writes the result back, skipping the write when the sets are already equal..active([premium]), and firessubscriptionStatusDidChange.Superwall.Shared.SubscriptionStatus— but the getter drops the entitlement list, returningActive []instead ofActive [premium].premiumand concludes the SDK is missing it (it isn't — the getter hid it). The "sets are equal, skip the write" exit can never trigger, so the app writesActive [premium]back.{"type": "Active"}to native. The native bridge parses no"entitlements"array and sets.active([]).subscriptionStatusDidChange(to: inactive)back into Unity.Inactive, backend still sayspremium, so it writesActive [premium]again → step 4. Steps 4–7 repeat forever.Reproduced on iOS sandbox with SDK 0.2.4: a single purchase produced ~45,000 loop iterations within minutes, with the app effectively frozen in the write/echo cycle:
Fix (C#-only; no native changes needed)
[{"id": ...}]), and lowercases the type to match the native serializers.BridgeCallbackHandler.DeserializeSubscriptionStatus(private→internal, same assembly) so entitlements survive the round trip.With the round trip intact, step 2 reads
Active [premium], the merge sees equal sets, skips the write, and the loop never starts; a genuinely needed write (backend knows an entitlement the SDK doesn't) converges after one echo.Test plan
subscriptionStatusDidChangeecho), and the purchased entitlement remains active in the native SDK after app restart.Superwall.Shared.SubscriptionStatus = .active([...])followed by a read returns the same entitlement ids.🤖 Generated with Claude Code