Skip to content

Always redeem existing web codes after a successful purchase and on restore#435

Merged
ianrumac merged 7 commits into
developfrom
redeem-after-purchase-restore
Jul 22, 2026
Merged

Always redeem existing web codes after a successful purchase and on restore#435
ianrumac merged 7 commits into
developfrom
redeem-after-purchase-restore

Conversation

@claude

@claude claude Bot commented Jul 22, 2026

Copy link
Copy Markdown

Requested by Ian Rumac · Slack thread

Changes in this pull request

Always redeem existing web codes after a successful Google Play purchase and on restore, so a new or restored subscription is linked to the identified user server-side right away.

Before
A successful Google Play purchase or a restore did not call /redeem. As a result, a newly purchased or restored subscription was not linked to the identified user on the backend until some other trigger happened to fire a redeem. Until then, the server's view of the user's entitlement state could lag behind reality.

After
Both a successful purchase and a successful restore now trigger a redeem of existing codes, pushing the current entitlement state to the backend immediately as part of the same flow.

How
The redeem is injected into TransactionManager as a suspend () -> Unit closure (redeemExistingCodes), following the existing AttributionManager.redeemAfterSetting precedent, rather than having TransactionManager reference WebPaywallRedeemer directly. DependencyContainer wires the closure to reedemer.redeem(WebPaywallRedeemer.RedeemType.Existing). Because the param is suspend, it calls the suspend redeem directly — no ioScope.launch wrapper is needed since it is already invoked from a coroutine inside TransactionManager.

The closure is hooked in:

  • didPurchase(...) — invoked after loadPurchasedProducts(...) in both the internal-paywall branch and the external/observer branch, so it fires exactly once per successful purchase. didPurchase is only reached on PurchaseResult.Purchased, so it is success-only; test mode returns before didPurchase.
  • didRestore(...) — invoked at the end, after entitlement/subscription-status handling. didRestore is only reached on a successful restore, and test mode returns before it, so success-only and test-mode-skip come for free.

The default value is a no-op ({}) so existing constructors and tests are unaffected.

Notes

  • No iOS-style custom/web-product guard is added: Android has no such product category — all native products go through Google Play billing. Purchases handled by an external purchase controller already return before didPurchase, so they naturally do not redeem, which is consistent with iOS.

Mirrors iOS PR #490 / Linear SW-5516.

Checklist

  • All unit tests pass.
  • All UI tests pass.
  • Demo project builds and runs.
  • 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 ktlint 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 and others added 6 commits July 22, 2026 11:53
Injects a redeemExistingCodes suspend closure into TransactionManager and
invokes it in didPurchase (both internal and external/observer success
branches) and didRestore. The closure is wired in DependencyContainer to
call reedemer.redeem(RedeemType.Existing), following the
AttributionManager.redeemAfterSetting precedent so TransactionManager never
references the redeemer directly.

Mirrors iOS PR #490 / SW-5516.
…ase-restore

# Conflicts:
#	.benchmark/REPORT.md
#	.benchmark/results/preload-benchmark-high.json
#	.benchmark/results/preload-benchmark-low.json
#	.benchmark/results/preload-benchmark-mid.json
@ianrumac
ianrumac marked this pull request as ready for review July 22, 2026 13:55
@ianrumac
ianrumac merged commit bc692ef into develop Jul 22, 2026
12 checks passed
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.

2 participants