feat: complete commerce implementation guide - #445
Conversation
Connect the guided documentation to the runnable example and IAPKit. Add four-store ownership and erasure handling, reproducible provider replacement evidence, and static discovery with consistent navigation and transitions.
e959b1a to
f0703f7
Compare
|
Important Review skippedToo many files! This PR contains 196 files, which is 96 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (27)
📒 Files selected for processing (196)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #445 +/- ##
==========================================
+ Coverage 76.00% 76.16% +0.15%
==========================================
Files 155 157 +2
Lines 16561 16674 +113
Branches 4763 4807 +44
==========================================
+ Hits 12588 12700 +112
- Misses 3973 3974 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…sed base The docs build pins the interop run's monorepo inputs, and #444 changed the root bun.lock and package.json that the recording had pinned, so the check refused the rebased tree. Re-ran the example -> IAPKit -> example harness on this base with the example at f23f663 (the revision the recording already matched) and exported it: 162 checks, identical outcomes and store coverage; only timestamps, job ids and the recorded revisions moved. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…it reads Kit: an ownership recheck that cannot reach Amazon or Horizon now fails the entitlements read as VERIFICATION_FAILED (502) like verify, instead of a generic 500; the ownership-moved-mid-read race is CONFLICT (409) instead of a 400 that no client would retry. Route tests cover both plus the Amazon binding identity. COMMERCE-EVENTS.md states what the erasure tombstone means for a returning customer. Docs: the purchase walkthrough names the provider role, not IAPKit, in the Amazon/Horizon recheck step; store labels come from one constant; prose that narrated how the evidence was produced is gone; the implementation page has one name and correct heading levels; collapsing the walkthrough no longer scrolls the viewport to the diagram. The build now checks that every source deep link (#L<n>) still lands on its symbol, so a regenerated snapshot cannot silently move them. Unknown and client-redirected routes fall back to a bare shell (dist/_app.html) rather than the prerendered homepage with its canonical URL, and SVG icon titles no longer count as the document title. Rules: the acceptance section asks for the runnable example plus at least one independent implementation rather than naming one vendor, and its evidence requirements are ones a PR can actually carry. The interop evidence is re-recorded on these sources. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… rebind, and evidence gate - bindPurchase binds only ENTITLED evidence and caps 20 bound purchases per app account; the 21st answers bound:false (SPEC 4.4) and is logged - entitlements rechecks pay their own admission bucket (300 tokens, 5/s, one per bound purchase) before any store call and skip unchanged verdicts - erased Amazon/Horizon evidence may bind again to another app user; the erased user id stays refused while its erasure job is retained - the docs build checks only the evidence's own consistency; the new `bun run audit:commerce-evidence` reports drift against the current sources as an advisory CI warning, and recordings from an uncommitted tree are marked -dirty - re-recorded interop evidence: 170 checks Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Same 170 checks; the recording now names commit 3bf9168 instead of an uncommitted tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the cap check runs before the evidence lookup, so neither the answer nor the operator log depends on someone else's purchase (SPEC 4.4) - binding also requires isValid; revoked rows still occupy the cap - pin the Amazon recheck skip, the recheck budget on failed reads, the log payload, shortRevision, and the harness inventory in tests - COMMERCE-EVENTS: the erasure window refuses the erased user's own retries; the read keeps the 20 bound as a backstop Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Same 170 checks; the recording names the committed sources instead of an uncommitted tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Grok review of the changes in Left as they are: a skipped recheck does not touch |
The SwiftUI example under `packages/apple/Example` no longer compiled, so the iOS row of the device regression could not run at all. Two drifts had accumulated against the generated types: - The example read `purchase.platform`. That field is now `store`, typed `IapStore`. Three call sites still used the old name. - Two `ProductTypeIOS` switches predate the `subscription-bundle` and `subscription-suite` cases, so they stopped being exhaustive. Checks: `xcodebuild -project Martie.xcodeproj -scheme OpenIapExample -configuration Debug -destination "generic/platform=iOS"` now succeeds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added display support for subscription bundle and subscription suite product types, including labels and color indicators. * **Bug Fixes** * Updated purchase details and success logging to show the correct store information. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
) Three environment conditions each cost a full debugging session during a Martie live-receipt run, and each looked like a store or account failure. - The generated `android/` directory keeps whichever store it was last prebuilt for. After the FireOS or Horizon row, a Play run links the wrong `openiap-google` flavor and stalls on `Connecting to Store...`. - iOS restores a scene session left by another app sharing the `dev.hyo.martie` bundle id, so UIKit attaches that app's scene delegate and the example's own `SceneDelegate` never starts React Native. Black screen, live process, no bundle request, no crash. Only a real uninstall clears it. - Expo links React Native as a prebuilt binary, and that slice compiles without `DEBUG`, so a Debug build can never reach Metro. Two smaller ones join them: reinstalling resets the iOS local-network grant, and the local origin is inlined when Metro starts, so a stale value quietly sends verification to the hosted service. Also records that a Play "not compatible with your device" banner does not block billing for a side-loaded license-tester build. Checks: `bunx prettier --check` on both files; the pre-commit agent-surface, layout, parity, sponsor, and kit-contract audits ran clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Added troubleshooting guidance for environment and workspace states that can cause misleading store or account failures during local receipt testing. - Documented Android store-flavor mismatches, Play compatibility warnings, persistent iOS scene sessions, prebuilt React Native limitations, local-network permission resets, and platform-specific local origins. - Added a prerequisite directing operators to review these troubleshooting notes before running the local receipt workflow. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The ownership recheck added two codes the operation never declares: a store fault surfaced as VERIFICATION_FAILED (502) and a purchase bound mid-read as CONFLICT (409). The Commerce Protocol conformance runner rejects any code a manifest does not list, so a real run against this build would have failed on its own specification. A store fault now answers INTERNAL_ERROR, which the operation does declare and which is what an unreachable dependency is. The mid-read binding no longer errors at all: the re-read keeps only the rows this pass actually reconfirmed, so a purchase that appeared after the recheck is simply absent, which is what fail-close already means for this operation. The bound-purchase read cap also answered INVALID_REQUEST, blaming the caller for stored rows drifting past a limit that binding enforces. It is ours, so it reports INTERNAL_ERROR too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Re-runs the 170 interoperability checks against the committed sources and moves the storeAccess anchor to the line the action now starts on. The description picks up the one behaviour that changed: a purchase bound after the recheck pass is absent from the answer instead of failing the read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… an account Three defects the review found, none of them new to this branch. An Amazon receipt the account no longer holds kept its slot in the 20-purchase bind cap for good: nothing ever cleared `appUserId`, and there is no unbind operation, so twenty refunds answered `bound: false` for the twenty-first legitimate purchase — indistinguishable from unknown evidence under §4.4. A CANCELED or INAUTHENTIC Amazon verdict now releases the binding, in every lane and before the unchanged-verdict return, since a resubscribe issues a new receipt id. Horizon keeps its binding: its rejection is a point-in-time answer on a row keyed by user and sku, which the same customer reuses. Subscription erasure meant something different from purchase erasure. Purchases treat it as unlinking the previous owner; subscriptions refused every later bind forever, and a token rotation could carry one row's marker onto the other row's live binding and revoke it. Both handlers now bind and clear the marker as purchases do, the pending-erasure gate is untouched, and a live binding survives a merge instead of being overwritten by a marker. The entitlements read keeps failing closed. An earlier attempt here omitted an un-integrated store instead; a project cannot hold bound rows for a store it never integrated, so the only case that fired was a store disabled after it had granted access — a partial answer the specification forbids. Also moves three shared components out of the docs `pages/` tree, which holds route files only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…verdict Releasing the binding the moment a store said CANCELED changed what the entitlements read means: a later positive recheck could no longer restore access, and the interop run proved the two implementations had diverged on it. Reclaim at the point of contention instead. When the twenty-first binding arrives, IAPKit first releases the caller's own bound purchases the store no longer honours, and refuses only if none can be freed. A row keeps its binding until the cap is actually contended, so the read is untouched, and refunded receipts can no longer hold an account's slots for good. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Re-runs the 170 interoperability checks against the committed sources and moves the erasure anchor to the line its function now starts on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`playwright install --with-deps` runs `apt-get update`, which fails whenever Google's Chrome repository serves an index whose hash does not match what its Release file promises. That happened today and failed the docs E2E and kit build jobs twice with nothing wrong in the tree. Playwright downloads its own chromium, so the runner image's Chrome apt source is dead weight here. Remove it before the install. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pping it The recheck loop passed over a bound row whose stored store is neither amazon nor horizon. Binding admits only those two, so such a row is state the read cannot classify — exactly what SPEC "Fail-close" says must fail the operation rather than be answered around. It now raises INTERNAL_ERROR. Also corrects two claims. The handler comment still described the per-store skip that was reverted, and the documentation told operators to release the rows of a disabled store: no such lever exists, because the recheck throws before anything can mark them invalid, so the bind cap never reclaims them and only eraseUser clears the binding. Re-enabling the store is the way back. Tests: the reclaim case now pins that the live rows keep their binding, the fail-closed case names the error and proves the read stopped before re-reading ownership, and a duplicated rate-limit case is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-ups Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…es not exist It offered "release the rows" and then said nothing can release them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commerce Protocol now walks readers through purchase, verification, ownership, access, events, and account deletion, with IAPKit and the runnable example alongside each step. The architecture opens explanations in place; navigation, nested accordions, and transitions follow the same documentation layout.
IAPKit binds Amazon and Horizon purchases to authenticated app accounts and rechecks ownership before returning access. Only entitled evidence binds, at most 20 purchases per app account; the 21st binding answers
bound: false(SPEC §4.4) and is logged, and the read keeps the same bound as a backstop. Rechecks draw on their own admission bucket (300 tokens, 5/s, one per bound purchase) before any store call and do not write back an unchanged verdict. Erasure refuses the erased app user id while its job is retained; another app account may bind the same Amazon or Horizon evidence afterwards as a first binding. Apple and Google subscription rows keep their permanent erasure marker.The example → IAPKit → example check keeps one app backend and receiver unchanged: 170 assertions, recorded at the committed sources. The docs build verifies that the report, snapshots, hashes, and
#L<n>anchors agree with each other; freshness against the current IAPKit sources is the advisorybun run audit:commerce-evidence, run withcontinue-on-errorin the web E2E job, and a recording from an uncommitted tree is marked-dirty. Static HTML, canonical metadata, sitemap generation, and readable AI entry points make the same guides available without JavaScript.Companion example: hyodotdev/openiap-commerce-protocol-example#1.
Checks: IAPKit lint and the full IAPKit test suite (one existing skip), compiled-server smoke, the protocol suite, 170 provider-replacement assertions, the composition and source-provenance checks, 144 prerendered pages, and the SDK parity, docs, layout, CI-path, and agent-surface audits pass locally.
Review: CodeRabbit skips this diff (over its 100-file limit) and Codex is over its usage limit until Sep 15, so this head was reviewed by three independent read-only review-self lenses (kit correctness, tests and tooling, docs and protocol) and by Grok on the pasted diff. Every finding that did not need a product decision was fixed in
3bf91680and the follow-up commit; the Grok notes left as they are, with reasons, are in the PR comments.Merge gate: device regression remains pending for the
packages/kitMartie live-receipt rows on iOS and Android/Play. The local four-store checks use synthetic evidence and mocked store responses; no real Amazon, Horizon, Apple, or Google checkout is claimed. Cross-company adoption and production migration are not established by this run.Preview
commerce-docs.webm
🤖 Generated with Claude Code