-
Notifications
You must be signed in to change notification settings - Fork 47
feat: Test Mode — test store products, purchase/restore overrides, sandbox detection #425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yusuftor
wants to merge
10
commits into
develop
Choose a base branch
from
feature/test-store
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add CodingKeys for snake_case decoding since Web2App decoder doesn't auto-convert from snake case. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Maps V2Product API response data into a StoreProductType for use as a test store product backed by the Superwall API. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TestModeManager for detecting test mode users via config - Add TestStoreUser model parsed from config testStoreUsers - Add TestModeColdLaunchAlert for showing test mode status on launch - Add TestModePurchaseDrawer for simulating purchases (purchase/abandon/fail) - Add TestModeTransactionHandler to intercept purchase and restore flows - Override StoreKitManager.getProducts to use cached test products - Override TransactionManager.purchase to show test mode drawer - Override TransactionManager.tryToRestore to show entitlement picker - Override DeviceHelper.isSandbox to return true in test mode - Rename V2Product to SuperwallProduct across codebase - Fetch products from /v1/products endpoint on config load when test mode Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tsById Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 files reviewed, 2 comments
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add bundleId to config endpoint application query - Include bundle_id_config in config JSON response - Decode bundleIdConfig in Swift Config model - Check bundle ID mismatch in evaluateTestMode — activates test mode when app bundle ID doesn't match config Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Changes in this pull request
/v1/productsendpoint, caches asStoreProductisSandbox = "true"when test mode active so all events route to sandboxChecklist
CHANGELOG.mdfor any breaking changes, enhancements, or bug fixes.swiftlintin the main directory and fixed any issues.🤖 Generated with Claude Code
Greptile Overview
Greptile Summary
This PR implements a comprehensive test mode system that allows Superwall team members to test purchase flows without real transactions. The system detects test users via config (
testStoreUsers), fetches mock products from the/v1/productsAPI endpoint, intercepts purchase/restore flows to show test dialogs, and forces sandbox mode for event routing.Key Changes
TestModeManagermatches current user against config'stestStoreUsersby aliasId, userId, or vendorIdConfigManager.fetchTestModeProducts()fetches products from API and populatesStoreKitManager.productsByIdwithTestStoreProductinstancesTransactionManagerchecks test mode before purchases/restores and delegates toTestModeTransactionHandlerwhich shows UI dialogs for simulating outcomesDeviceHelper.isSandboxreturns"true"when test mode active to route events to sandbox environmentDependencyContainerproperly wiresTestModeManagertoStoreKitManagerandDeviceHelperafter initializationArchitecture
The implementation follows the existing dependency injection patterns and integrates cleanly at key decision points (
TransactionManagerpurchase/restore,StoreKitManagerproduct fetching,DeviceHelpersandbox detection). Entitlement IDs are correctly converted fromInt(API format) toString(internalEntitlementformat) in bothTestModeManager.fakePurchase()andTestModeTransactionHandler.Confidence Score: 4/5
Int-to-Stringentitlement ID conversion which is correctly implemented. The code follows existing patterns and integrates cleanly. Minor points to verify: ensure test mode can be disabled in production, validate the cold launch alert UX, and confirm the/v1/productsendpoint is properly authenticated and scoped to the correct application.TestModeColdLaunchAlert.swiftandTestModePurchaseDrawer.swiftUI implementations for user experience qualityImportant Files Changed
Stringstorage for entitlement IDsIntentitlement IDs from API toStringforEntitlementobjectsgetProducts(lines 71-99) to return cached test products instead of StoreKit productsisSandbox = "true"when test mode active (lines 180-188)fetchTestModeProductswhen test mode active (lines 399-420, 641-669)Sequence Diagram
sequenceDiagram participant User participant ConfigManager participant TestModeManager participant Network participant StoreKitManager participant TransactionManager participant TestModeTransactionHandler participant DeviceHelper Note over ConfigManager,TestModeManager: Config Load & Test Mode Detection ConfigManager->>Network: getConfig() Network-->>ConfigManager: Config (with testStoreUsers) ConfigManager->>TestModeManager: evaluateTestMode(config) TestModeManager->>TestModeManager: Check aliasId/userId/vendorId match TestModeManager-->>ConfigManager: isTestMode = true/false alt Test Mode Active ConfigManager->>Network: getSuperwallProducts() Network-->>ConfigManager: SuperwallProductsResponse ConfigManager->>TestModeManager: setProducts(products) ConfigManager->>StoreKitManager: setProduct(TestStoreProduct) StoreKitManager->>StoreKitManager: Cache test products in productsById ConfigManager->>DeviceHelper: testModeManager reference DeviceHelper->>DeviceHelper: isSandbox = "true" (forced) end Note over User,TransactionManager: Purchase Flow in Test Mode User->>TransactionManager: purchase(productId) TransactionManager->>TestModeManager: Check isTestMode alt Test Mode TransactionManager->>TestModeTransactionHandler: handlePurchase() TestModeTransactionHandler->>User: Present drawer (Purchase/Abandon/Fail) User-->>TestModeTransactionHandler: Select outcome TestModeTransactionHandler->>TestModeManager: fakePurchase(entitlementIds) TestModeManager->>TestModeManager: Convert Int to String IDs TestModeTransactionHandler-->>TransactionManager: PurchaseResult else Normal Mode TransactionManager->>TransactionManager: Call StoreKit end Note over User,TransactionManager: Restore Flow in Test Mode User->>TransactionManager: tryToRestore() TransactionManager->>TestModeManager: Check isTestMode alt Test Mode TransactionManager->>TestModeTransactionHandler: handleRestore() TestModeTransactionHandler->>User: Present alert (Active/No Subscription/Cancel) User-->>TestModeTransactionHandler: Select status TestModeTransactionHandler->>TestModeManager: fakePurchase() or resetEntitlements() TestModeTransactionHandler-->>TransactionManager: RestorationResult else Normal Mode TransactionManager->>TransactionManager: Call StoreKit restore endContext used:
dashboard- CLAUDE.md (source)