Skip to content

refactor: update verifyPurchase to support platform-specific options#53

Merged
hyochan merged 9 commits into
mainfrom
refactor/verifyPurchase
Dec 12, 2025
Merged

refactor: update verifyPurchase to support platform-specific options#53
hyochan merged 9 commits into
mainfrom
refactor/verifyPurchase

Conversation

@hyochan
Copy link
Copy Markdown
Member

@hyochan hyochan commented Dec 12, 2025

  • Add VerifyPurchaseAppleOptions (jws) for iOS App Store verification
  • Add VerifyPurchaseGoogleOptions (packageName, purchaseToken, accessToken, isSub) for Play Store
  • Add VerifyPurchaseHorizonOptions (sku, userId, accessToken) for Meta Quest S2S API
  • Update VerifyPurchaseProps to accept apple, google, horizon fields
  • Deprecate androidOptions in favor of google field
  • Implement verifyPurchaseWithHorizon() for Meta S2S verification
  • Add platform-specific documentation comments

Note: Pending version updates for release:

  • openiap-gql: 1.3.3
  • openiap-google: 1.3.13
  • openiap-apple: 1.3.1

Summary by CodeRabbit

  • New Features

    • Multi-platform purchase/subscription verification: Apple App Store, Google Play, and Meta Horizon support.
    • Platform-specific verification inputs and IAPKit verification flows for Apple, Google, and Horizon.
    • Option to supply an external Apple JWS to short‑circuit local receipt fetch/validation.
  • Documentation

    • Updated API docs with platform-specific verification examples, schema changes, and deprecation notes for legacy Android options.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add VerifyPurchaseAppleOptions (jws) for iOS App Store verification
- Add VerifyPurchaseGoogleOptions (packageName, purchaseToken, accessToken, isSub) for Play Store
- Add VerifyPurchaseHorizonOptions (sku, userId, accessToken) for Meta Quest S2S API
- Update VerifyPurchaseProps to accept apple, google, horizon fields
- Deprecate androidOptions in favor of google field
- Implement verifyPurchaseWithHorizon() for Meta S2S verification
- Add platform-specific documentation comments

Note: Pending version updates for release:
- openiap-gql: 1.3.3
- openiap-google: 1.3.13
- openiap-apple: 1.3.1
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 12, 2025

Warning

Rate limit exceeded

@hyochan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 22 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 0b800ce and aeb2c38.

📒 Files selected for processing (1)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (6 hunks)

Walkthrough

Adds platform-specific purchase and verification types (Apple, Google, Horizon, IAPKit), updates GraphQL inputs, wires Horizon and provider-specific verification into native flows (including iOS JWS short‑circuit), and extends verification implementations and tests accordingly.

Changes

Cohort / File(s) Changes
Types - Apple
packages/apple/Sources/Models/Types.swift
Added platform-specific types: RequestPurchasePropsByPlatforms, RequestSubscriptionPropsByPlatforms, RequestVerifyPurchaseWithIapkitProps, VerifyPurchaseAppleOptions, VerifyPurchaseGoogleOptions, VerifyPurchaseHorizonOptions; expanded VerifyPurchaseProps and added VerifyPurchaseResultHorizon.
Native iOS module
packages/apple/Sources/OpenIapModule.swift
performVerifyPurchaseIOS short‑circuits when props.apple?.jws is provided (use external JWS → mark valid); otherwise retains StoreKit fetch/verify path and existing error handling.
Types - Android / JVM
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
Added platform-specific classes and (de)serialization: RequestPurchasePropsByPlatforms, RequestSubscriptionPropsByPlatforms, RequestVerifyPurchaseWithIapkitProps, VerifyPurchaseAppleOptions, VerifyPurchaseGoogleOptions, VerifyPurchaseHorizonOptions; updated VerifyPurchaseProps and VerifyPurchaseResult to include horizon.
Verification implementation - Android
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
Added verifyPurchaseWithHorizon and VerifyPurchaseResultHorizon; refactored IAPKit payload handling with buildGooglePayload; updated Google Play verification to use new google options; removed old buildPayload.
Native Android module
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
Extended verifyPurchase to call Horizon verification when props.horizon present (requires appId); otherwise falls back to Google Play verification.
Tests
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
Updated tests for new google options flow; added Horizon tests (missing options, success, non‑2xx error); adjusted assertions and imports.
GraphQL schemas
packages/gql/src/type.graphql, packages/gql/src/type-android.graphql, packages/gql/src/type-ios.graphql
Replaced VerifyPurchaseAndroidOptions with VerifyPurchaseGoogleOptions; added VerifyPurchaseAppleOptions, VerifyPurchaseHorizonOptions, VerifyPurchaseResultHorizon, RequestVerifyPurchaseWithIapkitProps; introduced per-platform RequestPurchasePropsByPlatforms / RequestSubscriptionPropsByPlatforms; deprecated legacy androidOptions.
Docs
packages/docs/src/pages/docs/updates/notes.tsx
Added documentation and examples describing platform-specific verification options (Apple JWS, Google options, Horizon) and deprecation note for legacy androidOptions.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant NativeModule as Native Module
    participant Verifier as Verification Service
    participant Result as VerifyResult

    Note over Client,Result: Platform-aware verifyPurchase (Apple/Google/Horizon/IAPKit)

    Client->>NativeModule: verifyPurchase(VerifyPurchaseProps)
    alt props.apple.jws present
        NativeModule->>NativeModule: accept external JWS (short‑circuit)
        NativeModule->>Result: return VerifyPurchaseResult (iOS JWS valid)
    else props.apple present without jws
        NativeModule->>Verifier: fetch StoreKit tx & validate
        Verifier-->>NativeModule: verification outcome
        NativeModule->>Result: return VerifyPurchaseResult
    end
    alt props.horizon present
        NativeModule->>Verifier: verifyPurchaseWithHorizon(props.horizon, appId)
        Verifier-->>Result: VerifyPurchaseResultHorizon
    else props.google present
        NativeModule->>Verifier: verifyPurchaseWithGooglePlay(props.google)
        Verifier-->>Result: VerifyPurchaseResultAndroid/IOS
    end
    Result-->>Client: VerifyPurchaseResult (platform-specific union)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to:
    • verifyPurchaseWithHorizon HTTP payload, response parsing, and error handling
    • Cross-language type consistency (Swift/Kotlin/GraphQL) for apple, google, horizon
    • iOS short‑circuit path correctness in performVerifyPurchaseIOS (JWS handling vs StoreKit)
    • Updated and new tests covering Google and Horizon flows

Possibly related PRs

Suggested labels

📱 iOS, 🤖 android, ⬡ gql, ❄️ types

Poem

🐰
I hopped through types and took a leap,
JWS, Play, Horizon — tidy and neat,
I nudged each flow to find its lane,
Verified carrots in every chain,
Hop, build, release — a rabbit's treat!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: updating verifyPurchase to support platform-specific options across Apple, Google, and Horizon.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- openiap-gql v1.3.3
- openiap-google v1.3.13
- openiap-apple v1.3.1

New VerifyPurchaseProps structure with apple, google, horizon fields
@hyochan hyochan marked this pull request as ready for review December 12, 2025 09:44
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/apple/Sources/OpenIapModule.swift (1)

595-628: Don't set isValid = true just because external JWS was provided — it contradicts the field's documented meaning.

The VerifyPurchaseResultIOS.isValid field is documented as "Whether the receipt is valid". Accepting an external JWS string and immediately marking it valid (lines 607–609) is incorrect: JWS presence ≠ verification. Your own comment says "The caller should verify the JWS on their server", confirming it's unverified at this point. This is a security issue: example code (PurchaseFlowScreen, SubscriptionFlowScreen) finishes purchases based on isValid, so unverified JWS would incorrectly trigger fulfillment.

Set isValid = false for external JWS, matching the StoreKit path's semantics (only true after checkVerified succeeds).

packages/apple/Sources/Models/Types.swift (1)

1145-1172: Stop editing packages/apple/Sources/Models/Types.swift directly; regenerate from schema instead.
This file is explicitly auto-generated, and direct edits will be overwritten and/or drift from the GraphQL schema. Per repo guidance: update the iOS/macOS gql version in packages/apple/openiap-versions.json, run ./scripts/generate-types.sh, then run swift test. Based on learnings / coding guidelines.

#!/bin/bash
set -euo pipefail

# Verify what the repo considers the source of truth for generating Types.swift
rg -n --hidden -S "AUTO-GENERATED TYPES|DO NOT EDIT DIRECTLY|npm run generate|generate-types\\.sh|openiap-versions\\.json|gql\"\\s*:" -C2 .

# Confirm this file is produced by generation and see if there's a generator header/footer marker
rg -n --hidden -S "Generated|AUTO-GENERATED|DO NOT EDIT" -n packages/apple/Sources/Models/Types.swift || true

Also applies to: 1237-1264, 1288-1309, 1329-1439

🧹 Nitpick comments (5)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)

2770-2813: Good direction, but ensure deprecated fields still work end-to-end (mapping/precedence).
Types look consistent, but VerifyPurchaseProps.fromJson() currently keeps both androidOptions (deprecated) and google without mapping. If verifyPurchaseWithGooglePlay(...) only reads props.google, older callers using androidOptions will regress.

Suggested approach (without editing this generated file): centralize compatibility in the verifier, e.g. treat androidOptions as a fallback when google == null, and clearly define precedence when both are present. Based on learnings, keep packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt generated via ./scripts/generate-types.sh, not hand-edited.

Also applies to: 2906-2949, 2989-3024, 3056-3082, 3084-3182, 3184-3232

packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (1)

303-322: Consider improving exception handling in test assertions.

The static analysis tool flagged that the caught exception is swallowed at line 319. While the test intent is clear, you could make the assertion more explicit:

         } catch (error: OpenIapError.InvalidPurchaseVerification) {
-            assertTrue(true)
+            // Expected exception type - test passes
         }

Alternatively, use JUnit's assertThrows for cleaner exception testing:

@Test
fun `verifyPurchaseWithHorizon throws on failure response`() = runTest {
    val horizonOptions = VerifyPurchaseHorizonOptions(
        sku = "50_gems",
        userId = "123456789",
        accessToken = "OC|app_id|app_secret"
    )
    val props = VerifyPurchaseProps(horizon = horizonOptions, sku = "50_gems")

    assertThrows<OpenIapError.InvalidPurchaseVerification> {
        verifyPurchaseWithHorizon(
            props,
            "test-app-id",
            "TEST"
        ) { _ -> FakeHttpURLConnection(400, """{"error":"invalid_user"}""") }
    }
}
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)

288-288: Remove empty line.

There's a trailing empty line that appears to be a remnant from refactoring.

 }
-
 private fun String?.orElse(fallback: String): String = this ?: fallback
packages/apple/Sources/Models/Types.swift (2)

1151-1172: Clarify/validate “only one platform field should be set” (avoid ambiguous payloads).
RequestPurchasePropsByPlatforms / RequestSubscriptionPropsByPlatforms / VerifyPurchaseProps allow multiple optional platform fields to be set simultaneously (e.g., android + google, ios + apple). If the backend expects exactly one, consider enforcing it at the schema level (preferred) or at least documenting precedence rules.

Also applies to: 1243-1264, 1414-1439


1152-1160: Use real Swift deprecation annotations (not only doc comments).
If the generator supports it, prefer emitting @available(*, deprecated, message: "...") for deprecated fields/types (e.g., androidOptions, android, ios, VerifyPurchaseAndroidOptions) so downstream users get compiler warnings. If not supported today, this is a generator/schema enhancement request.

Also applies to: 1244-1252, 1329-1347, 1415-1416

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3049ca7 and 0959e8f.

⛔ Files ignored due to path filters (4)
  • packages/gql/src/generated/Types.kt is excluded by !**/generated/**
  • packages/gql/src/generated/Types.swift is excluded by !**/generated/**
  • packages/gql/src/generated/types.dart is excluded by !**/generated/**
  • packages/gql/src/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (10)
  • packages/apple/Sources/Models/Types.swift (5 hunks)
  • packages/apple/Sources/OpenIapModule.swift (1 hunks)
  • packages/docs/src/pages/docs/updates/notes.tsx (1 hunks)
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (2 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (6 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (3 hunks)
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (3 hunks)
  • packages/gql/src/type-android.graphql (1 hunks)
  • packages/gql/src/type-ios.graphql (1 hunks)
  • packages/gql/src/type.graphql (4 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
packages/apple/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

packages/apple/**/*.swift: iOS-specific functions MUST have IOS suffix (e.g., presentCodeRedemptionSheetIOS(), showManageSubscriptionsIOS())
Use Pascal case for acronyms at the beginning or middle of names (e.g., IapManager, IapPurchase), but ALL CAPS only when appearing as a suffix (e.g., ProductIAP, OpenIAP)

Files:

  • packages/apple/Sources/OpenIapModule.swift
  • packages/apple/Sources/Models/Types.swift
packages/gql/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run generate to regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) from the GraphQL schema

Files:

  • packages/gql/src/type-ios.graphql
  • packages/gql/src/type-android.graphql
  • packages/gql/src/type.graphql
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable Kotlin helper functions in openiap/src/main/java/dev/hyo/openiap/utils/

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: iOS-specific functions must end with IOS suffix (e.g., clearTransactionIOS, getAppTransactionIOS)
Android-specific functions must end with Android suffix (e.g., acknowledgePurchaseAndroid, consumePurchaseAndroid)
Use action prefixes for function naming: get for retrieval, request for async operations, clear for removal, is/has for boolean checks, show/present for UI display, begin/finish/end for process control

Prefer interface for defining object shapes in TypeScript

Files:

  • packages/docs/src/pages/docs/updates/notes.tsx
packages/docs/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/docs/**/*.{ts,tsx}: Use kebab-case for search modal IDs (e.g., id: 'request-products')
Modal state should be defined once at the app root level using Preact Signals (signal from '@preact/signals-react'), not instantiated multiple times
ALL components must fit within parent boundaries and never overflow outside parent containers; use overflow-hidden, break-words, and whitespace-nowrap as needed
Delete unused components, functions, and imports immediately; do not keep commented-out code or unused variables
ANY function that returns a Promise must be wrapped with void operator when used where a void return is expected (e.g., event handlers)

Files:

  • packages/docs/src/pages/docs/updates/notes.tsx
packages/docs/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

packages/docs/**/*: Before committing, run npx prettier --write, npm run lint, bun run tsc or npm run typecheck, and npm run build to verify formatting, linting, types, and build success
Use conventional commit format with lowercase type prefix and lowercase description (e.g., feat: add user authentication)

Files:

  • packages/docs/src/pages/docs/updates/notes.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (GEMINI.md)

**/*.{ts,tsx,js,jsx}: Use camelCase for variable and function names
Use PascalCase for class and component names
Always use async/await for handling promises instead of .then() chains
Add JSDoc comments for public functions and exported APIs
Use const by default, let if reassignment is needed, avoid var

Files:

  • packages/docs/src/pages/docs/updates/notes.tsx
packages/apple/Sources/Models/Types.swift

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT edit Types.swift in Sources/Models/ as it is auto-generated from the OpenIAP GraphQL schema; regenerate using ./scripts/generate-types.sh

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

OpenIAP official types in Sources/Models/ must match types defined at openiap.dev/docs/types

Files:

  • packages/apple/Sources/Models/Types.swift
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)

Applied to files:

  • packages/apple/Sources/OpenIapModule.swift
  • packages/gql/src/type-ios.graphql
  • packages/gql/src/type.graphql
  • packages/apple/Sources/Models/Types.swift
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Helpers/**/*.swift : Place internal helper classes in `Sources/Helpers/` (e.g., `ProductManager.swift` for thread-safe caching, `IapStatus.swift` for UI status); these are NOT official OpenIAP types

Applied to files:

  • packages/apple/Sources/OpenIapModule.swift
  • packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility

Applied to files:

  • packages/apple/Sources/OpenIapModule.swift
  • packages/gql/src/type-ios.graphql
  • packages/gql/src/type-android.graphql
  • packages/docs/src/pages/docs/updates/notes.tsx
  • packages/gql/src/type.graphql
  • packages/apple/Sources/Models/Types.swift
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`

Applied to files:

  • packages/apple/Sources/OpenIapModule.swift
  • packages/gql/src/type-ios.graphql
  • packages/gql/src/type.graphql
  • packages/apple/Sources/Models/Types.swift
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/gql/src/type-ios.graphql
  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/docs/src/pages/docs/updates/notes.tsx
  • packages/gql/src/type.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/**/*.swift : Use Pascal case for acronyms at the beginning or middle of names (e.g., `IapManager`, `IapPurchase`), but ALL CAPS only when appearing as a suffix (e.g., `ProductIAP`, `OpenIAP`)

Applied to files:

  • packages/apple/Sources/Models/Types.swift
🧬 Code graph analysis (5)
packages/apple/Sources/OpenIapModule.swift (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (4)
  • jws (2951-2968)
  • jws (3088-3105)
  • isValid (2073-2101)
  • isValid (2339-2376)
packages/gql/src/generated/Types.kt (4)
  • jws (3023-3040)
  • jws (3160-3177)
  • isValid (2145-2173)
  • isValid (2411-2448)
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (2)
  • product (6-8)
  • purchaseIOS (103-174)
packages/docs/src/pages/docs/updates/notes.tsx (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
  • code (1763-1785)
  • code (2378-2397)
packages/gql/src/generated/Types.kt (2)
  • code (1835-1857)
  • code (2450-2469)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (2)
packages/gql/src/generated/types.ts (3)
  • VerifyPurchaseProps (1166-1177)
  • VerifyPurchaseGoogleOptions (1134-1143)
  • VerifyPurchaseHorizonOptions (1150-1157)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
  • verifyPurchaseWithGooglePlay (26-101)
  • verifyPurchaseWithHorizon (107-176)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)
  • verifyPurchaseWithHorizon (107-176)
packages/gql/src/generated/types.ts (1)
  • VerifyPurchaseResultAndroid (1181-1200)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (3)
  • VerifyPurchaseAppleOptions (1125-1128)
  • VerifyPurchaseGoogleOptions (1134-1143)
  • VerifyPurchaseHorizonOptions (1150-1157)
🪛 detekt (1.23.8)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt

[warning] 319-319: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (11)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)

37-37: Import addition is fine.

packages/gql/src/type-ios.graphql (1)

230-239: New VerifyPurchaseAppleOptions input looks correct.
Please just confirm it’s actually wired into the VerifyPurchaseProps input in the overall schema (not only defined).

packages/docs/src/pages/docs/updates/notes.tsx (1)

22-105: Documentation looks comprehensive and well-structured.

The new content block clearly documents the platform-specific verification options introduced in this PR. The code example effectively demonstrates the new VerifyPurchaseProps structure with apple, google, and horizon fields, along with the deprecation notice for androidOptions.

packages/gql/src/type-android.graphql (1)

373-424: New GraphQL input types are well-defined.

The VerifyPurchaseGoogleOptions and VerifyPurchaseHorizonOptions input types are properly structured with clear documentation. Note that VerifyPurchaseGoogleOptions uses purchaseToken while the deprecated VerifyPurchaseAndroidOptions uses productToken - this naming improvement aligns with Google's API terminology.

Per the learnings, ensure you run ./scripts/generate-types.sh to regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) after modifying the GraphQL schema, then compile to verify compatibility.

packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (2)

42-82: Good test coverage for the new google field path.

The test properly validates that verifyPurchaseWithGooglePlay works with the new VerifyPurchaseGoogleOptions type and correctly parses the response fields.


267-301: Good Horizon verification test coverage.

The tests appropriately cover:

  1. Missing horizon options throws IllegalArgumentException
  2. Successful response parsing with success and grantTime fields
  3. Non-2xx responses throw InvalidPurchaseVerification
packages/gql/src/type.graphql (2)

190-242: Clear documentation on platform vs store semantics.

The documentation blocks effectively clarify that "platforms" refers to SDK/OS level (apple, google), with google targeting Play Store by default or Horizon when built with horizon flavor. This distinction is important for developers.


245-273: Well-structured platform-specific verification options.

The VerifyPurchaseProps input type now properly supports:

  • apple: App Store Server API
  • google: Google Play Developer API
  • horizon: Meta's S2S API

The deprecation of androidOptions in favor of google is clearly marked.

packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)

31-58: Clean backward-compatible implementation.

The when clause correctly prioritizes the new google field over the deprecated androidOptions, maintaining backward compatibility. The field mapping from legacyOptions.productToken to purchaseToken properly handles the naming difference between old and new APIs.


103-176: Horizon verification implementation looks correct.

The function properly:

  1. Validates required horizon options
  2. Uses URL encoding for form data parameters
  3. Posts to Meta's S2S API endpoint
  4. Parses the JSON response for success and grant_time
  5. Handles errors consistently with the Google Play verification path
packages/apple/Sources/Models/Types.swift (1)

1330-1346: Verify token field mapping for Android legacy vs new Google options.
Legacy VerifyPurchaseAndroidOptions uses productToken, while the new VerifyPurchaseGoogleOptions uses purchaseToken. Make sure all call sites and any bridging/serialization logic map these correctly (and that backend expects the new key name).

Also applies to: 1364-1385

BREAKING CHANGE: androidOptions is removed, use google options instead

- Remove androidOptions/legacyOptions from verifyPurchaseWithGooglePlay
- Capture and check Horizon verification result, throw error if failed
- Populate VerifyPurchaseResultAndroid with actual Horizon data
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)

84-165: Horizon verification implementation looks correct.

The implementation properly validates inputs, constructs the Meta S2S API request with URL-encoded form data, handles errors, and parses the response. The error handling is consistent with verifyPurchaseWithGooglePlay.

Note: A previous review comment already flagged that VerifyPurchaseResultHorizon (lines 162-165) should be moved to the GraphQL schema rather than maintained as a local definition.

🧹 Nitpick comments (1)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)

657-689: Consider adding SKU validation between top-level and Horizon-specific fields.

The previous review suggested validating that props.sku matches props.horizon?.sku to prevent accidental mismatches. This validation would catch configuration errors early.

Apply this diff to add the validation:

 override val verifyPurchase: MutationVerifyPurchaseHandler = { props ->
     // Use Horizon API if horizon options provided, otherwise fallback to Google Play
     if (props.horizon != null) {
         val horizonAppId = appId ?: throw OpenIapError.DeveloperError
+        // Validate SKU consistency
+        if (props.sku != props.horizon?.sku) {
+            throw OpenIapError.DeveloperError
+        }
         val horizonResult = verifyPurchaseWithHorizon(props, horizonAppId, TAG)
         if (!horizonResult.success) {
             throw OpenIapError.InvalidPurchaseVerification
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0959e8f and 547f7e3.

📒 Files selected for processing (2)
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (2 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable Kotlin helper functions in openiap/src/main/java/dev/hyo/openiap/utils/

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
🧬 Code graph analysis (1)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
  • verifyPurchaseWithHorizon (88-157)
  • verifyPurchaseWithGooglePlay (26-82)
packages/gql/src/generated/types.ts (1)
  • VerifyPurchaseResultAndroid (1181-1200)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Android
🔇 Additional comments (4)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)

37-37: LGTM!

The import is correctly placed and necessary for the Horizon verification functionality.

packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (3)

26-82: LGTM!

The migration from deprecated androidOptions to the new google field structure is correctly implemented with proper validation and error handling.


174-176: LGTM!

The updated comment accurately describes IAPKit support limitations for Android platforms.


254-267: LGTM!

The helper function correctly builds the Google Play payload for IAPKit verification with proper validation.

…idOptions

BREAKING CHANGE: VerifyPurchaseAndroidOptions is removed from schema

- Add VerifyPurchaseResultHorizon type to GraphQL schema
- Add VerifyPurchaseResultHorizon to VerifyPurchaseResult union
- Remove deprecated VerifyPurchaseAndroidOptions from schema
- Remove deprecated androidOptions from VerifyPurchaseProps
- Update verifyPurchase to return VerifyPurchaseResultHorizon directly
- Fix tests to use google options instead of androidOptions
- Regenerate types for all platforms
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
packages/apple/Sources/Models/Types.swift (1)

1154-1181: Docs are helpful, but “google targets Horizon” wording may confuse API consumers.
Consider tightening the wording to avoid implying runtime routing via the google field; it’s really “Android build flavor/provider selection happens at build time”. (If you change this, do it in the generator/template, not by editing this file.)

packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)

37-37: Horizon verification branch looks correct (success-gated + returns Horizon result); consider SKU mismatch guard.
You now throw OpenIapError.InvalidPurchaseVerification when horizonResult.success is false and return horizonResult on success—this addresses the prior bug. Optional hardening: if props.horizon.sku is present and differs from props.sku, fail fast (likely OpenIapError.DeveloperError) to avoid verifying/granting the wrong item.

Also applies to: 657-668

packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (2)

83-147: Existing Google Play tests updated correctly; consider consolidating duplicates.
You now exercise both “works with new google field” and “parses successful response” with near-identical payloads; optional to merge to reduce duplication.


267-321: Horizon tests are solid; fix detekt “swallowed exception” by using assertThrows.
The catch block at Line 318 triggers detekt. Prefer assertThrows(OpenIapError.InvalidPurchaseVerification::class.java) { ... } (and optionally assert on message/HTTP code if exposed).

packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (3)

27-56: Google Play verification refactor looks correct; consider requiring isSub (or documenting default).
Core change (use VerifyPurchaseGoogleOptions + Bearer token + endpoint switching) looks good. One potential footgun: isSub is nullable and currently treated as false when null, which will hit the /products endpoint—consider requiring explicit isSub or clearly documenting that null => one-time product.


58-83: Add connect/read timeouts to avoid indefinite hangs.
Both verification paths use HttpURLConnection without connectTimeout/readTimeout, which can block Dispatchers.IO threads on bad networks.


85-158: Horizon S2S implementation is reasonable; keep token out of logs and consider explicit charset.
Form encoding + non-2xx mapping to InvalidPurchaseVerification is good. Minor: write bytes as UTF-8 explicitly (toByteArray(Charsets.UTF_8)) for consistency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 547f7e3 and 9438e28.

⛔ Files ignored due to path filters (4)
  • packages/gql/src/generated/Types.kt is excluded by !**/generated/**
  • packages/gql/src/generated/Types.swift is excluded by !**/generated/**
  • packages/gql/src/generated/types.dart is excluded by !**/generated/**
  • packages/gql/src/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (7)
  • packages/apple/Sources/Models/Types.swift (6 hunks)
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (2 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (8 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (4 hunks)
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (4 hunks)
  • packages/gql/src/type-android.graphql (1 hunks)
  • packages/gql/src/type.graphql (4 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable Kotlin helper functions in openiap/src/main/java/dev/hyo/openiap/utils/

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
packages/gql/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run generate to regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) from the GraphQL schema

Files:

  • packages/gql/src/type-android.graphql
  • packages/gql/src/type.graphql
packages/apple/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

packages/apple/**/*.swift: iOS-specific functions MUST have IOS suffix (e.g., presentCodeRedemptionSheetIOS(), showManageSubscriptionsIOS())
Use Pascal case for acronyms at the beginning or middle of names (e.g., IapManager, IapPurchase), but ALL CAPS only when appearing as a suffix (e.g., ProductIAP, OpenIAP)

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/Types.swift

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT edit Types.swift in Sources/Models/ as it is auto-generated from the OpenIAP GraphQL schema; regenerate using ./scripts/generate-types.sh

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

OpenIAP official types in Sources/Models/ must match types defined at openiap.dev/docs/types

Files:

  • packages/apple/Sources/Models/Types.swift
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt
  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/gql/src/type.graphql
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/gql/src/type.graphql
  • packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
  • packages/gql/src/type.graphql
  • packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to **/*.{ts,tsx} : Android-specific functions must end with `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`)

Applied to files:

  • packages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Helpers/**/*.swift : Place internal helper classes in `Sources/Helpers/` (e.g., `ProductManager.swift` for thread-safe caching, `IapStatus.swift` for UI status); these are NOT official OpenIAP types

Applied to files:

  • packages/apple/Sources/Models/Types.swift
🧬 Code graph analysis (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (1)
  • setRequestProperty (338-342)
packages/gql/src/generated/types.ts (1)
  • VerifyPurchaseResultHorizon (1196-1201)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (4)
  • VerifyPurchaseResultHorizon (1196-1201)
  • VerifyPurchaseAppleOptions (1117-1120)
  • VerifyPurchaseGoogleOptions (1126-1135)
  • VerifyPurchaseHorizonOptions (1142-1149)
packages/apple/Sources/Models/Types.swift (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (8)
  • grantTime (2343-2368)
  • apiKey (3026-3055)
  • apple (3194-3229)
  • jws (2982-2999)
  • jws (3091-3108)
  • accessToken (3114-3149)
  • accessToken (3156-3185)
  • purchaseToken (3001-3018)
packages/gql/src/generated/Types.kt (8)
  • grantTime (2415-2440)
  • apiKey (3098-3127)
  • apple (3266-3301)
  • jws (3054-3071)
  • jws (3163-3180)
  • accessToken (3186-3221)
  • accessToken (3228-3257)
  • purchaseToken (3073-3090)
packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt (1)
  • sku (163-172)
🪛 detekt (1.23.8)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt

[warning] 318-318: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test iOS
  • GitHub Check: Test Android
🔇 Additional comments (15)
packages/gql/src/type-android.graphql (1)

373-429: Well-structured platform-specific verification types.

The new Google Play and Horizon verification inputs and result types are properly documented and correctly capture the required fields for each platform's verification API.

packages/gql/src/type.graphql (4)

245-269: Clarify androidOptions removal vs. deprecation.

The PR objectives state "Deprecate androidOptions" but the schema shows VerifyPurchaseProps without an androidOptions field—indicating a removal rather than deprecation. If androidOptions was completely removed, this is a breaking change that:

  • Breaks clients still using the old field
  • Requires clear migration guidance
  • Should be marked as @deprecated with replacement hints before removal

Please verify:

  1. Was androidOptions removed entirely or deprecated with a @deprecated marker?
  2. If removed, confirm this breaking change is intentional and documented in release notes.
  3. If deprecated, add the @deprecated(reason: "Use google instead") marker to the schema.

190-242: Platform naming documentation is clear.

The added doc comments for RequestPurchasePropsByPlatforms and new RequestSubscriptionPropsByPlatforms clearly explain the distinction between SDK/OS "platforms" (apple, google) and stores, and properly mark deprecated aliases. This prevents confusion when the google platform targets different stores (Play Store vs. Horizon) based on build-time flavor.


270-309: Union and IAPKit types are well-structured.

The addition of VerifyPurchaseResultHorizon to the union and the new RequestVerifyPurchaseWithIapkitProps with platform-specific sub-inputs follow the established pattern and are properly documented.


1-309: Regenerate types for all platforms after GraphQL schema changes.

Per the coding guidelines, after GraphQL schema modifications, all platform-specific types (TypeScript, Swift, Kotlin, Dart) must be regenerated.

Run the following command to regenerate:

bun run generate

Verify that generated types in packages/apple/, packages/google/, and other platform packages reflect these schema changes before merging.

packages/apple/Sources/Models/Types.swift (6)

847-855: VerifyPurchaseResultHorizon looks consistent with existing cross-platform shape.
Fields (grantTime, success) align with the Kotlin types described in the context snippets, and Codable is appropriate here.


1246-1273: RequestSubscriptionPropsByPlatforms mirrors purchase props nicely.
No issues in the struct shape or init defaults.


1297-1318: RequestVerifyPurchaseWithIapkitProps additions are straightforward.
Clear separation of apple vs google inputs; Codable/init defaults look good.


1746-1750: Adding the Horizon case to VerifyPurchaseResult is expected for multi-provider verification.
Just ensure downstream decoding/handling is updated anywhere this enum is switched over (especially exhaustive switches in Swift).


1398-1424: This code is auto-generated from the GraphQL schema and the duplicate sku fields serve different purposes: VerifyPurchaseProps.sku is the general product SKU for validation, while VerifyPurchaseHorizonOptions.sku is the specific add-on item SKU required by Meta's API. Both fields are intentionally defined in the GraphQL schema and are not a contract footgun.

Likely an incorrect or invalid review comment.


1-4: No issues found. This PR properly follows the code generation workflow: the GraphQL schema was updated with VerifyPurchaseResultHorizon, and types were regenerated across all platforms using bun run generate. The commit message confirms "Regenerate types for all platforms," and the generated files are correctly synced. No manual edits to Types.swift were made.

packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (2)

5-13: Import updates align with new provider-specific types.


41-82: Good coverage for the new google options happy path.

packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)

247-260: buildGooglePayload extraction is a good cleanup.

packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)

2339-2368: Generated types update matches the new provider-specific verification model; please confirm they were regenerated (not hand-edited) across packages.
Given this is AUTO-GENERATED, please double-check the changes came from schema updates + regeneration (per repo guidance: update packages/google/openiap-versions.json → run ./scripts/generate-types.sh, and similarly for Apple + swift test). Based on learnings, do not manually edit this file.

Also applies to: 2801-2844, 2937-2980, 3020-3055, 3087-3229, 3324-3326

Comment thread packages/apple/Sources/Models/Types.swift
- Remove IAPKit request/response body logging to prevent token leaks
- Remove responseBody from error logs
- Rename test to reflect removal of androidOptions
- Add SECURITY warnings to VerifyPurchaseAppleOptions (jws)
- Add SECURITY warnings to VerifyPurchaseGoogleOptions (accessToken, purchaseToken)
- Add SECURITY warnings to VerifyPurchaseHorizonOptions (accessToken)
- Regenerate types for all platforms with security documentation
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)

52-83: Set connectTimeout/readTimeout on HttpURLConnection to avoid hangs.
Both Google and Horizon requests can block indefinitely today; this is painful in client SDKs and hard to recover from.

Example:

     val connection = connectionFactory(url).apply {
         requestMethod = "GET"
+        connectTimeout = 10_000
+        readTimeout = 10_000
         setRequestProperty("Content-Type", "application/json")
         setRequestProperty("Authorization", "Bearer $accessToken")
     }

(and similarly for Horizon POST).

Also applies to: 108-158


65-67: Avoid logging full verification response bodies (truncate/redact).
Even on failures, responses can include identifiers and diagnostic payloads; safer to log status + a capped snippet.

Also applies to: 132-135

♻️ Duplicate comments (1)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (1)

28-39: Prefer assertThrows (and assert message) instead of swallowing exceptions in these tests.
Detekt’s swallowed-exception warning (Line 318) is valid here; assertTrue(true) inside catch also hides regressions. This is the same theme as the earlier review note about preferring assertThrows.

Example direction:

+import org.junit.Assert.assertThrows
 ...
-        try {
-            verifyPurchaseWithHorizon(...)
-            throw AssertionError("Expected IllegalArgumentException for missing horizon options")
-        } catch (expected: IllegalArgumentException) {
-            // Expected path
-        }
+        assertThrows(IllegalArgumentException::class.java) {
+            runTest { verifyPurchaseWithHorizon(...) { _ -> ... } }
+        }

Also applies to: 127-147, 269-321

🧹 Nitpick comments (2)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (2)

41-82: Make this test assert the “new google field” behavior (URL + auth header), or drop it as redundant.
Right now it overlaps heavily with verifyPurchaseWithGooglePlay parses successful response, but doesn’t validate the key refactor: URL construction using packageName + purchaseToken and the Authorization: Bearer <accessToken> header.

Concrete tweak:

-        val result = verifyPurchaseWithGooglePlay(
-            props,
-            "TEST_TAG"
-        ) { _ -> FakeHttpURLConnection(200, body) }
+        var seenUrl: String? = null
+        val connection = FakeHttpURLConnection(200, body)
+        val result = verifyPurchaseWithGooglePlay(props, "TEST_TAG") { url ->
+            seenUrl = url
+            connection
+        }
+        assertTrue(seenUrl!!.contains("/applications/dev.hyo.app/"))
+        assertEquals("Bearer token", connection.headers["Authorization"])

283-321: Horizon tests: assert form-encoded body + content-type (core behavior) instead of only response parsing.
You already have writtenBody and headers; use them to validate the request payload (access_token, user_id, sku) and Content-Type.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9438e28 and 8704c07.

📒 Files selected for processing (2)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (5 hunks)
  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable Kotlin helper functions in openiap/src/main/java/dev/hyo/openiap/utils/

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/**/*.swift : Use Pascal case for acronyms at the beginning or middle of names (e.g., `IapManager`, `IapPurchase`), but ALL CAPS only when appearing as a suffix (e.g., `ProductIAP`, `OpenIAP`)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
🧬 Code graph analysis (1)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (2)
packages/gql/src/generated/types.ts (3)
  • VerifyPurchaseProps (1158-1167)
  • VerifyPurchaseGoogleOptions (1126-1135)
  • VerifyPurchaseHorizonOptions (1142-1149)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
  • verifyPurchaseWithGooglePlay (27-83)
  • verifyPurchaseWithHorizon (89-158)
🪛 detekt (1.23.8)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt

[warning] 318-318: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Android
🔇 Additional comments (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)

167-175: buildGooglePayload() extraction looks good.
Clearer separation and keeps the IAPKit request construction focused.

Also applies to: 240-253

- Add encodePathSegment helper for URL path encoding
- URL-encode packageName, sku, purchaseToken in Google Play API URL
- Use explicit Charsets.UTF_8 for Horizon form data encoding
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)

116-116: Include charset in Content-Type header.

The Content-Type header should specify charset=UTF-8 to match the byte encoding used when writing the form data.

Apply this diff:

-        setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
+        setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8704c07 and 0b800ce.

⛔ Files ignored due to path filters (4)
  • packages/gql/src/generated/Types.kt is excluded by !**/generated/**
  • packages/gql/src/generated/Types.swift is excluded by !**/generated/**
  • packages/gql/src/generated/types.dart is excluded by !**/generated/**
  • packages/gql/src/generated/types.ts is excluded by !**/generated/**
📒 Files selected for processing (5)
  • packages/apple/Sources/Models/Types.swift (6 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (8 hunks)
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (6 hunks)
  • packages/gql/src/type-android.graphql (1 hunks)
  • packages/gql/src/type-ios.graphql (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/gql/src/type-ios.graphql
🧰 Additional context used
📓 Path-based instructions (6)
packages/gql/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Run bun run generate to regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) from the GraphQL schema

Files:

  • packages/gql/src/type-android.graphql
packages/google/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT add Android suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use acknowledgePurchase() not acknowledgePurchaseAndroid())

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable Kotlin helper functions in openiap/src/main/java/dev/hyo/openiap/utils/

Files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
packages/apple/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

packages/apple/**/*.swift: iOS-specific functions MUST have IOS suffix (e.g., presentCodeRedemptionSheetIOS(), showManageSubscriptionsIOS())
Use Pascal case for acronyms at the beginning or middle of names (e.g., IapManager, IapPurchase), but ALL CAPS only when appearing as a suffix (e.g., ProductIAP, OpenIAP)

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/Types.swift

📄 CodeRabbit inference engine (CLAUDE.md)

DO NOT edit Types.swift in Sources/Models/ as it is auto-generated from the OpenIAP GraphQL schema; regenerate using ./scripts/generate-types.sh

Files:

  • packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

OpenIAP official types in Sources/Models/ must match types defined at openiap.dev/docs/types

Files:

  • packages/apple/Sources/Models/Types.swift
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify

Applied to files:

  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility

Applied to files:

  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/apple/Sources/Models/Types.swift
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`

Applied to files:

  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)

Applied to files:

  • packages/gql/src/type-android.graphql
  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to **/*.{ts,tsx} : Android-specific functions must end with `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`)

Applied to files:

  • packages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/apple/Sources/Models/Types.swift
  • packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
  • packages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/**/*.swift : Use Pascal case for acronyms at the beginning or middle of names (e.g., `IapManager`, `IapPurchase`), but ALL CAPS only when appearing as a suffix (e.g., `ProductIAP`, `OpenIAP`)

Applied to files:

  • packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Helpers/**/*.swift : Place internal helper classes in `Sources/Helpers/` (e.g., `ProductManager.swift` for thread-safe caching, `IapStatus.swift` for UI status); these are NOT official OpenIAP types

Applied to files:

  • packages/apple/Sources/Models/Types.swift
🧬 Code graph analysis (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (1)
  • setRequestProperty (338-342)
packages/gql/src/generated/types.ts (1)
  • VerifyPurchaseResultHorizon (1214-1219)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (4)
  • VerifyPurchaseResultHorizon (1214-1219)
  • VerifyPurchaseAppleOptions (1119-1125)
  • VerifyPurchaseGoogleOptions (1133-1148)
  • VerifyPurchaseHorizonOptions (1157-1167)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Android
🔇 Additional comments (21)
packages/gql/src/type-android.graphql (4)

373-398: Approve the new VerifyPurchaseGoogleOptions input with security documentation, but regenerate types for all platforms.

The GraphQL input type replaces the old VerifyPurchaseAndroidOptions with a clearer, more descriptive name that aligns with platform branding. The doc comments are excellent and prominently feature security warnings (⚠️) about sensitive tokens—this is a best practice for developer-facing schemas. Field definitions are correct: required fields (packageName, purchaseToken, accessToken) are properly marked with !, and the optional isSub field allows flexibility.

However, since this is a GraphQL schema file change, the schema is self-documenting and can include descriptions with Markdown, which you've done well. The critical next step is type regeneration.

Per the coding guidelines for packages/gql/**/*, run bun run generate to regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) from the updated GraphQL schema. Please confirm this has been completed before merge.


400-421: Approve VerifyPurchaseHorizonOptions with security documentation.

The new Horizon (Meta Quest) verification input is well-designed. The doc comments include the API endpoint, security warnings about the accessToken token, and clear field descriptions. All required fields are properly marked with !. The schema is correct and follows the same security-first documentation pattern as the Google options above.


423-436: Approve VerifyPurchaseResultHorizon type.

The new result type for Horizon verification is correctly defined. The success field is required (Boolean!), and grantTime is properly optional (Float) to accommodate cases where the grant timestamp may not be available. Documentation is clear and concise.


373-437: Verify schema completeness and old input removal.

Confirm the following to ensure the refactor is complete:

  1. Old input removal: Ensure VerifyPurchaseAndroidOptions has been fully removed from the GraphQL schema (across all schema files, if it was defined elsewhere).
  2. Apple options: Verify that VerifyPurchaseAppleOptions (mentioned in PR objectives with jws field) has been added to the appropriate iOS/Apple GraphQL schema file (e.g., type-ios.graphql).
  3. Root types: Check that the VerifyPurchaseResultHorizon type is properly wired into the mutation/query root types (i.e., that there is a mutation that returns this type).
  4. IAPKit options: The PR objectives mention VerifyPurchaseIAPKitOptions, but it's not present in this file. Verify it has been added to the appropriate schema file if needed.
packages/apple/Sources/Models/Types.swift (4)

1-4: Acknowledge: Auto-generated file - changes sourced from GraphQL schema.

This file is auto-generated and should not be manually edited. Based on learnings, any corrections must be made in the GraphQL schema, then regenerated using ./scripts/generate-types.sh.


847-854: LGTM: Horizon verification result type added.

The new VerifyPurchaseResultHorizon struct appropriately supports Meta Horizon entitlement verification, aligning with the PR objectives.


1338-1406: Security warnings properly address past concerns.

The security warnings for sensitive tokens (jws, accessToken, purchaseToken) in these Codable structs appropriately address the previous review concern about tokens potentially appearing in logs or crash reports. Higher-layer code must respect these warnings and implement proper redaction.

Based on past review comments, this concern has been addressed.


1408-1434: LGTM: Verification parameters updated per PR objectives.

The VerifyPurchaseProps structure now supports platform-specific verification (Apple, Google, Horizon) as intended. The documentation clearly explains which API each platform targets, and the structure aligns with the PR's goal to add multi-platform verification support.

packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (6)

13-13: LGTM! Type now properly generated.

The VerifyPurchaseResultHorizon type is now correctly defined in the auto-generated Types.kt file (lines 2343-2368), resolving the previous concern about local type definitions.


28-29: LGTM! Correct URL path encoding.

The helper correctly encodes path segments using UTF-8 and replaces + with %20, which is appropriate for URL paths (not query strings).


36-60: LGTM! URL encoding and security properly addressed.

The refactoring to use google options and application of encodePathSegment to all path parameters correctly addresses the previous URL encoding concerns. Sensitive tokens are not logged.


172-179: LGTM! Correct clarification about Horizon support.

The comment accurately clarifies that Horizon verification is not supported through IAPKit and requires direct S2S API calls (implemented separately in verifyPurchaseWithHorizon).


194-194: LGTM! UTF-8 encoding correctly applied.

The body is now written using explicit UTF-8 encoding, addressing the previous charset concern.


245-258: LGTM! Clean payload builder for IAPKit integration.

The function appropriately extracts Google payload building logic and validates required fields before constructing the minimal IAPKit payload.

packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (7)

2339-2368: LGTM! Type definition matches GraphQL schema.

The VerifyPurchaseResultHorizon data class correctly matches the GraphQL schema with appropriate field types, nullability, and documentation.


2801-2844: LGTM! Clear documentation of platform vs. store distinction.

The added documentation effectively clarifies that "platforms" refers to SDK/OS level (apple, google) rather than stores, and explains the build-time determination of Horizon support.


2937-2980: LGTM! Consistent documentation pattern.

The documentation mirrors the approach in RequestPurchasePropsByPlatforms, maintaining clarity and consistency across related types.


3020-3055: LGTM! Clear IAPKit verification documentation.

The documentation effectively clarifies the verification approach for each platform (App Store JWS vs. Play Store token) supported by IAPKit.


3087-3195: LGTM! Comprehensive verification option types with security warnings.

The three new verification option types (Apple, Google, Horizon) are well-documented with appropriate security warnings about sensitive tokens, and match the GraphQL schema.


3197-3239: LGTM! Complete verification props with all platforms.

The VerifyPurchaseProps type properly integrates Apple, Google, and Horizon verification options with clear documentation and correct serialization/deserialization.


3332-3340: LGTM! Horizon result properly integrated into union type.

The VerifyPurchaseResultHorizon case is correctly added to the VerifyPurchaseResult sealed interface, completing the type integration.

@hyochan hyochan merged commit b5c1dce into main Dec 12, 2025
5 checks passed
@hyochan hyochan deleted the refactor/verifyPurchase branch December 12, 2025 18:54
hyochan added a commit that referenced this pull request Dec 12, 2025
Refactor again based on #53 

- Remove sku from VerifyPurchaseProps root level
- Add sku to VerifyPurchaseAppleOptions (replaces jws)
- Add sku to VerifyPurchaseGoogleOptions
- Update Kotlin code to use googleOptions.sku
- Update tests for new schema structure
- Update release notes for v1.3.4/v1.3.14/v1.3.2

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Breaking Changes
* **verifyPurchase API Refactored**: SKU parameter moved from root-level
options into platform-specific options. Apple and Google options now
require platform-specific SKU fields. Apple verification updated to use
SKU instead of legacy JWS field. Horizon platform option added.

## Documentation
* Updated release notes and code examples to reflect new API structure.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
hyochan added a commit to hyochan/expo-iap that referenced this pull request Dec 13, 2025
- Update VerifyPurchaseProps to use apple/google/horizon fields
- Remove deprecated sku root field and androidOptions
- Update native iOS/Android modules for new API structure
- Update example apps with platform-specific verification
- Update documentation and blog for 3.2.0 release
- Bump openiap versions (gql 1.3.4, google 1.3.14, apple 1.3.2)

Reflects hyodotdev/openiap#53 and
hyodotdev/openiap#54

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* verifyPurchase API restructured to accept platform-specific provider
options (apple, google, horizon).
  * Added Horizon (Meta) verification support.

* **Improvements**
* Stronger input validation and safer UI guards for purchase/discount
handling.
  * Verification libraries updated to newer versions.

* **Documentation**
* Docs and examples updated for the new parameter shape and breaking
change notes.

* **Tests**
  * Tests updated to exercise provider-wrapped input shapes.

* **Chores**
  * Pre-commit now re-stages linted files before running tests.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant