feat(iapkit): route Horizon verification through IAPKit — no Horizon key on the device#103
feat(iapkit): route Horizon verification through IAPKit — no Horizon key on the device#103hyochan wants to merge 1 commit into
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 48 minutes and 14 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (5)
📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThis PR adds Meta Horizon entitlement verification support across multiple SDK platforms by introducing a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Code Review
This pull request implements Meta Horizon (Quest) verification support across the monorepo. It adds the RequestVerifyPurchaseWithIapkitHorizonProps input type to the GraphQL schema and updates the corresponding platform-specific types and Android verification logic. A review comment correctly suggests using idiomatic immutable maps in Kotlin. Crucially, most changes in this PR affect auto-generated files which, according to the style guide, should not be edited manually; instead, the GraphQL schema should be updated and the sync scripts executed to propagate changes.
| return mutableMapOf<String, Any?>( | ||
| "store" to IapStore.Horizon.rawValue, | ||
| "userId" to horizon.userId, | ||
| "sku" to horizon.sku | ||
| ) |
There was a problem hiding this comment.
Using mutableMapOf is unnecessary here as the map is not modified after creation. mapOf is more idiomatic for creating immutable maps in Kotlin. Additionally, the explicit type arguments <String, Any?> can be omitted as they are inferred from the context.
| return mutableMapOf<String, Any?>( | |
| "store" to IapStore.Horizon.rawValue, | |
| "userId" to horizon.userId, | |
| "sku" to horizon.sku | |
| ) | |
| return mapOf( | |
| "store" to IapStore.Horizon.rawValue, | |
| "userId" to horizon.userId, | |
| "sku" to horizon.sku | |
| ) |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
libraries/flutter_inapp_purchase/lib/types.dart (1)
4453-4479:⚠️ Potential issue | 🟠 MajorWire
horizoninto the verifyPurchaseWithProvider serializer.
RequestVerifyPurchaseWithIapkitPropsincludes an optionalhorizonfield, but the manual serializer inverifyPurchaseWithProvider()only forwardsapiKey,apple, andiapkit.horizonwill have that data silently dropped.Proposed fix
if (iapkit != null) { args['iapkit'] = { if (iapkit.apiKey != null) 'apiKey': iapkit.apiKey, if (iapkit.apple != null) 'apple': {'jws': iapkit.apple!.jws}, if (iapkit.google != null) 'google': {'purchaseToken': iapkit.google!.purchaseToken}, + if (iapkit.horizon != null) + 'horizon': { + 'userId': iapkit.horizon!.userId, + 'sku': iapkit.horizon!.sku, + }, }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libraries/flutter_inapp_purchase/lib/types.dart` around lines 4453 - 4479, The verifyPurchaseWithProvider serializer is dropping the RequestVerifyPurchaseWithIapkitProps.horizon field; update the payload construction inside verifyPurchaseWithProvider to include the horizon property alongside apiKey, apple, and google so Horizon (Quest) verification params are forwarded to the native layer, referencing RequestVerifyPurchaseWithIapkitProps.horizon and the verifyPurchaseWithProvider function when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@libraries/flutter_inapp_purchase/lib/types.dart`:
- Around line 4453-4479: The verifyPurchaseWithProvider serializer is dropping
the RequestVerifyPurchaseWithIapkitProps.horizon field; update the payload
construction inside verifyPurchaseWithProvider to include the horizon property
alongside apiKey, apple, and google so Horizon (Quest) verification params are
forwarded to the native layer, referencing
RequestVerifyPurchaseWithIapkitProps.horizon and the verifyPurchaseWithProvider
function when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 87eb7c27-d9f8-4eb5-82da-e8f5cc195e1a
⛔ Files ignored due to path filters (5)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/Types.swiftis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**packages/gql/src/generated/types.gdis excluded by!**/generated/**packages/gql/src/generated/types.tsis excluded by!**/generated/**
📒 Files selected for processing (9)
libraries/expo-iap/src/types.tslibraries/flutter_inapp_purchase/lib/types.dartlibraries/godot-iap/addons/godot-iap/types.gdlibraries/kmp-iap/library/src/commonMain/kotlin/io/github/hyochan/kmpiap/openiap/Types.ktlibraries/react-native-iap/src/types.tspackages/apple/Sources/Models/Types.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.ktpackages/gql/src/type.graphql
f0de553 to
76c6171
Compare
…or only)
Lets Quest apps use `verifyPurchaseWithIapkit` with just the IAPKit
Bearer API key. Meta App Secret stays on the IAPKit server (per
project), so the client never carries an Oculus access token.
Isolation (matches the flavor split the rest of this SDK uses):
- src/play/ — untouched (0 lines diff).
- src/main/ — zero runtime changes. `verifyPurchaseWithIapkit`
keeps its historical Google-only contract; only
its stale "not yet supported" comment is updated
to point at the new horizon-flavor file.
- src/horizon/utils/ — NEW `PurchaseVerificationValidatorHorizon.kt`
with `verifyPurchaseWithIapkitHorizon`: builds
the {store, userId, sku} payload and posts it to
IAPKit with the same Bearer-key auth the
Google helper uses. HTTP plumbing is
intentionally duplicated so the Play-flavor
artifact never carries Meta/Quest code.
- src/horizon/OpenIapModule.kt — `verifyPurchaseWithProvider` branches
on `options.horizon`: horizon helper when the
caller supplied a horizon payload, shared
Google helper otherwise. A Quest app holding
a Play purchase token still verifies via main.
Schema (platform-agnostic, required everywhere):
- New input `RequestVerifyPurchaseWithIapkitHorizonProps { userId, sku }`.
- Extend `RequestVerifyPurchaseWithIapkitProps` with an optional
`horizon` field so apple / google / horizon sit alongside each other.
Regenerated types (unedited by hand):
- TypeScript (react-native-iap, expo-iap, gql), Kotlin (google Types.kt,
kmp-iap, gql), Swift (apple, gql), Dart (flutter, gql), Godot
(godot-iap, gql) — all pick up the new input type automatically.
Build:
- `./gradlew :openiap:compileHorizonDebugKotlin` — success
- `./gradlew :openiap:compilePlayDebugKotlin` — success (no regression;
Play flavor sees zero Horizon code at compile time)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
76c6171 to
4616640
Compare
|
Closing — the IAPKit server (https://github.com/hyodotdev/openiap-kit/pull/3) already accepts |
Summary
Lets clients use `verifyPurchaseWithIapkit` for Meta Horizon the same way they already use it for Google — with just the IAPKit Bearer API key. The Meta App Secret stays on the IAPKit server (stored per project) and the server composes `OC|APP_ID|APP_SECRET` per request, so no Horizon credential ever lives on the device.
Pairs with hyodotdev/openiap-kit#3 which adds the matching server-side `verify_entitlement` path on the IAPKit backend.
Changes
GraphQL schema
`packages/gql/src/type.graphql`:
Regenerated types (unedited)
`bun run generate` regenerated target types so every wrapper gets the new shape automatically:
Android validator
`packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt`:
Call site (consumer view)
```kotlin
val result = verifyPurchaseWithIapkit(
RequestVerifyPurchaseWithIapkitProps(
apiKey = "openiap-kit_…", // only credential needed on the device
horizon = RequestVerifyPurchaseWithIapkitHorizonProps(
userId = currentOculusUser.id,
sku = "coin_pack_100"
),
),
tag = TAG,
)
```
Behind the scenes IAPKit reads the project's stored App ID + App Secret, builds the Meta access token, calls `graph.oculus.com/{APP_ID}/verify_entitlement`, and returns a harmonized `{ isValid, state }`.
Test plan
Related
🤖 Generated with Claude Code
Summary by CodeRabbit