Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d9fd7ea
refactor(spec)!: remove deprecated APIs
hyochan Jul 27, 2026
d1b490f
refactor(native)!: remove legacy APIs
hyochan Jul 27, 2026
3772a7a
refactor(sdk)!: remove legacy compatibility
hyochan Jul 27, 2026
911f644
docs!: document major API removals
hyochan Jul 27, 2026
006de2a
chore(api): enforce canonical major surface
hyochan Jul 27, 2026
785b5d4
fix(sdk): migrate remaining canonical API call sites
hyochan Jul 27, 2026
2a94cba
test(api): guard canonical major call sites
hyochan Jul 27, 2026
e1d2d93
docs: add major migration preview
hyochan Jul 27, 2026
69d55a7
fix(expo-iap): use canonical native event type
hyochan Jul 27, 2026
dbbc621
docs: sync generated context after rebase
hyochan Jul 27, 2026
2212113
fix(docs): preserve major release links after rebase
hyochan Jul 28, 2026
88fda70
fix(api): preserve canonical major migration behavior
hyochan Jul 28, 2026
38709d3
fix(expo): remove unused reanimated dependency
hyochan Jul 28, 2026
9575918
fix(flutter): restore ios example launch
hyochan Jul 28, 2026
dcd016d
docs: align major migration guidance
hyochan Jul 28, 2026
df801ad
feat: finalize major API migration
hyochan Jul 28, 2026
8cd893d
fix(apple): align Xcode 27 build paths
hyochan Jul 28, 2026
a99b3ab
fix(audit): skip generated symbolic links
hyochan Jul 28, 2026
c934493
fix(maui): isolate shared contract restore
hyochan Jul 28, 2026
1ceb10f
fix(apple): complete Xcode 27 distribution paths
hyochan Jul 29, 2026
8d8cbd3
docs: clarify Xcode 27 release artifacts
hyochan Jul 29, 2026
e7cffc4
fix: harden major release validation
hyochan Jul 29, 2026
a67de39
docs: announce OpenIAP 3
hyochan Jul 29, 2026
c4c1af4
docs: sharpen OpenIAP 3 announcement
hyochan Jul 29, 2026
96956c5
docs: align Flutter SwiftPM release notes
hyochan Jul 29, 2026
5ade570
fix(google): deduplicate amazon purchase pages
hyochan Jul 29, 2026
90d3cdb
fix(release): harden OpenIAP 3 migration
hyochan Jul 30, 2026
7e4cbae
fix(flutter): avoid Xcode version pipe crash
hyochan Jul 30, 2026
2a0802f
fix(react-native): restore Ruby 3.4 CocoaPods support
hyochan Jul 30, 2026
31846fc
ci(flutter): allow consumer build to finish
hyochan Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 1 addition & 2 deletions .claude/commands/audit-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,7 @@ consumer builds. That matrix is the verification SSOT and includes:
- Godot Android and iOS bridge builds
- KMP tests and all three Android example flavors
- MAUI Android bindings for Play, Amazon, and Horizon, plus iOS/macCatalyst
bindings and platform TFMs (the shared `net9.0`/`net10.0` TFMs are not a
substitute)
bindings and platform TFMs (the shared `net10.0` TFM is not a substitute)

After that matrix passes, rerun the audit-specific consistency gates:

Expand Down
30 changes: 15 additions & 15 deletions .claude/commands/e2e-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,8 @@ cd packages/google
cd ../..
bash packages/apple/scripts/build-xcframework.sh
cd libraries/maui-iap
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj -p:TargetFrameworks=net9.0 --nologo
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj -p:TargetFrameworks=net9.0-ios --nologo
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj -p:TargetFrameworks=net10.0 --nologo
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj -p:TargetFrameworks=net10.0-ios --nologo
```

Normal Android / Play build and launch smoke:
Expand All @@ -723,24 +723,24 @@ rm -rf \
src/OpenIap.Maui/bin src/OpenIap.Maui/obj \
example/OpenIap.Maui.Example/bin example/OpenIap.Maui.Example/obj
dotnet build src/OpenIap.Maui.Bindings.Android/OpenIap.Maui.Bindings.Android.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=play \
--nologo
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=play \
-p:BuildProjectReferences=false \
--nologo
dotnet build example/OpenIap.Maui.Example/OpenIap.Maui.Example.csproj \
-f net9.0-android \
-f net10.0-android \
-p:OpenIapAndroidStore=play \
-p:EmbedAssembliesIntoApk=true \
--nologo
# Build-only regression can stop here.
: "${ANDROID_SERIAL:?Set ANDROID_SERIAL to the target Android device serial}"
adb -s "$ANDROID_SERIAL" uninstall dev.hyo.martie || true
adb -s "$ANDROID_SERIAL" install --no-incremental -r \
example/OpenIap.Maui.Example/bin/Debug/net9.0-android/dev.hyo.martie-Signed.apk
example/OpenIap.Maui.Example/bin/Debug/net10.0-android/dev.hyo.martie-Signed.apk
adb -s "$ANDROID_SERIAL" shell monkey -p dev.hyo.martie 1
```

Expand All @@ -756,24 +756,24 @@ rm -rf \
src/OpenIap.Maui/bin src/OpenIap.Maui/obj \
example/OpenIap.Maui.Example/bin example/OpenIap.Maui.Example/obj
dotnet build src/OpenIap.Maui.Bindings.Android/OpenIap.Maui.Bindings.Android.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=amazon \
--nologo
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=amazon \
-p:BuildProjectReferences=false \
--nologo
dotnet build example/OpenIap.Maui.Example/OpenIap.Maui.Example.csproj \
-f net9.0-android \
-f net10.0-android \
-p:OpenIapAndroidStore=amazon \
-p:EmbedAssembliesIntoApk=true \
--nologo
# Build-only regression can stop here.
: "${FIREOS_SERIAL:?Set FIREOS_SERIAL to the target FireOS device serial}"
adb -s "$FIREOS_SERIAL" uninstall dev.hyo.martie || true
adb -s "$FIREOS_SERIAL" install --no-incremental -r \
example/OpenIap.Maui.Example/bin/stores/amazon/Debug/net9.0-android/dev.hyo.martie-Signed.apk
example/OpenIap.Maui.Example/bin/stores/amazon/Debug/net10.0-android/dev.hyo.martie-Signed.apk
adb -s "$FIREOS_SERIAL" shell monkey -p dev.hyo.martie 1
```

Expand All @@ -789,16 +789,16 @@ rm -rf \
src/OpenIap.Maui/bin src/OpenIap.Maui/obj \
example/OpenIap.Maui.Example/bin example/OpenIap.Maui.Example/obj
dotnet build src/OpenIap.Maui.Bindings.Android/OpenIap.Maui.Bindings.Android.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=horizon \
--nologo
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=horizon \
-p:BuildProjectReferences=false \
--nologo
dotnet build example/OpenIap.Maui.Example/OpenIap.Maui.Example.csproj \
-f net9.0-android \
-f net10.0-android \
-p:OpenIapAndroidStore=horizon \
--nologo
```
Expand All @@ -809,13 +809,13 @@ iOS physical-device build and launch smoke:
cd libraries/maui-iap/example/OpenIap.Maui.Example
: "${IOS_UDID:?Set IOS_UDID to the target iOS device UDID}"
dotnet build \
-f net9.0-ios \
-f net10.0-ios \
-p:RuntimeIdentifier=ios-arm64 \
-p:ValidateXcodeVersion=false \
--nologo
xcrun devicectl device install app \
--device "$IOS_UDID" \
bin/Debug/net9.0-ios/ios-arm64/OpenIap.Maui.Example.app
bin/Debug/net10.0-ios/ios-arm64/OpenIap.Maui.Example.app
xcrun devicectl device process launch \
--device "$IOS_UDID" \
dev.hyo.martie
Expand Down
28 changes: 3 additions & 25 deletions .claude/commands/verify-all.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,11 @@ set -euo pipefail

# MAUI shared contracts, Android bindings, and platform library
(cd libraries/maui-iap && \
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net9.0 --nologo && \
dotnet build src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net10.0 --nologo && \
dotnet run \
--project tests/OpenIap.Maui.ContractTests/OpenIap.Maui.ContractTests.csproj \
--framework net9.0 -p:TargetFrameworks=net9.0 --no-launch-profile && \
dotnet run \
--project tests/OpenIap.Maui.ContractTests/OpenIap.Maui.ContractTests.csproj \
--framework net10.0 -p:TargetFrameworks=net10.0 --no-launch-profile)
--framework net10.0 --no-launch-profile)
(cd packages/google && ./gradlew \
:openiap:assemblePlayRelease \
:openiap:assembleAmazonRelease \
Expand All @@ -146,42 +141,25 @@ set -euo pipefail
:openiap:assembleRelease -PopenIapAndroidStore="$store")
dotnet build \
src/OpenIap.Maui.Bindings.Android/OpenIap.Maui.Bindings.Android.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore="$store" \
"${DOTNET_BUILD_ARGS[@]}"
dotnet build \
src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net9.0-android \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore="$store" \
-p:BuildProjectReferences=false \
"${DOTNET_BUILD_ARGS[@]}"
if [ "$store" = play ]; then
dotnet build \
src/OpenIap.Maui.Bindings.Android/OpenIap.Maui.Bindings.Android.csproj \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=play \
"${DOTNET_BUILD_ARGS[@]}"
dotnet build \
src/OpenIap.Maui/OpenIap.Maui.csproj \
-p:TargetFrameworks=net10.0-android \
-p:OpenIapAndroidStore=play \
-p:BuildProjectReferences=false \
"${DOTNET_BUILD_ARGS[@]}"
fi
done
)

# MAUI iOS/macCatalyst binding and platform library (requires xcodegen + MAUI workload)
bash packages/apple/scripts/build-xcframework.sh
(cd libraries/maui-iap/src/OpenIap.Maui.Bindings.iOS && \
dotnet build -p:TargetFrameworks=net9.0-ios --nologo && \
dotnet build -p:TargetFrameworks=net10.0-ios --nologo && \
dotnet build -p:TargetFrameworks=net9.0-maccatalyst --nologo && \
dotnet build -p:TargetFrameworks=net10.0-maccatalyst --nologo)
(cd libraries/maui-iap/src/OpenIap.Maui && \
dotnet build -p:TargetFrameworks=net9.0-ios --nologo && \
dotnet build -p:TargetFrameworks=net10.0-ios --nologo && \
dotnet build -p:TargetFrameworks=net9.0-maccatalyst --nologo && \
dotnet build -p:TargetFrameworks=net10.0-maccatalyst --nologo)

# Documentation/context consistency and patch hygiene
Expand Down
10 changes: 10 additions & 0 deletions .claude/skills/rebase-main/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: rebase-main
description: Safely update the local main branch with a fast-forward-only pull, rebase the current work branch onto it, resolve conflicts without losing local work, and restore staged, unstaged, and untracked changes. Use when the user asks to pull main and rebase the current branch, update a branch from main, remove rebase conflicts, or says "rebase-main".
---

# Rebase Main (Claude Code)

The canonical workflow lives in `.codex/skills/rebase-main/SKILL.md`. Read and
follow it completely, including its working-tree safeguard, conflict-resolution,
restoration, verification, and no-implicit-push rules.
96 changes: 96 additions & 0 deletions .codex/skills/rebase-main/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
name: rebase-main
description: Safely update the local main branch with a fast-forward-only pull, rebase the current work branch onto it, resolve conflicts without losing local work, and restore staged, unstaged, and untracked changes. Use when the user asks to pull main and rebase the current branch, update a branch from main, remove rebase conflicts, or says "rebase-main".
---

# Rebase Main

Update `main`, rebase the current work branch, and preserve every pre-existing
working-tree change. Do not commit or push unless the user separately authorizes
it.

## Establish The Target

1. Read the repository Git instructions.
2. Record the current branch, `HEAD`, upstream, worktrees, staged changes,
unstaged changes, untracked files, and relevant ignored environment files
with content fingerprints.
3. Stop if the checkout is detached, the current branch is `main`, another
merge/rebase/cherry-pick is active, or `main` is checked out in another
worktree that cannot safely be updated.
4. Use `origin` and `main` by default. If either is absent or the repository uses
another base, derive the target from repository configuration or ask.

## Safeguard Local Work

If the worktree is dirty:

1. Capture status and content fingerprints so restoration can be checked.
2. Create one clearly named stash with `--include-untracked`. Never use
`--all`: ignored `.env`, credentials, build caches, and other ignored local
state must remain in place.
3. Record the stash object and confirm the tracked/untracked worktree is clean.
4. If stashing a modified ignore rule makes a previously ignored local file
appear untracked, do not stash, move, or delete that file. Add only its exact
path to `.git/info/exclude` as a recorded temporary safeguard, but only after
confirming that neither `main` nor the work branch tracks that exact path.
If either target tree contains it, stop without switching branches.
5. If stashing fails or other changes remain unexplained, stop before switching
branches.

Treat the stash as a recovery point. Do not drop it until restoration is
complete and verified.

## Update Main And Rebase

Before every branch switch, compare each recorded ignored/local path with the
destination tree and stop on any tracked-path collision. Git normally permits a
checkout to overwrite ignored files, so use `git checkout
--no-overwrite-ignore <branch>` for these guarded transitions rather than a
plain switch.

1. Run `git fetch origin main`, then check every recorded ignored/local path
against both the current `main` tree and the freshly fetched `origin/main`.
Stop on any collision.
2. Check out `main` with `--no-overwrite-ignore`.
3. Run `git merge --ff-only origin/main`. This explicit fetch-plus-fast-forward
is the guarded equivalent of `git pull --ff-only origin main`: it updates
only to the exact remote commit whose paths were checked. Never reset,
force-update, or create a merge commit for a divergent local `main`.
4. Confirm that `main` and `origin/main` resolve to the exact same commit after
the fast-forward. A local `main` that is ahead-only makes a fast-forward
update report no incoming work without publishing or removing its unique
commits; stop and report that divergence instead of rebasing the work branch
onto unpublished work.
5. Check out the recorded work branch with `--no-overwrite-ignore` after
repeating the ignored-path collision check against that branch.
6. Run `git rebase origin/main`, using the exact remote-tracking commit just
verified against local `main`.
7. For each conflict, inspect the base, new-main side, and work-branch side.
Preserve both compatible intents; do not apply blanket `ours`/`theirs`
resolution. Regenerate generated files through their documented generator.
8. Run `git rebase --continue` only after the resolved files and staged diff
have been reviewed. If intent is ambiguous, stop with the exact conflict
list and keep the rebase recoverable.

Never use `git reset --hard`, `git checkout --`, `git clean`, or destructive
recovery shortcuts.

## Restore And Verify

1. Apply the recorded stash with its index so staged state is restored.
2. Resolve restoration conflicts with the same three-way care. Do not drop the
stash while any restoration conflict or mismatch remains.
3. Remove any temporary `.git/info/exclude` entries only after the restored
worktree ignore rules cover those paths again.
4. Compare the restored staged, unstaged, and untracked state with the initial
snapshot. Confirm ignored environment files still exist and were not added to
Git.
5. Once restoration is exact, drop only the named safeguard stash.
6. Run `git status`, `git diff --check`, and the repository's affected
lightweight audits or tests. Confirm the branch is based on the updated
`main` and report the old/new main and branch heads.

If the work branch was already published, explain that the rewritten history
will require a later `git push --force-with-lease`. Never perform that push
unless the user authorized it.
4 changes: 4 additions & 0 deletions .codex/skills/rebase-main/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Rebase Main"
short_description: "Safely rebase a work branch onto main"
default_prompt: "Use $rebase-main to update local main and safely rebase the current branch onto it."
Binary file added .github/pr-previews/pr-256-major-api-removal.mp4
Binary file not shown.
62 changes: 58 additions & 4 deletions .github/workflows/ci-expo-iap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ on:
pull_request:
branches: [main, next]
paths:
- 'libraries/expo-iap/**'
- 'packages/gql/src/generated/types.ts'
- "libraries/expo-iap/**"
- "packages/gql/src/generated/types.ts"
- "packages/apple/Sources/**"
- "packages/apple/Package.swift"
- "openiap-versions.json"
- ".github/workflows/ci-expo-iap.yml"
push:
branches: [main, next]
paths:
- 'libraries/expo-iap/**'
- 'packages/gql/src/generated/types.ts'
- "libraries/expo-iap/**"
- "packages/gql/src/generated/types.ts"
- "packages/apple/Sources/**"
- "packages/apple/Package.swift"
- "openiap-versions.json"
- ".github/workflows/ci-expo-iap.yml"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -79,3 +87,49 @@ jobs:

- name: Consumer install smoke test
run: bun run verify:consumer-install

ios-xcode-27:
name: iOS example (Xcode 27)
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: xcode-27
timeout-minutes: 60
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 1
persist-credentials: false

- uses: actions/setup-node@v7
with:
node-version: 20

- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.13

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Install example dependencies
working-directory: libraries/expo-iap/example
run: bun install --frozen-lockfile

- name: Generate scene-based iOS project
working-directory: libraries/expo-iap/example
run: npx expo prebuild --platform ios --clean

- name: Install CocoaPods dependencies
working-directory: libraries/expo-iap/example/ios
run: pod install --repo-update

- name: Build scene-based iOS example
working-directory: libraries/expo-iap/example/ios
run: |
set -o pipefail
xcodebuild build \
-workspace expoiapexample.xcworkspace \
-scheme ExpoIAPExample \
-destination 'generic/platform=iOS Simulator' \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
COMPILER_INDEX_STORE_ENABLE=NO
Loading