Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup
### Enhancements

- Adds the user's system-wide text size (Dynamic Type) as three device attributes for use in paywalls and audience filters: `fontScale`, `fontSize`, and `preferredContentSizeCategory`.
- Links subscriptions to the user server-side after successful purchase or restore.

## 4.16.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,19 @@ final class TransactionManager {
)
await Superwall.shared.track(trackedEvent)
}

// `didRestore` is only reached on a successful restore, at which point
// receipts/entitlements have just been reloaded. Redeem existing codes so a
// freshly-restored subscription is linked to the identified user server-side,
// mirroring the purchase path in `loadPurchasedProductsIfNeeded`. Skipped in
// test mode, which sets subscription state without real receipt data
// (SW-5516: always redeem after a successful purchase & restore).
if factory.makeTestModeManager().isTestMode {
return
}
Task {
await Superwall.shared.dependencyContainer.webEntitlementRedeemer.redeem(.existingCodes)
}
}

private func purchase(
Expand Down Expand Up @@ -830,11 +843,23 @@ final class TransactionManager {
}

private func loadPurchasedProductsIfNeeded(for product: StoreProduct) async {
guard !shouldSkipReceiptLoading(for: product) else {
if shouldSkipReceiptLoading(for: product) {
return
}

await receiptManager.loadPurchasedProducts(config: nil)

// A successful native (App Store) purchase doesn't otherwise hit `/redeem`,
// so a new subscription for an already-identified user isn't linked to them
// server-side until some other trigger fires. Redeem existing codes here so
// the freshly-loaded receipts + appTransactionId are always pushed to the
// backend right after a native purchase completes. This is the purchase path;
// the equivalent redeem for restores lives in `didRestore`. Gated by
// `shouldSkipReceiptLoading`, so it skips custom/web products and test mode
// (SW-5516: always redeem after a successful purchase & restore).
Task {
await Superwall.shared.dependencyContainer.webEntitlementRedeemer.redeem(.existingCodes)
}
}

private func shouldSkipReceiptLoading(for product: StoreProduct) -> Bool {
Expand Down
Loading