HoopSpark IAP — production readiness report

HOOP 系统设计图纸 · 正文唯一真相是 docs/iap_readiness.md;改 md 再跑 ./tools/deploy_docs.sh,这页是生成的

版本 1c9cddb02
2026-09-30 14:17

Final pre-launch review requested by Leong on 2026-09-29 23:27. Written by Tora Master, 2026-09-30. Task-by-task detail and live status: final review checklist. ✅ Merged and deployed 2026-09-30 (Leong 01:23 "can proceed merge", 01:24 "merge the (D) also"): master 2739871f3 = v2.827. Live now: backend, database migrations 479–483, web v2.827, Terms and privacy. The app part reaches phones with NOVA's next TestFlight build.

11. Is it technically ready? (the short answer first)

The code: yes, and it is now live. The branch merged into master on 2026-09-30 with no conflicts. On the merged result the server builds and passes go vet, the IAP, wallet and developer tests pass, and the app shows no new analyzer issues. After deploying: the live database is at 483, not dirty, and the "balance never negative" rule is validated. Health returns 200, and the new routes answer 401 when not logged in.

Selling to customers: not yet. Four things stand between us and the first real purchase, and none of them is code:

  1. Jeff sets up the two store consoles (setup guide Parts A and B) and sends Tora the keys and tester IDs.
  2. ~~Leong says merge~~ ✅ done 2026-09-30. NOVA builds the app for TestFlight (the WIP line is in).
  3. Tora puts the keys on the server (Part C).
  4. One real purchase on each platform, end to end, on two phones (Part D). This has never been done: every test so far runs against fake Apple and Google servers that follow their documented formats.

1. What was reviewed

Seven reviewers read the whole IAP path, and Tora then re-checked every serious finding against the code:

  • Customer path: top-up page, purchase states, restore, offline, web version.
  • Admin tools: risk page, welcome gift, payouts.
  • Backend: receipt checks for both stores, notifications, refunds, sweeps.
  • Database: migrations, constraints, rollbacks.
  • Store setup: Apple and Google configuration, against the setup guide.
  • Documents: the guides, Terms and privacy policy.
  • Edge cases and security.

Where a claim could be checked on the live server (key file permissions, settings, row counts), it was.

2. What was found

47 tasks in total:

  • 1 critical: F1, key files the server could not read.
  • 9 high: F2, F3, F4, F5, F7, F8, T1, P1, P2.
  • Medium and low: the rest.

The ones that could lose or wrongly move money:

  • F4 / F5 / F9 — refund, then reversed, then refunded again. A second reversal gave the Sparks back twice, a second refund after a reversal failed forever, and a refund that arrived before the purchase was credited left the buyer paid with nothing.
  • F10 — missed Apple refund notifications were lost for good.
  • F6 (A) — the account written on a purchase was trusted blindly. A modified app could buy for someone else and refund it, so the refund landed on the victim. Leong's one-time-code idea closed it completely.
  • F7 / F8 — no tool to look up a customer or fix a balance by hand. Support could not act on anything.

3. What was fixed

Commit IDs below are from branch tora/iap-final; on master the same commits carry new IDs because they were rebased at merge.

Every fix below has a test that fails when the fix is removed. We checked this on purpose for each one.

Area Fixed Commits
Refund cycle F4, F5, F9 b55add8fc
Server batch F10 (revoked receipts), F11, F12, F13, F29, F31, F32, F33, F34, F35, F37 662b626cf
App batch F16, F17, F18, F25, F26, F27, F28 83d7a7efe
Support tools (B) Look up a customer; adjust Sparks with a reason, an audit row and a double-click guard (migration 000480) eb013e9a1, e35f492fa, 64c43e71e
Daily Apple refund check (C) Catches refunds whose notification never arrived; will not replay a refund Apple later cancelled 2d57519cd
Database F38 balance can never go negative (migration 000481); F39 rollback refuses to drop financial records 52bf32d2f
Late Apple refund question F36 a2d5ffa42
Android price line F30 7f205ecc3
Play permissions F24: red line on the admin page when Google refuses our key 3d992b4af
Admin risk page F14: tap a row → customer page, show all rows, refunds Apple cancelled 5f83b5915
Missing tests T2: F11, F31, F32, F34 64f443a48
One-time purchase code (A) Server + migration 000483, app, design doc a009b98ae, 153aa020b, d0f2ec5af
Guides (already live) F1, F3, F19, F20, F21, F23, F40 on master
Last four (Leong 09-30 02:16) F43 welcome gift once per inbox (alias emails), F41 15-minute grace for Google "not found", F42 Apple certificate checked at the signed date, F15 welcome-gift change history live as v2.830
Retired packs (Leong 09-30 06:03) A pack's Sparks change via a new ID; the old one is retired: not offered, no purchase code, late purchases still credited live as v2.851

4. What is still outstanding

Task What Waiting for
P1 Merge and deploy ✅ Done 2026-09-30; the app waits for NOVA's TestFlight build
P2 Server settings for keys and testers Jeff's console work
T1 Real-phone end-to-end tests All of the above
F2 App Review demo account Jeff (guide step A8.1 is live)

5. Risks that remain

  • Never run against the real stores. The fakes copy Apple's and Google's documented behaviour, but only a real purchase proves it (T1).
  • The whole app has not been built by Tora. Only NOVA builds TestFlight. Single files pass the analyzer and tests, but the first NOVA build is the first full compile.
  • …spark.34000 (RM 249.90) loses money at Apple's 30% commission. Create it, but don't submit it until the 15% programme is confirmed (guide A2.3).
  • Older TestFlight builds keep working after the merge. Their purchases go to whoever sends the receipt, which is harmless.
  • Nine admin-dashboard test files already fail on master, for reasons unrelated to IAP. The IAP changes add no new failures (compared file by file); they are not fixed here.
  • Out of scope: creator payouts in Sparks. Li Min has said Sparks are never cash; that change is waiting on her decision about the new token.

6. Tests still missing

  • T1: real purchases on two phones. Buy, refund, restore, and a tester vs a non-tester, on iPhone and Android (guide Part D).
  • A full app build, which NOVA does.
  • Nothing else. Every code fix has its own test, and the checklist records which assertion each "knife" turned red.

7. Apple configuration required (guide Part A)

  • A2: Paid Apps agreement, tax and bank, plus the Small Business Program (15%).
  • A3–A5: the six products, exactly the IDs in the guide. Prices 4.90 / 14.90 / 29.90 / 54.90 / 109.90 / 249.90. Malaysia only. Hold back spark.34000.
  • A7: refund notification URL, and an In-App Purchase key. The key now also runs the daily refund check.
  • A8: sandbox testers, and the App Review demo account (A8.1).
  • A9: App Privacy answers.

8. Google configuration required (guide Part B)

  • B1–B2: the app on internal testing, a payments profile, and the 15% service fee.
  • B3–B5: the same six products and prices, Malaysia only, "Backwards compatible" turned on.
  • B6: license testers.
  • B7: a service account invited in Play Console with the order and financial permissions, and real-time notifications to our server. If the permission is missing, the admin page now shows a red line within a minute of startup.
  • B7.5: Data safety answers.

9. What Jeff needs to do

  1. Work through the setup guide Part A (Apple) and Part B (Google), in any order.
  2. Send Tora three things, never by pasting a key's contents:
  3. the key files, by scp to the server;
  4. the Apple key and issuer IDs, and the Google service-account email;
  5. the HOOP IDs of the testers and of the App Review demo account.
  6. After the NOVA build, do the Part D tests on two phones.

10. Deployment configuration

At merge, Tora did these in this order (✅ = done 2026-09-30):

  1. Migrations 000480 (support adjustments), 000481 (balance never negative), 000483 (purchase codes). 000482 belongs to another task and is deployed by its owner.
  2. Backend deploy: rsync + docker compose up -d backend. up -d is required, because a plain restart does not re-read .env.
  3. Server settings as keys arrive (guide C1):
  4. Apple: IAP_ASC_ISSUER_ID, IAP_ASC_KEY_ID, IAP_ASC_KEY_FILE, IAP_REFUND_CONSENT_LIVE=true.
  5. Google: IAP_GOOGLE_CREDS_FILE, IAP_GOOGLE_NOTIFY_SA.
  6. Testers: IAP_TEST_UIDS.
  7. Key files go in deploy/secrets/ owned by uid 10001.
  8. App: version bump and CHANGELOG, a line for NOVA's next TestFlight build in WIP, then the web version (top-up stays hidden there).
  9. Documents:
  10. The setup guide's "how a purchase becomes Sparks" step changes from "the app attaches the user ID" to the one-time code.
  11. The Apple key's description adds the daily refund check.
  12. The plan pages are regenerated.
  13. Terms and privacy: ✅ published 2026-09-30 after Leong's OK (D), byte-checked live.
  14. Release the WIP claims on router.go and api_client.dart.