Add QPurchaseResult to EntitlementsUpdateListener#769
Conversation
Review & Fix: Mutual Recursion in
|
| Scenario | SDK calls 2-arg → | Result |
|---|---|---|
| Old code (overrides 1-arg only) | 2-arg default → 1-arg override | ✅ Works |
| New code (overrides 2-arg only) | 2-arg override runs | ✅ Works |
| Both overridden | 2-arg override runs | ✅ Works |
| Neither overridden | 2-arg default → 1-arg no-op | ✅ Silent no-op (no crash) |
Note on local tests
Could not run ./gradlew :sdk:test locally — requires JDK 11+ (only JDK 8 available on this machine). Please verify CI passes. The change is a 1-line no-op replacement with no logic changes to production flow.
🤖 Generated with Claude Code
Local Test Results ✅Environment: JDK 17.0.18 (Temurin), Android SDK, macOS (aarch64)
|
…ases When deferred consumable purchases complete in background (no active callback), the SDK now passes QPurchaseResult to the entitlements update listener. This allows developers to access purchase details for consumables that don't create entitlements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the 1-arg method's default body (which delegated to 2-arg) with a no-op. This breaks the mutual recursion cycle that would cause StackOverflowError if neither method was overridden. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
745db80 to
ab24e65
Compare
NickSxti
left a comment
There was a problem hiding this comment.
Self-review (LEVER)
Logic: When deferred/background purchases complete without an active callback, the previous onEntitlementsUpdated(entitlements) delivered only entitlements. Consumable purchases that don't create entitlements produced an empty map and no transaction info (1768+ instances in API Gateway logs). Fix adds overloaded onEntitlementsUpdated(entitlements, purchaseResult) with a QPurchaseResult? argument; QProductCenterManager success path (~line 1047) and calculatePurchasePermissionsLocally fallback now pass QPurchaseResult to the listener when no purchase callback exists.
Edge cases:
- Listener overrides only old method: default impl delegates to old — unchanged behavior.
- Listener overrides only new method: default impl delegates to new — unchanged behavior.
- Listener overrides both: direct invocation, no re-entry (confirm tests assert no double-dispatch).
- Active purchase callback present: listener is NOT called (unchanged) — test covers.
- Deferred purchase with null result from store: listener receives
nullQPurchaseResultvia nullable param. - Java interop: both method signatures remain accessible.
Verification:
- Tests: listener receives
QPurchaseResultfor deferred purchases; listener not called when callback exists. - CI:
./gradlew :sdk:assemble+:sdk:testpending. - Sample app updated to demonstrate consumable flow.
- Manual test plan for deferred consumable + normal purchase flow pending.
Effects / blast radius:
- Public API surface grows by one overload — non-breaking due to mutual-delegation defaults.
@Deprecatedon old method guides new adopters.- Two internal call sites updated.
Risks / rollback:
- Revert removes overload — any consumer that adopted the new signature would break; ensure iOS-sdk#644 ships in lockstep for parity.
- Mutual delegation must not infinite-loop — tests must explicitly assert single invocation for each override combination.
- Consumer migration is gradual because old method stays functional.
|
Superseded by #785 (QDeferredPurchasesListener, shipped in SDK 9.3.0 on 2026-04-06). The overload approach was replaced with a dedicated listener interface; QEntitlementsUpdateListener is now |
Summary
QEntitlementsUpdateListenerwith a new overloadedonEntitlementsUpdated(entitlements, purchaseResult)methodQPurchaseResultwith full purchase detailsChanges
QEntitlementsUpdateListener: Added second method withQPurchaseResult?parameter. Both methods have default implementations for mutual delegation — fully backward compatibleQProductCenterManager: Success path (line ~1047) and fallback path (calculatePurchasePermissionsLocally) now passQPurchaseResultto listener when no purchase callback existsEntitlementsFragmentto demonstrate the new method with consumable purchase handlingQPurchaseResultfor deferred purchases, and is NOT called when a callback existsBackward compatibility
@Deprecatedannotation guides new adopters to the new methodTest plan
./gradlew :sdk:assemble./gradlew :sdk:testQPurchaseResultResolves: SUP3-58
🤖 Generated with Claude Code