From bfc9434ef289486eae069a49bf5115e9581dfac1 Mon Sep 17 00:00:00 2001 From: hyochan Date: Wed, 19 Aug 2026 02:23:59 +0900 Subject: [PATCH 1/6] docs: refresh amazon and iapkit surfaces Add Amazon sponsorship across the site and position IAPKit as optional infrastructure. Align hosted-service guidance, keep the pinned test runtime compatible, and enforce the canonical repository layout. --- .github/workflows/ci.yml | 5 + .husky/pre-commit | 5 + .vscode/settings.json | 11 +- AGENTS.md | 10 +- README.md | 11 +- bun.lock | 18 +- gql/.vscode/settings.json | 11 -- knowledge/_claude-context/context.md | 30 ++- knowledge/internal/02-architecture.md | 28 ++- package.json | 1 + .../conformance/CONFORMANCE_AUDIT.md | 0 packages/conformance/README.md | 4 + packages/docs/README.md | 9 +- packages/docs/SHOWCASE.md | 4 +- packages/docs/public/llms-full.txt | 28 ++- packages/docs/public/llms.txt | 2 +- .../docs/public/sponsors/amazon-dark.webp | Bin 0 -> 15858 bytes packages/docs/public/sponsors/amazon.webp | Bin 0 -> 18206 bytes .../docs/src/components/AmazonWordmark.tsx | 28 +++ .../docs/src/components/EcosystemDiagram.tsx | 33 +++- packages/docs/src/components/MetaWordmark.tsx | 10 +- .../docs/src/components/ShowcaseCards.tsx | 126 +++++++------ packages/docs/src/lib/showcase.ts | 2 +- packages/docs/src/lib/sponsors.ts | 35 ++++ .../docs/src/pages/community-resources.tsx | 21 ++- packages/docs/src/pages/docs/ecosystem.tsx | 23 ++- .../src/pages/docs/features/validation.tsx | 17 +- .../src/pages/docs/foundation/one-pager.tsx | 6 +- .../src/pages/docs/foundation/sponsorship.tsx | 46 +++-- packages/docs/src/pages/docs/setup/maui.tsx | 18 +- .../src/pages/docs/updates/announcements.tsx | 44 +++-- packages/docs/src/pages/home.tsx | 46 +++-- packages/docs/src/pages/showcase.tsx | 5 +- packages/docs/src/pages/sponsors.tsx | 40 ++-- packages/docs/src/styles/base.css | 34 ++++ .../docs/src/styles/community-resources.css | 49 ++++- .../docs/src/styles/ecosystem-diagram.css | 21 ++- packages/docs/src/styles/explore-pages.css | 35 +++- packages/docs/src/styles/home.css | 56 ++++-- packages/kit/CONVENTION.md | 42 +++-- packages/kit/README.md | 17 +- packages/kit/SECURITY.md | 2 +- packages/kit/index.html | 24 ++- packages/kit/package.json | 4 +- packages/kit/public/llms-full.txt | 39 +++- packages/kit/public/llms.txt | 26 ++- packages/kit/public/manifest.json | 2 +- packages/kit/public/og-preview.webp | Bin 14862 -> 13008 bytes packages/kit/scripts/generate-og-image.js | 8 +- packages/kit/src/components/Footer.tsx | 5 +- packages/kit/src/content/privacy-policy.md | 34 +++- packages/kit/src/content/terms-of-service.md | 8 +- .../src/pages/auth/organization/create.tsx | 6 +- packages/kit/src/pages/blog/index.tsx | 2 +- packages/kit/src/pages/docs/nav.ts | 20 ++ packages/kit/src/pages/docs/routes.tsx | 8 + .../kit/src/pages/docs/sections/analytics.tsx | 18 +- .../src/pages/docs/sections/compatibility.tsx | 88 +++++++++ .../src/pages/docs/sections/introduction.tsx | 15 +- .../kit/src/pages/docs/sections/orders.tsx | 82 +++++++++ .../kit/src/pages/docs/sections/projects.tsx | 26 ++- .../src/pages/docs/sections/quickstart.tsx | 18 +- .../src/pages/docs/sections/release-notes.tsx | 97 ++++++++++ .../docs/sections/verification-amazon.tsx | 172 ++++++++++++++++++ .../kit/src/pages/docs/sections/webhooks.tsx | 111 +++++++++++ packages/kit/src/pages/index.tsx | 11 +- packages/kit/src/pages/landing.tsx | 17 +- scripts/audit-repo-layout.mjs | 67 +++++++ scripts/audit-repo-layout.test.mjs | 49 +++++ 69 files changed, 1575 insertions(+), 315 deletions(-) delete mode 100644 gql/.vscode/settings.json rename docs/conformance-audit.md => packages/conformance/CONFORMANCE_AUDIT.md (100%) create mode 100644 packages/docs/public/sponsors/amazon-dark.webp create mode 100644 packages/docs/public/sponsors/amazon.webp create mode 100644 packages/docs/src/components/AmazonWordmark.tsx create mode 100644 packages/docs/src/lib/sponsors.ts create mode 100644 packages/kit/src/pages/docs/sections/compatibility.tsx create mode 100644 packages/kit/src/pages/docs/sections/orders.tsx create mode 100644 packages/kit/src/pages/docs/sections/verification-amazon.tsx create mode 100644 packages/kit/src/pages/docs/sections/webhooks.tsx create mode 100644 scripts/audit-repo-layout.mjs create mode 100644 scripts/audit-repo-layout.test.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62b8a8395..6fc4846b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,11 @@ jobs: with: node-version: 20 + - name: Audit repository layout + run: >- + node --test scripts/audit-repo-layout.test.mjs && + node scripts/audit-repo-layout.mjs + - name: Reject prerelease metadata on main run: node scripts/release-branch-policy.mjs audit diff --git a/.husky/pre-commit b/.husky/pre-commit index b3840f324..066a2a293 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -52,6 +52,11 @@ fi echo "🔎 SDK parity audit — running CI mirror…" node scripts/audit-non-godot-parity.mjs +# Canonical project directories must not be duplicated at repository root. +echo "🧭 repository layout audit…" +node --test scripts/audit-repo-layout.test.mjs +node scripts/audit-repo-layout.mjs + # Unconditional: either side of the kit/spec contract can move. echo "🔎 IAPKit spec contract audit — running CI mirror…" node --test scripts/audit-kit-spec-contract.test.mjs diff --git a/.vscode/settings.json b/.vscode/settings.json index c58eeca10..dd33559ca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,16 @@ { "cSpell.words": [ + "apollographql", + "codegen", + "gradlew", "hyodotdev", "Iapkit", + "JsonSlurper", "openiap", + "preorder", + "pubspec", "Skus", - "Slurper", - "JsonSlurper" + "Slurper" ], "files.associations": { "*.podspec": "ruby" @@ -47,4 +52,4 @@ "java.configuration.updateBuildConfiguration": "automatic", "gradle.nestedProjects": false, "eslint.workingDirectories": [{ "mode": "auto" }] -} \ No newline at end of file +} diff --git a/AGENTS.md b/AGENTS.md index 15c766369..134e32065 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,11 +21,12 @@ This document provides an overview for AI agents working across the OpenIAP mono ```text openiap/ ├── packages/ +│ ├── conformance/ # Behavioral conformance spec, runner, and reports │ ├── docs/ # Documentation site (React/Vite/Vercel) │ ├── gql/ # GraphQL schema & type generation │ ├── google/ # Android library │ ├── apple/ # iOS/macOS library -│ ├── kit/ # Hosted receipt-validation SaaS (Fly.io app) +│ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app) │ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp) ├── plugins/ │ └── openiap/ # Codex + Claude Code plugin (skills + MCP config) @@ -79,6 +80,13 @@ KISS and SSOT are mandatory release criteria. The canonical rules live in [`knowledge/internal/03-coding-style.md`](knowledge/internal/03-coding-style.md#0-kiss-and-ssot-are-release-requirements). Apply that section before implementation and during every review. +### Repository Layout + +Treat the directory ownership rules in +[`knowledge/internal/02-architecture.md`](knowledge/internal/02-architecture.md#directory-ownership-guardrail) +as mandatory. Extend the existing owner instead of creating a parallel root +directory, and run `bun run audit:layout` after adding or moving directories. + ### Comment Style Keep comments short — default to one line. AI-authored comments over-explain by diff --git a/README.md b/README.md index accc23d18..b0112306c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ This monorepo contains all OpenIAP packages: - **[spec](packages/gql)** - OpenIAP specification and type generation [![Spec Release](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fhyodotdev%2Fopeniap%2Fmain%2Fopeniap-versions.json&query=%24.spec&label=version&logo=graphql&color=purple&prefix=v)](https://github.com/hyodotdev/openiap/blob/main/openiap-versions.json) - **[google](packages/google)** - Android library [![Maven Central (Play)](https://img.shields.io/maven-central/v/io.github.hyochan.openiap/openiap-google?label=Play%20Store)](https://central.sonatype.com/artifact/io.github.hyochan.openiap/openiap-google) [![Maven Central (Horizon)](https://img.shields.io/maven-central/v/io.github.hyochan.openiap/openiap-google-horizon?label=Meta%20Horizon)](https://central.sonatype.com/artifact/io.github.hyochan.openiap/openiap-google-horizon) [![CI](https://github.com/hyodotdev/openiap/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/hyodotdev/openiap/actions/workflows/ci.yml?query=branch%3Amain) - **[apple](packages/apple)** - iOS/macOS library [![Swift Package](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fhyodotdev%2Fopeniap%2Fmain%2Fopeniap-versions.json&query=%24.apple&label=version&logo=swift&color=orange&prefix=v)](https://github.com/hyodotdev/openiap/releases?q=Apple&expanded=true) [![CocoaPods](https://img.shields.io/cocoapods/v/openiap?color=E35A5F&logo=cocoapods)](https://cocoapods.org/pods/openiap) [![CI](https://github.com/hyodotdev/openiap/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/hyodotdev/openiap/actions/workflows/ci.yml?query=branch%3Amain) -- **[kit](packages/kit)** - Hosted receipt-validation backend (SaaS + dashboard at [kit.openiap.dev](https://kit.openiap.dev)). Free for every developer under hosted fair-use safeguards. [![Kit CI](https://github.com/hyodotdev/openiap/actions/workflows/deploy-kit.yml/badge.svg?branch=main)](https://github.com/hyodotdev/openiap/actions/workflows/deploy-kit.yml?query=branch%3Amain) +- **[kit](packages/kit)** - Open-source purchase validation and entitlement infrastructure for the OpenIAP ecosystem, with a hosted service and dashboard at [kit.openiap.dev](https://kit.openiap.dev). Free for every developer under hosted fair-use safeguards. [![Kit CI](https://github.com/hyodotdev/openiap/actions/workflows/deploy-kit.yml/badge.svg?branch=main)](https://github.com/hyodotdev/openiap/actions/workflows/deploy-kit.yml?query=branch%3Amain) - **[mcp-server](packages/mcp-server)** - IAPKit MCP server, hosted at [kit.openiap.dev/mcp](https://kit.openiap.dev/mcp) for AI coding agents ## Libraries @@ -85,7 +85,14 @@ For bug reports, please [open an issue](https://github.com/hyodotdev/openiap/iss

- Meta + Meta + +        + + + + Amazon Developer +

diff --git a/bun.lock b/bun.lock index a379f8c0d..a5f2c9a5c 100644 --- a/bun.lock +++ b/bun.lock @@ -145,7 +145,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^15.15.0", - "jsdom": "^30", + "jsdom": "29.1.1", "lint-staged": "^16.1.5", "npm-run-all": "^4.1.5", "postcss": "~8.5.26", @@ -213,9 +213,13 @@ "@ardatan/relay-compiler": ["@ardatan/relay-compiler@13.0.2", "", { "dependencies": { "@babel/runtime": "^8.0.0", "immutable": "^5.1.9", "invariant": "^2.2.4" }, "peerDependencies": { "graphql": "*" } }, "sha512-VFpv9UP820SiwDUPYtq7PmD3jifzZlevkQ26bhbSzFeruSTys0eHzQCZyKg+IhgmZzwPI9AFjPe26ABNjGeIKg=="], - "@asamuzakjp/css-color": ["@asamuzakjp/css-color@6.0.7", "", { "dependencies": { "@csstools/css-calc": "^3.3.0", "@csstools/css-color-parser": "^4.1.10", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "lru-cache": "^11.5.2" } }, "sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw=="], + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.11", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="], - "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@8.3.2", "", { "dependencies": { "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.5.2" } }, "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q=="], + "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.1.1", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ=="], + + "@asamuzakjp/generational-cache": ["@asamuzakjp/generational-cache@1.0.1", "", {}, "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg=="], + + "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], "@auth/core": ["@auth/core@0.41.3", "", { "dependencies": { "@panva/hkdf": "^1.2.1", "jose": "^6.0.6", "oauth4webapi": "^3.3.0", "preact": "10.24.3", "preact-render-to-string": "6.5.11" }, "peerDependencies": { "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.2", "nodemailer": "^7.0.7 || ^8.0.5" }, "optionalPeers": ["@simplewebauthn/browser", "@simplewebauthn/server", "nodemailer"] }, "sha512-sJ3JMHHkXMD3aOjopv7mOBTO1Ocw4b0fAEXJBz6k7YHLpYQI6C40jCUPc5fNvUKxXRXNE1/sRISA15UrwWJBTw=="], @@ -1667,7 +1671,7 @@ "js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], - "jsdom": ["jsdom@30.0.1", "", { "dependencies": { "@asamuzakjp/css-color": "^6.0.5", "@asamuzakjp/dom-selector": "^8.3.0", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.7", "@exodus/bytes": "^1.15.1", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.5.2", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.2", "undici": "^8.9.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^17.1.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.2.3" }, "optionalPeers": ["canvas"] }, "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA=="], + "jsdom": ["jsdom@29.1.1", "", { "dependencies": { "@asamuzakjp/css-color": "^5.1.11", "@asamuzakjp/dom-selector": "^7.1.1", "@bramus/specificity": "^2.4.2", "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@exodus/bytes": "^1.15.0", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.3.5", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.1", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -2361,7 +2365,7 @@ "unc-path-regex": ["unc-path-regex@0.1.2", "", {}, "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg=="], - "undici": ["undici@8.10.0", "", {}, "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ=="], + "undici": ["undici@7.29.0", "", {}, "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], @@ -2419,7 +2423,7 @@ "whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], - "whatwg-url": ["whatwg-url@17.1.0", "", { "dependencies": { "@exodus/bytes": "^1.15.1", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw=="], + "whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -2585,8 +2589,6 @@ "convex/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="], - "data-urls/whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], - "dir-glob/path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], diff --git a/gql/.vscode/settings.json b/gql/.vscode/settings.json deleted file mode 100644 index c1659144e..000000000 --- a/gql/.vscode/settings.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "cSpell.words": [ - "apollographql", - "codegen", - "gradlew", - "openiap", - "preorder", - "pubspec", - "skus" - ] -} \ No newline at end of file diff --git a/knowledge/_claude-context/context.md b/knowledge/_claude-context/context.md index cd1f62154..303dfcc5a 100644 --- a/knowledge/_claude-context/context.md +++ b/knowledge/_claude-context/context.md @@ -1,7 +1,7 @@ # OpenIAP Project Context > **Auto-generated for Claude Code** -> Last updated: 2026-08-13T01:31:14.251Z +> Last updated: 2026-08-18T16:58:40.514Z > > Usage: `claude --context knowledge/_claude-context/context.md` @@ -287,11 +287,12 @@ const IsSubscription: boolean; // No PascalCase for variables ``` openiap/ ├── packages/ +│ ├── conformance/ # Behavioral conformance spec, runner, and reports │ ├── docs/ # Documentation (React/Vite/Vercel) │ ├── gql/ # GraphQL schema & type generation │ ├── google/ # Android library (Kotlin) │ ├── apple/ # iOS/macOS library (Swift) -│ ├── kit/ # Hosted receipt-validation SaaS (Fly.io app) +│ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app) │ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp) ├── plugins/ │ └── openiap/ # Codex + Claude Code plugin (skills + MCP config) @@ -313,6 +314,31 @@ openiap/ Libraries reference local `packages/apple` and `packages/google` source directly (not published CocoaPods/Maven artifacts), enabling immediate development without waiting for native releases. +## Directory Ownership Guardrail + +Keep each project surface under its canonical owner: + +| Content | Canonical location | +| ------------------------------------------------ | ----------------------- | +| Deployable packages and native implementations | `packages//` | +| Framework SDKs | `libraries//` | +| Agent integrations distributed to users | `plugins//` | +| Behavioral conformance spec, runner, and reports | `packages/conformance/` | +| Repository knowledge | `knowledge/` | +| Repository-wide automation | `scripts/` | +| Shared editor settings | `.vscode/` | + +- Never create a root directory that duplicates a child of `packages/`, + `libraries/`, or `plugins/`. For example, use `packages/docs/` and + `packages/gql/`, never root `docs/` or `gql/`. +- Before adding a top-level directory, search for an existing owner and extend + it. Add a new root only when no canonical owner fits, and document that owner + in this section in the same change. +- Keep shared editor settings in root `.vscode/`. Package-specific settings are + allowed only when they apply exclusively to that package's toolchain. +- Run `bun run audit:layout` after directory changes. Pre-commit and CI enforce + the same audit; do not weaken it to permit a duplicate owner. + ## Package Responsibilities ### packages/gql diff --git a/knowledge/internal/02-architecture.md b/knowledge/internal/02-architecture.md index af4ca15e3..06e597bc0 100644 --- a/knowledge/internal/02-architecture.md +++ b/knowledge/internal/02-architecture.md @@ -8,11 +8,12 @@ ``` openiap/ ├── packages/ +│ ├── conformance/ # Behavioral conformance spec, runner, and reports │ ├── docs/ # Documentation (React/Vite/Vercel) │ ├── gql/ # GraphQL schema & type generation │ ├── google/ # Android library (Kotlin) │ ├── apple/ # iOS/macOS library (Swift) -│ ├── kit/ # Hosted receipt-validation SaaS (Fly.io app) +│ ├── kit/ # Purchase validation + entitlement infrastructure (Fly.io app) │ └── mcp-server/ # IAPKit MCP server (hosted at kit.openiap.dev/mcp) ├── plugins/ │ └── openiap/ # Codex + Claude Code plugin (skills + MCP config) @@ -34,6 +35,31 @@ openiap/ Libraries reference local `packages/apple` and `packages/google` source directly (not published CocoaPods/Maven artifacts), enabling immediate development without waiting for native releases. +## Directory Ownership Guardrail + +Keep each project surface under its canonical owner: + +| Content | Canonical location | +| ------------------------------------------------ | ----------------------- | +| Deployable packages and native implementations | `packages//` | +| Framework SDKs | `libraries//` | +| Agent integrations distributed to users | `plugins//` | +| Behavioral conformance spec, runner, and reports | `packages/conformance/` | +| Repository knowledge | `knowledge/` | +| Repository-wide automation | `scripts/` | +| Shared editor settings | `.vscode/` | + +- Never create a root directory that duplicates a child of `packages/`, + `libraries/`, or `plugins/`. For example, use `packages/docs/` and + `packages/gql/`, never root `docs/` or `gql/`. +- Before adding a top-level directory, search for an existing owner and extend + it. Add a new root only when no canonical owner fits, and document that owner + in this section in the same change. +- Keep shared editor settings in root `.vscode/`. Package-specific settings are + allowed only when they apply exclusively to that package's toolchain. +- Run `bun run audit:layout` after directory changes. Pre-commit and CI enforce + the same audit; do not weaken it to permit a duplicate owner. + ## Package Responsibilities ### packages/gql diff --git a/package.json b/package.json index 4725662d9..9659db3a6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "lint": "bun run --filter '*' lint", "e2e:web": "node scripts/e2e-web-sites.mjs", "audit:deprecations": "node --test scripts/audit-deprecation-schedule.test.mjs && node scripts/audit-deprecation-schedule.mjs", + "audit:layout": "node --test scripts/audit-repo-layout.test.mjs && node scripts/audit-repo-layout.mjs", "audit:parity": "node scripts/audit-non-godot-parity.mjs", "audit:kit-contract": "node --test scripts/audit-kit-spec-contract.test.mjs && node scripts/audit-kit-spec-contract.mjs", "audit:docs": "bun run scripts/audit-docs.ts", diff --git a/docs/conformance-audit.md b/packages/conformance/CONFORMANCE_AUDIT.md similarity index 100% rename from docs/conformance-audit.md rename to packages/conformance/CONFORMANCE_AUDIT.md diff --git a/packages/conformance/README.md b/packages/conformance/README.md index c5d5c9dad..fdc1f7f8e 100644 --- a/packages/conformance/README.md +++ b/packages/conformance/README.md @@ -14,6 +14,10 @@ packages/gql/src/capability-matrix.mjs which stores must implement what packages/conformance/src/spec/ what each behavior must do <- you are here ``` +See the [Conformance Testing Audit](https://github.com/hyodotdev/openiap/blob/main/packages/conformance/CONFORMANCE_AUDIT.md) +for the repository assessment that led to this suite and the remediation +history. + ## Versioning A report states two versions, and neither is optional: diff --git a/packages/docs/README.md b/packages/docs/README.md index 218849d5c..25918f118 100644 --- a/packages/docs/README.md +++ b/packages/docs/README.md @@ -20,6 +20,13 @@ Visit [openiap.dev](https://openiap.dev) for full documentation. Meta +        + + + + Amazon Developer + +

-Thank you to our sponsors for supporting the OpenIAP initiative. [Become a sponsor](https://openiap.dev/sponsors) \ No newline at end of file +Thank you to our sponsors for supporting the OpenIAP initiative. [Become a sponsor](https://openiap.dev/sponsors) diff --git a/packages/docs/SHOWCASE.md b/packages/docs/SHOWCASE.md index d4dcbb7a5..0f82352b5 100644 --- a/packages/docs/SHOWCASE.md +++ b/packages/docs/SHOWCASE.md @@ -63,7 +63,7 @@ Leave `ratings` and `installs` out of your PR — the script fills them in. | `logo` | ✅ | Path under `packages/docs/public` (e.g. `/showcase/your-app.webp`) or a full https URL. | | `ratings` / `installs` | — | Maintainer-managed ordering metrics. Leave these out. | | `library` | ✅ | One of `expo-iap`, `react-native-iap`, `flutter_inapp_purchase`, `kmp-iap`, `maui-iap`, `godot-iap`. | -| `iapkit` | — | Set to `true` only when the app uses IAPKit receipt validation. Omit it otherwise. | +| `iapkit` | — | Set to `true` only when the app uses IAPKit. Omit it otherwise. | | `ios` | — | App Store URL. | | `android` | — | Google Play URL. | | `web` | — | Website or other store, shown as "Website". | @@ -75,7 +75,7 @@ are skipped at render time. Reply to [discussion #350](https://github.com/hyodotdev/openiap/discussions/350) or email **hyo@hyo.dev** with your app name, one-liner, logo, store links, and which -library you use. Also tell us whether you use IAPKit for receipt validation. +library you use. Also tell us whether you use IAPKit. ## Removal and updates diff --git a/packages/docs/public/llms-full.txt b/packages/docs/public/llms-full.txt index 84c345cd5..d7a0d2f58 100644 --- a/packages/docs/public/llms-full.txt +++ b/packages/docs/public/llms-full.txt @@ -3,7 +3,7 @@ > OpenIAP: Unified in-app purchase specification for iOS & Android > Documentation: https://openiap.dev > Quick Reference: https://openiap.dev/llms.txt -> Generated: 2026-08-17T10:30:38.945Z +> Generated: 2026-08-17T23:14:29.027Z ## Table of Contents 1. Installation @@ -2053,7 +2053,8 @@ transient or malformed responses. # IAPKit -> Receipt-validation SaaS managed by OpenIAP. Hosted at https://kit.openiap.dev. +> Open-source purchase validation and entitlement infrastructure for the +> OpenIAP ecosystem, managed by OpenIAP. Hosted at https://kit.openiap.dev. > One Bearer-authed endpoint for Apple / Google / Horizon / Amazon; > harmonized response shape with `{ store, isValid, state, productId?, environment? }` so your backend has a single code path for > entitlement + refund detection. @@ -2174,6 +2175,25 @@ Harmonized `state` values (truthy `isValid`): `ENTITLED`, - A matching IAPKit catalog row must exist before a payload write. After creating a product directly in App Store Connect or Play Console, run a pull sync and wait for its job to succeed, or create the row with secret-authenticated `POST /v1/products`; early writes return `PRODUCT_NOT_FOUND`. - Secret-key `GET /v1/products/client-payload/{productId}?platform=...` returns the current payload plus durable `expectedVersion`, including the revision after deletion. MCP exposes it as `iapkit_get_client_payload`. +## Dashboard order lookup + +The authenticated project dashboard includes a read-only Orders tab for live +Apple and Google order lookup. It reuses the project's store credentials, +returns the order plus best-effort subscription status, and never stores or +logs the order ID or result. Apple lookup is production-only and needs the App +Apple ID; Google requires the Play service account's View financial data +permission. This operator tool does not accept API keys and is not part of +`/v1`. + +## Version compatibility + +Hosted `/v1` responses evolve additively. Existing fields keep their meaning, +new response data is optional or request-gated, and a breaking contract would +use `/v2` while `/v1` keeps serving. SDKs report their compile-time spec in +`X-OpenIAP-Spec` for rollout measurement only; the header never changes receipt +verification. Gate on `isValid`, require the store-verified `productId`, and +allow optional metadata to be absent or unknown. + ## Status codes - `200` — verification ran; require `isValid`, an operation-appropriate `state`, and an exact store-verified `productId` match @@ -2213,10 +2233,14 @@ assuming DAU implies safe request volume. - [/docs](https://kit.openiap.dev/docs) — full in-dashboard documentation - [/docs/quickstart](https://kit.openiap.dev/docs/quickstart) — signup → project → API key → first verify - [/docs/products](https://kit.openiap.dev/docs/products) — catalog sync, client payload editing, retrieval, caching, and limits +- [/docs/orders](https://kit.openiap.dev/docs/orders) — read-only Apple and Google order support tooling - [/docs/verification/apple](https://kit.openiap.dev/docs/verification/apple) — bundle ID, Issuer ID, Key ID, .p8 - [/docs/verification/google](https://kit.openiap.dev/docs/verification/google) — package name, service account JSON - [/docs/verification/horizon](https://kit.openiap.dev/docs/verification/horizon) — App ID + App Secret (write-only) +- [/docs/verification/amazon](https://kit.openiap.dev/docs/verification/amazon) — RVS shared secret, App Tester sandbox, and background rechecks - [/docs/api](https://kit.openiap.dev/docs/api) — request shapes, responses, errors, headers, and Amazon RVS payloads +- [/docs/compatibility](https://kit.openiap.dev/docs/compatibility) — additive `/v1` policy and older-SDK behavior +- [/docs/webhooks](https://kit.openiap.dev/docs/webhooks) — inbound Apple ASN v2 and Google RTDN setup - [/docs/operations](https://kit.openiap.dev/docs/operations) — fair use, capacity, rate and concurrency limits, logs, `/health`, graceful shutdown - [openiap.dev/docs/webhooks](https://openiap.dev/docs/webhooks) — operator setup steps for inbound Apple ASN v2 and Google RTDN lifecycle delivery - [/docs/ai-assistants](https://kit.openiap.dev/docs/ai-assistants) — how to point Codex / Claude / Cursor / etc. at this file diff --git a/packages/docs/public/llms.txt b/packages/docs/public/llms.txt index 5811b9328..d9fe35eaf 100644 --- a/packages/docs/public/llms.txt +++ b/packages/docs/public/llms.txt @@ -3,7 +3,7 @@ > OpenIAP: Unified in-app purchase specification for iOS & Android > Documentation: https://openiap.dev > Full Reference: https://openiap.dev/llms-full.txt -> Generated: 2026-08-17T10:30:38.945Z +> Generated: 2026-08-17T23:14:29.027Z ## Installation diff --git a/packages/docs/public/sponsors/amazon-dark.webp b/packages/docs/public/sponsors/amazon-dark.webp new file mode 100644 index 0000000000000000000000000000000000000000..e7337c894c1d1ce25cfeac39a55ae15dfe990576 GIT binary patch literal 15858 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 ICDOqG0NNFX#{d8T literal 0 HcmV?d00001 diff --git a/packages/docs/public/sponsors/amazon.webp b/packages/docs/public/sponsors/amazon.webp new file mode 100644 index 0000000000000000000000000000000000000000..5d4cf6342b261eedca252ac55c40b90dbda7f513 GIT binary patch literal 18206 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 literal 0 HcmV?d00001 diff --git a/packages/docs/src/components/AmazonWordmark.tsx b/packages/docs/src/components/AmazonWordmark.tsx new file mode 100644 index 000000000..8f4aebff6 --- /dev/null +++ b/packages/docs/src/components/AmazonWordmark.tsx @@ -0,0 +1,28 @@ +import type { ReactElement } from 'react'; + +interface AmazonWordmarkProps { + className?: string; +} + +function AmazonWordmark({ className }: AmazonWordmarkProps): ReactElement { + const classes = ['amazon-wordmark', className].filter(Boolean).join(' '); + + return ( + + + + + ); +} + +export default AmazonWordmark; diff --git a/packages/docs/src/components/EcosystemDiagram.tsx b/packages/docs/src/components/EcosystemDiagram.tsx index fb0870c39..f80efd36c 100644 --- a/packages/docs/src/components/EcosystemDiagram.tsx +++ b/packages/docs/src/components/EcosystemDiagram.tsx @@ -1,4 +1,5 @@ import { Link } from 'react-router-dom'; +import { IAPKIT_LOGO_PATH, IAPKIT_URL, trackIapKitClick } from '../lib/config'; import { LIBRARIES, type FrameworkLibraryName } from '../lib/images'; import { OPENIAP_VERSIONS } from '../lib/versioning'; import '../styles/ecosystem-diagram.css'; @@ -63,6 +64,7 @@ const SQUARE_ART = new Set([ '/logos/flutter_inapp_purchase.webp', '/logos/kmp-iap.webp', '/frameworks/maui.webp', + IAPKIT_LOGO_PATH, ]); interface DiagramMark { @@ -77,6 +79,7 @@ interface DiagramNode { icon: string; href: string; marks?: DiagramMark[]; + onClick?: () => void; } const SPEC_NODES: DiagramNode[] = [ @@ -118,6 +121,15 @@ const CORE_NODES: DiagramNode[] = [ }, ]; +const IAPKIT_NODE: DiagramNode = { + id: 'iapkit', + name: 'IAPKit', + note: 'Optional verification · entitlements · store sync · MCP', + icon: IAPKIT_LOGO_PATH, + href: IAPKIT_URL, + onClick: trackIapKitClick, +}; + function artClass(src: string, base: string): string { const classes = [base]; @@ -153,6 +165,7 @@ function NodeCard({ href={node.href} target="_blank" rel="noopener noreferrer" + onClick={node.onClick} > + + + +
+

+ Infrastructure +

+
+ +
+
openiap-gql generates the type system for the core native packages and for every framework library, and the core - packages are bundled into each library. Select any node to open its + packages are bundled into each library. IAPKit is the + optional hosted layer for purchase verification, entitlements, store + notifications, and product operations. Select any node to open its documentation or project.
diff --git a/packages/docs/src/components/MetaWordmark.tsx b/packages/docs/src/components/MetaWordmark.tsx index 812456a3f..ec30a6299 100644 --- a/packages/docs/src/components/MetaWordmark.tsx +++ b/packages/docs/src/components/MetaWordmark.tsx @@ -1,8 +1,14 @@ import type { ReactElement } from 'react'; -function MetaWordmark(): ReactElement { +interface MetaWordmarkProps { + className?: string; +} + +function MetaWordmark({ className }: MetaWordmarkProps): ReactElement { + const classes = ['meta-wordmark', className].filter(Boolean).join(' '); + return ( - + diff --git a/packages/docs/src/components/ShowcaseCards.tsx b/packages/docs/src/components/ShowcaseCards.tsx index a0cdac865..28cddcc8b 100644 --- a/packages/docs/src/components/ShowcaseCards.tsx +++ b/packages/docs/src/components/ShowcaseCards.tsx @@ -1,4 +1,4 @@ -import type { CSSProperties } from 'react'; +import type { CSSProperties, ReactElement } from 'react'; import { SiApple, SiGoogleplay } from 'react-icons/si'; import { Globe } from 'lucide-react'; import type { ShowcaseApp } from '../lib/showcase'; @@ -55,6 +55,70 @@ const badgeStyle: CSSProperties = { padding: '0.1rem 0.5rem', }; +export function ShowcaseAppMeta({ app }: { app: ShowcaseApp }): ReactElement { + return ( +
+ {app.ios ? ( + + + + ) : null} + {app.android ? ( + + + + ) : null} + {app.web ? ( + + + + ) : null} + + {app.library} + + {app.iapkit ? ( + + IAPKit + + ) : null} +
+ ); +} + export function ShowcaseAppCard({ app }: { app: ShowcaseApp }) { return (
@@ -80,65 +144,7 @@ export function ShowcaseAppCard({ app }: { app: ShowcaseApp }) { > {app.tagline}
-
- {app.ios ? ( - - - - ) : null} - {app.android ? ( - - - - ) : null} - {app.web ? ( - - - - ) : null} - - {app.library} - - {app.iapkit ? ( - - IAPKit - - ) : null} -
+ ); diff --git a/packages/docs/src/lib/showcase.ts b/packages/docs/src/lib/showcase.ts index a3ee6dbf6..399acb718 100644 --- a/packages/docs/src/lib/showcase.ts +++ b/packages/docs/src/lib/showcase.ts @@ -20,7 +20,7 @@ export type ShowcaseApp = { logo: string; /** Which OpenIAP library the app ships with. */ library: FrameworkLibraryName; - /** Whether the app uses IAPKit for receipt validation. */ + /** Whether the app uses IAPKit. */ iapkit?: boolean; ios?: string; android?: string; diff --git a/packages/docs/src/lib/sponsors.ts b/packages/docs/src/lib/sponsors.ts new file mode 100644 index 000000000..8447e64fc --- /dev/null +++ b/packages/docs/src/lib/sponsors.ts @@ -0,0 +1,35 @@ +import type { ComponentType } from 'react'; +import AmazonWordmark from '../components/AmazonWordmark'; +import MetaWordmark from '../components/MetaWordmark'; + +interface SponsorWordmarkProps { + className?: string; +} + +export interface Sponsor { + id: string; + name: string; + shortName: string; + tier: string; + url: string; + Wordmark: ComponentType; +} + +export const CURRENT_SPONSORS = [ + { + id: 'meta', + name: 'Meta', + shortName: 'Meta', + tier: 'Angel', + url: 'https://meta.com', + Wordmark: MetaWordmark, + }, + { + id: 'amazon', + name: 'Amazon Developer', + shortName: 'Amazon', + tier: 'Angel', + url: 'https://developer.amazon.com/', + Wordmark: AmazonWordmark, + }, +] as const satisfies readonly Sponsor[]; diff --git a/packages/docs/src/pages/community-resources.tsx b/packages/docs/src/pages/community-resources.tsx index b33532dc4..e1aabadcb 100644 --- a/packages/docs/src/pages/community-resources.tsx +++ b/packages/docs/src/pages/community-resources.tsx @@ -30,7 +30,10 @@ import { type FrameworkLibraryName, } from '../lib/images'; import { FEATURED_SHOWCASE_APPS, SHOWCASE_APPS } from '../lib/showcase'; -import { SHOWCASE_DISCUSSION_URL } from '../components/ShowcaseCards'; +import { + ShowcaseAppMeta, + SHOWCASE_DISCUSSION_URL, +} from '../components/ShowcaseCards'; import { useScrollToHash } from '../hooks/useScrollToHash'; interface EcosystemMeta { @@ -647,17 +650,17 @@ function CommunityResources() {

{app.tagline}

- {app.library} - - View showcase -
))} + + + See more apps built with OpenIAP +