Skip to content

Commit 287a399

Browse files
yusuftorclaude
andcommitted
Correct the revocation wording: empty responses are artifacts
Verified against subscriptions-api (code and a live grant/revoke cycle): the server reports a revocation by returning the entitlement as inactive and enumerates every config-mapped entitlement even for users with no purchases, so real revocations arrive non-empty and apply immediately. A fully empty entitlements array is a backend or config artifact, which is the only shape the guard ignores. Update the changelog and the guard comment to stop claiming revocations wait for the expiry date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 9d1ef98 commit 287a399

2 files changed

Lines changed: 13 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup
77
### Fixes
88

99
- Fixes subscribers being reported as `inactive` on cold launch when the App Store has no purchases to report, which hit web and Stripe subscribers hardest. Refunded and expired App Store subscriptions still deactivate immediately.
10-
- Fixes paying web subscribers being reported as `inactive` on later cold launches after the server temporarily returns no entitlements for them. Revoking a web subscriber's entitlements now takes effect once their expiry date passes.
10+
- Fixes paying web subscribers being reported as `inactive` on later cold launches after the server temporarily returns no entitlement data for them. Revocations still take effect immediately, because the server reports revoked entitlements as inactive rather than omitting them.
1111

1212
## 4.16.3
1313

Sources/SuperwallKit/Web/WebEntitlementRedeemer.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -921,15 +921,18 @@ actor WebEntitlementRedeemer {
921921
)
922922

923923
// A response with zero entitlements must not replace cached web
924-
// entitlements that are still within their expiry date. This request is
925-
// keyed on appUserId/deviceId alone, so an alias mismatch or backend
926-
// hiccup can return empty for a still-paying subscriber. If it replaced
927-
// the cache, the next cold launch would read the user as inactive until
928-
// a network poll recovered them. The cost: revoking a web entitlement
929-
// before its expiry date only takes effect once that date passes.
930-
// Entitlements with no expiry date are not protected and remain
931-
// revocable at any time. We skip saving the fetch date so the next
932-
// poll retries without waiting out `entitlementsMaxAge`.
924+
// entitlements that are still within their expiry date. The server
925+
// reports a revocation by returning the entitlement as inactive —
926+
// and it enumerates every config-mapped entitlement even for users
927+
// with no purchases — so a fully empty array is a backend or config
928+
// artifact (alias mismatch, failed upstream lookup), not a
929+
// revocation. Real revocations arrive non-empty and apply
930+
// immediately through the save below. If an empty response replaced
931+
// the cache, the next cold launch would read the user as inactive
932+
// until a network poll recovered them. Entitlements with no expiry
933+
// date are not protected by this guard. We skip saving the fetch
934+
// date so the next poll retries without waiting out
935+
// `entitlementsMaxAge`.
933936
let hasUnexpiredWebEntitlements = existingWebEntitlements.contains {
934937
$0.isActive && ($0.expiresAt ?? .distantPast) > Date()
935938
}

0 commit comments

Comments
 (0)