| New file |
| | |
| | | # Pro Pricing Model App Review Risk Checklist - 8.1.6 build 20260628.0230 |
| | | |
| | | ## Scope |
| | | |
| | | This checklist gates the App Store submission that changes TagLauncher from a paid-upfront macOS app to a free app with an optional one-time Pro unlock. |
| | | |
| | | Submission is **No-Go** until every required item below is `PASS`. |
| | | |
| | | ## Apple Rules Referenced |
| | | |
| | | - App Review Guidelines 3.1.1: digital features or full-version unlocks inside the app must use In-App Purchase, and restorable purchases need a restore mechanism. |
| | | - App Review Guidelines 2.3: metadata, screenshots, privacy information, and review notes must accurately describe core experience, pricing, new functionality, and product changes. |
| | | - App Review Guidelines "Before You Submit": App Review notes should include detailed explanations of non-obvious features and in-app purchases. |
| | | |
| | | Official source: https://developer.apple.com/app-store/review/guidelines/ |
| | | |
| | | ## Gate A - Product And Pricing Model |
| | | |
| | | | ID | Check | Status | Evidence / Required Evidence | |
| | | | --- | --- | --- | --- | |
| | | | A1 | Pro is a one-time unlock, not a subscription. | PASS | PRD states "一次性买断"; code uses one StoreKit product id `com.taglauncher.pro.lifetime`. | |
| | | | A2 | Pro unlock uses Apple In-App Purchase only; no external license key, website payment, QR code, or other unlock mechanism. | PASS | `ProEntitlement.swift` uses StoreKit `Product.products`, `product.purchase`, and `Transaction.currentEntitlements`. No external payment path found in current grep scope. | |
| | | | A3 | The ASC In-App Purchase product exists with product id `com.taglauncher.pro.lifetime`. | PASS | ASC IAP created: `Lifetime Pro Unlock`, Apple ID `6785107181`, product ID `com.taglauncher.pro.lifetime`. | |
| | | | A4 | The ASC product type is Non-Consumable / one-time purchase. | PASS | ASC product was created as `非消耗型项目` / Non-Consumable. | |
| | | | A5 | The Pro price matches the previous paid app price as intended by PRD. | PASS-WITH-NOTE | ASC IAP pricing configured at US `$6.99`; local StoreKit price shown as `¥48.00`, matching the intended previous paid-app price regionally. | |
| | | | A6 | Family Sharing is enabled if available for this non-consumable product. | PASS | ASC shows Family Sharing enabled: family group members can share this IAP. | |
| | | | A7 | App price is set/scheduled to Free only in a way that keeps IAP available for review and sale. | FAIL-ASC | ASC pricing page currently shows `中国大陆 (CNY)` app price `¥18.00`; this conflicts with the intended free-download + one-time Pro unlock model. Do not submit until the App price schedule is changed to Free or the pricing model is explicitly changed. | |
| | | |
| | | ## Gate B - Existing Paid Users |
| | | |
| | | | ID | Check | Status | Evidence / Required Evidence | |
| | | | --- | --- | --- | --- | |
| | | | B1 | Existing users who obtained the app before the free + Pro transition automatically receive lifetime Pro. | PASS | `firstFreeProVersion = "8.1.0"` and `AppTransaction.shared` legacy path are implemented. | |
| | | | B2 | Primary legacy rule uses `originalAppVersion` earlier than `8.1.0`. | PASS | `isLegacyPaidUser` compares `appTransaction.originalAppVersion` against `firstFreeProVersion`. | |
| | | | B3 | `originalPurchaseDate` fallback exists for cases where version parsing is unavailable. | PASS-WITH-NOTE | Code supports fallback, but `legacyFallbackOriginalPurchaseDateISO8601` is currently `nil`; acceptable only if `originalAppVersion` is reliable enough for this release. | |
| | | | B4 | Legacy Pro state is cached so offline launch does not immediately lose confirmed Pro status. | PASS | `ProEntitlementCache` persists `accessState`, `originalAppVersion`, and `originalPurchaseDate`. | |
| | | | B5 | Review Notes explicitly state old paid users get lifetime Pro and are not asked to pay again. | PASS | `APP_REVIEW_NOTES.md` now states existing paid users automatically receive lifetime Pro via `originalAppVersion`. | |
| | | |
| | | ## Gate C - Purchase / Restore Behavior |
| | | |
| | | | ID | Check | Status | Evidence / Required Evidence | |
| | | | --- | --- | --- | --- | |
| | | | C1 | Purchase flow calls StoreKit purchase. | PASS | `ProEntitlementCenter.purchasePro()` calls `product.purchase()`. | |
| | | | C2 | Restore flow exists and calls App Store sync. | PASS | `restorePurchases()` calls `AppStore.sync()`. | |
| | | | C3 | Current entitlements are checked after purchase/restore/app start. | PASS | `Transaction.currentEntitlements` is used in `hasPurchasedLifetimePro()`. | |
| | | | C4 | Unverified transactions do not unlock Pro. | PASS | Code ignores `.unverified` transactions. | |
| | | | C5 | Product unavailable state is user-visible and does not crash. | PASS | Missing product returns `pro.purchase.unavailable`. | |
| | | | C6 | Restore-with-no-purchase state is user-visible. | PASS | `restoreNotFound` maps to localized user message. | |
| | | | C7 | Sandbox purchase and restore are testable during App Review. | TODO-ASC | IAP product exists and StoreKit product id matches code. Must attach the IAP to the app version before final submit so App Review can test purchase and restore. | |
| | | |
| | | ## Gate D - Free App Value And Pro Boundaries |
| | | |
| | | | ID | Check | Status | Evidence / Required Evidence | |
| | | | --- | --- | --- | --- | |
| | | | D1 | Free users can still use the core app launcher and tag workflow. | PASS | Pro gates are limited to premium themes, import/export, unlimited notes, and persistent manual sorting. | |
| | | | D2 | Free users can preview Pro themes without dirtying permanent state. | PASS | Theme preview state is transient and expires; countdown QA passed. | |
| | | | D3 | Free preview clearly shows remaining time and restores the real available theme on expiry. | PASS | `pro_theme_preview_countdown_qa.sh` passed. | |
| | | | D4 | Free users are not misled into thinking Pro-only saved state has been permanently saved. | PASS | Sorting preview toast and feature gates are wired by `pro_feature_gate_qa.sh`. | |
| | | | D5 | Pro prompts appear only when the user actively triggers Pro functionality. | PASS | Static gate QA passed; no always-on paywall detected in checked paths. | |
| | | |
| | | ## Gate E - Metadata / Review Notes / Screenshots |
| | | |
| | | | ID | Check | Status | Evidence / Required Evidence | |
| | | | --- | --- | --- | --- | |
| | | | E1 | What’s New accurately describes user-visible Pro changes without over-selling monetization. | PASS | English (US) `whatsNew` was empty on ASC and was filled/saved with user-visible 8.1.6 changes; no external purchase path is mentioned. | |
| | | | E2 | Review Notes include a dedicated pricing model change explanation. | PASS | `APP_REVIEW_NOTES.md` now describes paid-upfront to free + Pro. | |
| | | | E3 | Review Notes explain Pro is one-time non-consumable IAP, not a subscription. | PASS | `APP_REVIEW_NOTES.md` says Pro is a one-time Apple In-App Purchase and not a subscription. | |
| | | | E4 | Review Notes explain old paid users automatically get lifetime Pro. | PASS | `APP_REVIEW_NOTES.md` documents the `originalAppVersion` legacy unlock path. | |
| | | | E5 | Review Notes explain free users keep core functionality. | PASS | `APP_REVIEW_NOTES.md` lists free core launching, tagging, and search workflow. | |
| | | | E6 | Screenshots/metadata do not imply every Pro feature is free. | PASS-WITH-USER-OVERRIDE | User manually adjusted ASC screenshots and instructed Codex not to handle screenshots further in this pass. Current metadata text separates Free core functionality from Pro-only features. | |
| | | | E7 | App description/subtitle does not use risky Apple comparison terms. | PASS | Release metadata already bans Launchpad/Spotlight comparison terms. | |
| | | | E8 | If Pro/IAP appears in screenshots or description, the additional purchase requirement is clear. | PASS | App description keeps core app value separate; review notes explicitly explain optional one-time Pro unlock and old paid-user lifetime Pro protection. | |
| | | | E9 | ASC screenshots use accepted macOS screenshot dimensions. | PASS-WITH-USER-OVERRIDE | User manually adjusted ASC screenshots and instructed Codex not to handle screenshots further in this pass. After English What's New was filled, ASC enabled `添加以供审核`, indicating screenshot metadata is no longer blocking version submission. | |
| | | | E10 | IAP review screenshot clearly shows the Pro unlock entry, price, restore, and unlocked benefits. | PASS-WITH-NOTE | IAP review screenshot was uploaded manually by user. Codex previously prepared stronger Pro-unlock screenshots under `Screenshots/IAPReview/`; user chose to manage screenshots manually. Final submission dialog should still be checked for IAP inclusion. | |
| | | |
| | | ## Gate F - Local QA Evidence |
| | | |
| | | | ID | Check | Status | Evidence / Required Evidence | |
| | | | --- | --- | --- | --- | |
| | | | F1 | Pro feature gate static QA passed. | PASS | `bash Scripts/pro_feature_gate_qa.sh` passed. | |
| | | | F2 | Pro localization QA passed for all 29 languages. | PASS | `bash Scripts/pro_localization_qa.sh` passed: 29 languages, 46 keys. | |
| | | | F3 | Pro theme preview countdown QA passed. | PASS | `bash Scripts/pro_theme_preview_countdown_qa.sh` passed. | |
| | | | F4 | macOS 14 build metadata QA passed. | PASS | Recorded in release checklist. | |
| | | | F5 | Signed PKG upload package verified. | PASS | Recorded in release checklist. | |
| | | |
| | | ## Current Go / No-Go |
| | | |
| | | **NO-GO** |
| | | |
| | | Reasons: |
| | | |
| | | 1. Local code, package, Pro gates, localization, and countdown QA passed. |
| | | 2. ASC build is selected on the 8.1.6 version page. |
| | | 3. English (US) What's New was filled and saved; `Add for Review` became enabled. |
| | | 4. User manually handled screenshots and instructed Codex not to block on screenshot replacement in this pass. |
| | | 5. Release-blocking pricing mismatch remains: ASC still shows the app itself as paid in China (`¥18.00`), while this submission is meant to transition to a free app with optional one-time Pro unlock. |
| | | |
| | | Before the final external side-effect, fix or explicitly resolve A7, then open the `Add for Review` flow and confirm the submission summary includes the 8.1.6 build and the intended `Lifetime Pro Unlock` IAP. Do not click final `Submit for Review` without action-time user confirmation. |