Files
OSGKeyboard/docs/STOREKIT_TESTING.md
T
rockyandClaude Opus 5 5d51c5bf77 chore: snapshot uncommitted local work before rebasing onto cloud-agent
Safety commit so the 94 uncommitted files in the working tree (in-flight
Account/Mac/Tip work plus the feature-preview recording tooling) survive the
move onto cursor/cloud-agent-1787824905651-vxuwc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 11:28:07 -07:00

6.0 KiB

StoreKit — managed credit packs

OSGKeyboard ships three Consumable in-app purchases and no subscriptions. All three add credits to the optional OSG managed account. The authoritative local catalog is OSGKeyboard.storekit; App Store Connect must match it exactly.

Product ID Type Reference name Display name (en) Price (CN) Grants
500tks Consumable 500 OSG 积分 500 OSG Credits ¥8 500 account credits
1500tks Consumable 1,500 OSG 积分 1,500 OSG Credits ¥18 1,500 account credits
3000tks Consumable 3,000 OSG 积分 3,000 OSG Credits ¥28 3,000 account credits

Rules that apply to all three:

  • Consumables cannot be restored (Apple policy). The UI states this.
  • Nothing here gates a feature. Local dictation, BYOK providers, typing, clipboard skills, and iCloud sync stay free with or without a purchase.
  • Credits only add balance to the optional OSG account. They are useless without Sign in with Apple, and the app never requires an account.
  • Credit packs are iOS / iPadOS only. The macOS build ships via Developer ID outside the Mac App Store, so it loads no StoreKit products at all.

Where the packs appear

iOS Home → credit card → Account Center, and Settings → Account Center. Both require Sign in with Apple.

Purchase flow (server-verified)

Credit packs are not client-trusted. AccountCreditPurchaseManager:

  1. Buys with StoreKit 2, binding appAccountToken to the signed-in account ID.
  2. Rejects any transaction whose appAccountToken or product ID does not match the configured set.
  3. Submits the signed transaction to the account service (submitCreditTransaction) and checks the returned transaction and product IDs against the local ones.
  4. Calls transaction.finish() only after the server confirms and the ledger is updated.

A purchase that fails server verification stays unfinished and is retried from the transaction-updates stream, so a crashed or backgrounded app does not lose credits.


App Store Connect setup

  1. Open App Store Connect → OSGKeyboard → In-App Purchases.
  2. Create each of the three products as Consumable with the exact Product IDs above. Do not rename an existing ID — Apple treats it as a new product.
  3. Add en + zh-Hans localizations. Copy comes from OSGKeyboard.storekit (localizations) and docs/APPSTORE_METADATA.md.
  4. Set pricing to the nearest App Store tier for China (¥8 / ¥18 / ¥28) and let Apple map the equivalent tiers elsewhere.
  5. Submit the IAPs for review together with the app version that exposes them. A first IAP submitted without a build is rejected as incomplete.

Review screenshot (required — otherwise「元数据丢失」)

Each product needs a screenshot under 审核信息 → 截屏: run the app signed in, open Home → credit card → Account Center, and capture the pack list showing localized prices.

Optional 审核备注:

Consumable credits for the optional OSG managed speech/AI service.
Requires Sign in with Apple. Local dictation and user-supplied API keys
remain free and never require credits. Consumable — cannot restore.

Save — status should become 准备提交.


Local testing (StoreKit Test — no Connect, no sandbox account)

The OSGKeyboard scheme already references OSGKeyboard.storekit in project.yml (storeKitConfiguration).

  1. ./Scripts/generate-xcodeproj.sh && open OSGKeyboard.xcodeproj
  2. Product → Scheme → Edit Scheme → Run → Options — confirm StoreKit Configuration = OSGKeyboard.storekit
  3. Run OSGKeyboard on a simulator (e.g. iPhone 17) or a device.
  4. Sign in with Apple, then open Home → credit card → Account Center.
  5. Buy 500tks → balance increases by 500 after the account service confirms; the purchase-history row appears.
  6. Cancel a purchase → returns to idle with no error spam.
  7. Repeat a purchase → allowed (Consumable).

Debug menu: Xcode → Debug → StoreKit → Manage Transactions to inspect or delete test purchases.

Automated coverage: AccountCreditPurchaseManagerTests (in the hermetic manifest — see TESTING.md) and AccountCenterUITests, which runs separately via xcodebuild -scheme OSGKeyboardUITests against the IAPReview stub harness — see ACCOUNT_MANAGED_GATEWAY_TESTING.md.


Sandbox testing (after Connect IAPs reach 准备提交)

  1. App Store Connect → 用户和访问 → 沙盒 → create a Sandbox Tester.
  2. On device: 设置 → App Store → 沙盒账户 → sign in (not your real Apple ID).
  3. Install via TestFlight, or Debug-run with StoreKit Configuration = None so the app hits Connect products.
  4. Buy each pack once and confirm the server-side ledger, not just the UI.
  5. Sandbox charges are free; the receipt path is the real one.

The full server-side matrix (replayed transactions, interrupted network, wrong-account rejection) lives in ACCOUNT_MANAGED_GATEWAY_TESTING.md.


Release checklist

  • All three products load and show localized prices
  • Each pack adds the correct balance after server verification
  • Purchase history lists the new transaction without duplicates
  • A failed/interrupted verification is retried and not lost
  • User cancel returns to idle (no error spam)
  • Repeat purchase works (Consumable allows multiple)
  • No Restore button for any of these products
  • Local dictation, BYOK, typing, and clipboard skills unchanged after purchase

Files

File Role
OSGKeyboard/Views/Account/AccountCreditPurchaseManager.swift Purchase + server verification
OSGKeyboard/Views/Account/AccountCreditStore.swift StoreKit 2 product/purchase abstraction
OSGKeyboard/Views/Account/AccountCenterView.swift Credit pack UI, purchase history
OSGKeyboard.storekit Local StoreKit Test catalog