← All packs
App Store Machinery
complex-universalverifiedStoreKit 2 IAP with server-side JWS validation, fastlane + TestFlight pipeline, privacy manifest, and a review-rejection preflight — the complete shipping rig for an existing iOS app.
How install works
- Accounts
- Tools to install (exact pins — verify.sh checks these)
Total active setup time: roughly 1–3 hours, plus up to 2 days of Apple Developer Program approval happening in the background — that wait does not block anything below.
Full setup walkthrough ships in the zip after purchase.
Coming soon — $129Execution receipt✓3 of 3 runs passed- Model
- claude-sonnet-5
- Run at
- 2026-08-02T11:08:37+00:00
- Pack version
- 9cdca87e85da
- Tokens in / out
- 11,415,370 / 165,503 · per-run mean · billed input includes 10,854,227 cache reads, priced far below fresh input
- Wall time
- 21 min · per-run mean
- Deploy
- not deployed
Acceptance checks
- toolchain✓ PASS
- purchasekit-build✓ PASS
- purchasekit-tests✓ PASS
- privacy-manifest✓ PASS
- storekit-config✓ PASS
- server-setup✓ PASS
- server-jws-verification✓ PASS
- server-notifications✓ PASS
- server-http✓ PASS
- fastlane-lanes✓ PASS
- review-preflight✓ PASS
- docs-present✓ PASS
Versions lock
swiftui-storekit2-node
| xcode | 26.6 |
| ios-sdk | 26.5 |
| swift | 6.3 |
| swift-tools-version | 6.2 |
| min-ios-deployment | 17.0 |
| fastlane | 2.237.0 |
| ruby | 3.4.10 |
| node | 24.18.1 |
| @apple/app-store-server-library | 3.1.0 |
| jsonwebtoken | 9.0.2 |
Scaffold integrity
6 scaffold files modified or deleted by the buyer-agent during verification
- Gemfile
- fastlane/Fastfile
- ios/PurchaseKit/Tests/PurchaseKitTests/EntitlementTests.swift
- server/package.json
- server/src/server.js
- server/src/verifier.js
Getting in-app purchases right on the App Store means passing cryptographic verification you can't eyeball, routing around an undocumented sandbox error code, and clearing five rejection guidelines that Xcode will happily let you submit anyway — most of which surface days later as a review rejection, not a build error. This pack turns an existing SwiftUI app into one that sells subscriptions and a lifetime unlock correctly the first time: a StoreKit 2 paywall, a server that is the actual source of truth for who's entitled to what, a non-interactive fastlane pipeline, and a preflight that catches the top rejection causes before Apple does. The receipt requires the JWS chain-verification logic to be proven cryptographically correct against a locally-signed, Apple-format test chain before this pack ships — not just "the code compiled."
Example use cases
- **An indie SwiftUI habit-tracker adding its first paywall** — the developer has never touched StoreKit; this pack adds monthly/annual subscriptions plus a lifetime unlock without them learning JWS verification or App Store Server Notifications from scratch.
- **A freemium PDF-scanner app adding a lifetime-unlock tier alongside an existing subscription** — the non-consumable path (offer + entitlement math + preflight checks) is already wired in, not bolted on after the fact.
- **A solo developer who shipped their own StoreKit 2 code and keeps getting rejected on 3.1.2 / 5.1.2-style guidelines** — the review-rejection preflight and pre-filled privacy manifest target exactly the guidelines that generic StoreKit tutorials skip.
- **A small agency delivering IAP for a client app that needs audit-ready entitlements** — server-authoritative state driven by Notifications V2 (not "unlock on the client and hope"), so entitlement survives app deletion, refunds, and billing-grace renewals.
Why this pack
| Approach | Cost | Time | Correctness risk | Lock-in |
|---|
| **Bare agent, no pack** | Agent tokens only | Unpredictable — often several rejection round-trips (each a multi-day Apple review cycle) before it's right | High. See the five traps below — this is exactly where general-purpose agents guess wrong, because the failure modes are undocumented or buried in Apple's own docs | None up front, but the same traps recur every time you touch this code without the decisions written down |
| **This pack** | $129 one-time | 2–3 agent sessions on a $20/mo plan (per `PRD.md`); `verify.sh` itself budgeted under 10 minutes on a clean machine | Mitigated by pinned decisions, an offline-verifiable JWS test chain, and the preflight lane — not eliminated: App Review is still Apple's call, never guaranteed | None. Plain Node + SQLite server you own outright, no per-MAU fee, no vendor SDK in your app beyond Apple's own StoreKit |
| **RevenueCat-class SaaS** | Free tier, then a recurring percentage of tracked revenue | Fast to wire in — you integrate their SDK and dashboard rather than owning the server | Low — mature, handles the same traps | Your entitlement history lives in their system; migrating later means re-deriving it. Genuinely the better choice if you need cross-platform (Android) parity or built-in paywall analytics/A-B testing — this pack doesn't do either |
| **Hand-rolling from Apple's docs** | Your time only, no fee, no recurring percentage | Longest — reading WWDC sessions, the App Store Server API docs, and StoreKit 2 docs, then writing chain verification from scratch | Highest — you find the five traps below by shipping and getting rejected, or in a security review months later | None — full ownership, but no test suite proving correctness until you write one yourself |
**The five traps this pack pre-solves:**
- **JWS chain verification** — the deprecated `verifyReceipt` endpoint is the path of least resistance for an agent that hasn't been told otherwise; this pack pins local `SignedDataVerifier` verification and greps the repo for banned legacy symbols in preflight.
- **4040010 sandbox routing** — TestFlight/sandbox transactions returning "TransactionIdNotFound" from the *production* Server API is an undocumented gap; this pack hardcodes production-first, retry-on-4040010-against-sandbox routing so it isn't rediscovered by trial and error.
- **Paid-agreement empty products** — `Product.products(for:)` silently returns an empty array until the Paid Applications agreement, banking, and tax forms are complete in App Store Connect — nothing in Xcode tells you why. The onboarding calls this out as the single most common "products won't load" cause before you burn hours debugging code that was never broken.
- **Privacy-manifest reason codes** — required-reason API declarations (like `CA92.1` for `UserDefaults`) sit in a long enum buried in Apple's docs; this pack pre-fills the manifest and lints it in preflight.
- **Review-rejection preflight** — guidelines 2.1, 3.1.1, 3.1.2, 5.1.2, and export compliance are all things Xcode lets you submit and Apple rejects 2–3 days later; the preflight lane statically catches what's checkable before you burn a review cycle finding out.
Scale envelope
This pack ships shipping machinery, not a serving system — the numbers below are about the entitlements server, not app-store-wide traffic. Derived from the shipped architecture, not aspiration — and labeled as engineering estimates, since none of this is load-tested at this pack's price point.
- **Entitlements store (subscriber ceiling):** the server is a single Node process backed by `node:sqlite` on one Railway volume — no clustering, no read replica. SQLite serializes writes through one process, but comfortably absorbs this write rate as the sole writer — this server writes roughly once per verified transaction and once per notification — order of a handful of rows per subscriber per *month* (a renewal, occasionally a grace-period bump). That comfortably covers an app in the range of **roughly 25,000–50,000 active subscribers** on a single small Railway instance before instance-level limits (not subscriber count) become the constraint.
- **First ceiling:** not subscriber count — it's concurrency and durability. One Node process is a single point of failure with no horizontal scaling; a burst of many simultaneous purchases (a launch spike, a price-drop promo) serializes through `node:sqlite`'s single-writer lock instead of parallelizing; and one Railway volume is your entire durability story until you back it up.
- **App Store Server Notification volume:** notifications fire once per subscription lifecycle event, not per app session — a monthly subscriber generates roughly 12 renewal notifications a year (annual: 1), plus one-off `SUBSCRIBED` / `EXPIRED` / `REFUND` / `GRACE_PERIOD` events. At 25,000 monthly-heavy subscribers that's on the order of **800–1,000 notifications/day** — well inside the under-5-second response budget Apple requires, for a server this small.
- **Upgrade path:** `server/src/store.js` is the only file that talks to the database (every read and write in the pack routes through it) — swapping it for a Postgres-backed implementation when you outgrow single-instance SQLite is a contained, single-file change, not a rewrite of the JWS verification or notification-handling logic. `ARCHITECTURE.md` documents the adjacent `node:sqlite` → `better-sqlite3` driver swap as the escape hatch for the (cosmetic) experimental-warning issue; moving to Postgres for horizontal scale is the natural next step through that same seam.
**What it costs to skip the pack (modelled, not measured):** building this rig yourself the way most people actually work — one agent on a top-tier model, no cheap-tier routing, no parallel subagents, no prompt-caching discipline — models out at roughly **$90–370 in API tokens, likely around $185**. That's this pack's own measured build volume repriced at Opus-class rates, multiplied by an exploration factor for the five traps above and the 15 pinned decisions behind them; the pack-guided build itself measured ≈$7 of tokens per verification run (August 2026 rates), because tasks route down to cheaper tiers. Honest reading: at the likely figure that clears the $129 price, but at the low end it doesn't — treat the token math as break-even. The number tokens can't price is the rejection round-trip: every wrong guess here costs a multi-day App Review cycle, not an API call.
Verified against
- claude-opus-5
- claude-sonnet-5
- claude-haiku-4-5
Supported stacks
- swiftui-storekit2-node
| xcode | 26.6 |
| ios-sdk | 26.5 |
| swift | 6.3 |
| swift-tools-version | 6.2 |
| min-ios-deployment | 17.0 |
| fastlane | 2.237.0 |
| ruby | 3.4.10 |
| node | 24.18.1 |
| @apple/app-store-server-library | 3.1.0 |
| jsonwebtoken | 9.0.2 |
Estimated buyer token cost
730,000 tokens for one build pass (estimate, not a guarantee).
FAQ
What if the build fails?
Run `./verify.sh` — it prints one `CHECK: <name> PASS|FAIL` line per check plus the failing log. If it can't reach `VERIFY PASSED` on a clean checkout with the pinned toolchain (Xcode 26.6, Node 24.18.1, Ruby 3.4.10) — not after you've hand-edited the scaffold — that's a pack defect, not a buyer mistake. Full refund within 14 days of purchase if `verify.sh` fails on a supported configuration (the pinned stack and a model from the pack's matrix) — reply to your Stripe receipt email with the verify output or `FAILURE-REPORT.md` attached.
Do I need a Mac?
Yes, hard requirement. The pack builds a Swift package and drives Xcode/fastlane — there's no Linux or Windows path for any part of it.
What models does this need, and what plan?
Verified against `claude-opus-5`, `claude-sonnet-5`, and `claude-haiku-4-5`. Tasks route to the cheapest tier that can do them — only one task in the whole build needs the top tier. Budgeted at 2–3 sessions on a $20/mo plan — the binding constraint is prompt volume inside Claude's 5-hour rolling window and weekly cap, not raw tokens, and Anthropic doesn't publish an exact token-per-window figure, so treat the session count as an expectation, not a guarantee. Plan on **~730k fresh tokens** (input + output) for one build pass; the receipt separately reports ~11.4M tokens of total billed input (95% cache reads, billed near a tenth of fresh-input price) and a measured **$7.18 per verification run** — the most reliable of the three numbers.
Will this get my app approved by App Review?
The preflight lane covers the top statically-checkable rejection causes (missing restore button, missing privacy manifest, legacy receipt symbols, missing policy links, export-compliance flag) — but App Review is Apple's call, and this pack never guarantees approval, only that you're not submitting with the avoidable mistakes still in.
Do I own the code afterward?
Yes. It's a Swift package and a plain Node service in your own repo — no vendor SDK beyond Apple's own StoreKit, no hosted dashboard you depend on to keep functioning.
How do I test before going live — sandbox vs. production?
Locally, the bundled `Products.storekit` file drives StoreKit Testing in Xcode with no network calls. Once deployed, App Store Connect gives you a sandbox tester account for real device testing; the server routes production-first with an automatic sandbox fallback on error code 4040010, and both a Production and a Sandbox notification URL point at the same endpoint, which reads `data.environment` to pick the matching verifier — you don't configure anything differently between the two.
Can I customize or extend after the build?
Yes — `ARCHITECTURE.md` documents the extension points: promotional/win-back offers (reusing the same ASC key infrastructure), consumables (add a `grants` table; transactions already flow through `/verify`), and App Store Server API polling as a reconciliation cron if notifications are ever missed.
What if I don't already have an Xcode project?
Creating a bare SwiftUI app in Xcode is a 2-minute human step on `MANUAL-CHECKLIST.md` (item 6) — this pack integrates into an existing project rather than generating one from scratch.
Specific problems this pack pre-solves
Prescribed services
Disclosure: some links on this page are affiliate links. We may earn a commission if you sign up through them, at no extra cost to you. We only link to services the pack actually verified against.