Skip to content

Trigger /redeem after a successful native purchase#490

Draft
claude[bot] wants to merge 2 commits into
developfrom
redeem-on-native-purchase
Draft

Trigger /redeem after a successful native purchase#490
claude[bot] wants to merge 2 commits into
developfrom
redeem-on-native-purchase

Conversation

@claude

@claude claude Bot commented Jul 18, 2026

Copy link
Copy Markdown

Requested by David Granzin · Slack thread

Changes in this pull request

Before: A successful native (App Store) purchase only refreshed entitlements locally via receiptManager.loadPurchasedProducts. It never called /redeem, so when an already-identified user started a new native subscription, that subscription was not linked to them server-side until some unrelated trigger fired (deep-link code, identity set, config reset, attribution change, or the one-time appTransactionId registration). Successful restores had the same gap.

After: Per Linear SW-5516 ("always call redeem after a successful purchase & on restore"), /redeem now fires on both paths: after a successful native purchase and after a successful restore. Each call carries the fresh device receipts + appTransactionId, so new subscriptions are linked to the user on the backend immediately after the purchase or restore completes.

How

Two success-only hooks, both re-sending the current codes plus the freshly-loaded native receipts and appTransactionId via Superwall.shared.dependencyContainer.webEntitlementRedeemer.redeem(.existingCodes) (see WebEntitlementRedeemer.createRedeemRequest):

  • Purchase: In Sources/SuperwallKit/StoreKit/Transactions/TransactionManager.swift, loadPurchasedProductsIfNeeded(for:) calls redeem(.existingCodes) right after receiptManager.loadPurchasedProducts(config: nil). This is the single funnel that both SK1 and SK2 purchases settle through (purchasehandle(result:state:)didPurchase()), so no per-purchaser edits are needed.
  • Restore: The didRestore path calls redeem(.existingCodes) after a successful restore, so restored subscriptions are linked server-side just like fresh purchases.

Both calls sit behind the existing test-mode / shouldSkipReceiptLoading guards, so they are skipped for custom/web (Stripe) products and in test mode, and both fire only on success. It matches the existing access pattern used by ReceiptManager for the one-time appTransactionId registration.

Checklist

  • All unit tests pass.
  • All UI tests pass.
  • Demo project builds and runs on iOS.
  • Demo project builds and runs on Mac Catalyst.
  • Demo project builds and runs on visionOS.
  • I added/updated tests or detailed why my change isn't tested.
  • I added an entry to the CHANGELOG.md for any breaking changes, enhancements, or bug fixes.
  • I have run swiftlint in the main directory and fixed any issues.
  • I have updated the SDK documentation as well as the online docs.
  • I have reviewed the contributing guide

Generated by Claude Code

claude added 2 commits July 18, 2026 22:57
A successful native (App Store) purchase only refreshed entitlements
locally via `loadPurchasedProducts` and never called `/redeem`, so a new
subscription for an already-identified user wasn't linked to them
server-side until another trigger fired.

Call `webEntitlementRedeemer.redeem(.existingCodes)` after
`loadPurchasedProducts` in `TransactionManager.loadPurchasedProductsIfNeeded`
so the freshly-loaded receipts + appTransactionId are pushed to the
backend on every native purchase. This is the single funnel for both SK1
and SK2 purchases and, being gated by `shouldSkipReceiptLoading`, skips
custom/web products and test mode. Restores go through `didRestore` and
are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrsXRkhSGciN62RK7mVpBV
Extend SW-5516 (already redeeming after a native purchase) to also fire
webEntitlementRedeemer.redeem(.existingCodes) on the successful restore
path. Added in TransactionManager.didRestore, which is only reached after
a restore succeeds and after receipts/entitlements have been reloaded, so
a freshly-restored subscription is linked to the identified user
server-side. Skipped in test mode, mirroring the purchase path's
shouldSkipReceiptLoading gate. Updated the purchase-path comment and
CHANGELOG accordingly.
@anglinb
anglinb requested a review from yusuftor July 20, 2026 17:00
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