feat: verify purchase with provider#46
Conversation
|
Warning Rate limit exceeded@hyochan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 29 seconds before requesting another review. ⌛ 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. 📒 Files selected for processing (5)
WalkthroughAdds provider-based purchase verification (IAPKit) and renames receipt validation APIs to VerifyPurchase across platforms. Introduces new enums/props/results for IAPKit, implements verifyPurchaseWithProvider (with IAPKit HTTP payloads) on Apple and Google, updates GraphQL schema, preserves backward-compatible deprecated aliases, and updates tests and examples to exercise the new flows. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant OpenIapStore
participant OpenIapModule
participant IAPKit
Client->>OpenIapStore: verifyPurchaseWithProvider(props)
OpenIapStore->>OpenIapModule: verifyPurchaseWithProvider(props)
OpenIapModule->>OpenIapModule: validate provider & iapkit props
OpenIapModule->>OpenIapModule: build IAPKit payload (Apple/Google)
OpenIapModule->>IAPKit: POST payload (optional Bearer API key)
IAPKit-->>OpenIapModule: 2xx + JSON (per-store results)
OpenIapModule-->>OpenIapStore: VerifyPurchaseWithProviderResult(iapkitResults, provider)
OpenIapStore-->>Client: [RequestVerifyPurchaseWithIapkitResult]
sequenceDiagram
participant Client
participant OpenIapStore
participant OpenIapModule
Client->>OpenIapStore: verifyPurchase(sku)
OpenIapStore->>OpenIapModule: verifyPurchase(VerifyPurchaseProps)
OpenIapModule->>OpenIapModule: choose verify path (GooglePlay / IAPKit / local)
OpenIapModule->>OpenIapModule: perform platform/provider verification
OpenIapModule-->>OpenIapStore: VerifyPurchaseResult (Android or iOS)
OpenIapStore-->>Client: VerifyPurchaseResultIOS (extracted)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–75 minutes
Focus areas:
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/gql/src/type.graphql (1)
221-288: IAPKit/provider input/result wiring is well-shaped; keep invariants in resolversThe input/result types (
RequestVerifyPurchaseWithIapkit*,VerifyPurchaseWithProviderProps/Result) match the platform models and docs clearly state whenapple/VerifyPurchaseWithProviderResult(currentlyiapkit) to preserve the intended union-like semantics.packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
5-6: IAPKit verification implementation is solid; consider tiny logging/DRY tweaksThe new
verifyPurchaseWithIapkitimplementation looks correct:
- Validates endpoint and store-specific fields (Apple receipt/appId, Google packageName/purchaseId/purchaseToken).
- Runs on
Dispatchers.IO, builds a JSON payload consistent with the new Iapkit types, and posts with optional BearerapiKey.- Maps non‑2xx/JSON issues to
OpenIapError.InvalidReceiptand IO problems toOpenIapError.NetworkError, matching existingvalidateReceiptWithGooglePlaypatterns.Two minor nits you may want to adjust:
- The HTTP error log message says
"verifyPurchaseWithProvider failed"from insideverifyPurchaseWithIapkit; renaming it for this function would make logs easier to follow.- The HTTP/JSON handling is nearly identical to
validateReceiptWithGooglePlay; if this pattern grows further, a shared helper for “execute JSON HTTP request and parse” could reduce duplication.Also applies to: 10-14, 85-175
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
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.tsis excluded by!**/generated/**
📒 Files selected for processing (15)
.vscode/settings.json(1 hunks)packages/apple/Sources/Models/Types.swift(10 hunks)packages/apple/Sources/OpenIapModule.swift(1 hunks)packages/apple/Sources/OpenIapProtocol.swift(1 hunks)packages/apple/Sources/OpenIapStore.swift(1 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift(3 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift(1 hunks)packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt(3 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(8 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt(2 hunks)packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt(4 hunks)packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt(2 hunks)packages/gql/src/api.graphql(1 hunks)packages/gql/src/type.graphql(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/gql/src/type.graphqlpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/apple/Sources/Models/Types.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
🧬 Code graph analysis (10)
packages/apple/Sources/OpenIapProtocol.swift (4)
packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithProvider(594-603)packages/apple/Sources/OpenIapStore.swift (1)
verifyPurchaseWithProvider(388-394)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
verifyPurchaseWithProvider(2537-2537)packages/gql/src/generated/Types.kt (1)
verifyPurchaseWithProvider(2604-2604)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(85-175)
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (2)
packages/gql/src/generated/types.ts (3)
RequestVerifyPurchaseWithIapkitProps(810-821)RequestVerifyPurchaseWithIapkitAppleProps(792-799)RequestVerifyPurchaseWithIapkitGoogleProps(801-808)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(85-175)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (1)
setRequestProperty(265-269)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (2)
packages/gql/src/generated/types.ts (10)
RequestVerifyPurchaseWithIapkitResult(823-826)ReceiptValidationResult(625-625)ReceiptValidationResultIOS(648-657)VerifyPurchaseWithProviderResult(890-892)VerifyPurchaseWithProviderProps(885-888)RequestVerifyPurchaseWithIapkitProps(810-821)RequestVerifyPurchaseWithIapkitAppleProps(792-799)IapkitStore(177-177)PurchaseError(481-485)Subscription(828-840)packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (24)
initConnection(75-75)endConnection(76-76)fetchProducts(79-79)getPromotedProductIOS(80-80)requestPurchase(83-83)requestPurchaseOnPromotedProductIOS(84-84)restorePurchases(85-85)getAvailablePurchases(86-86)finishTransaction(89-89)getPendingTransactionsIOS(90-90)clearTransactionIOS(91-91)isTransactionVerifiedIOS(92-92)getTransactionJwsIOS(93-93)currentEntitlementIOS(94-94)latestTransactionIOS(95-95)getReceiptDataIOS(98-98)validateReceiptIOS(99-104)validateReceipt(106-108)verifyPurchase(110-112)purchaseUpdatedListener(138-140)purchaseErrorListener(142-144)promotedProductListenerIOS(146-148)removeListener(150-152)removeAllListeners(154-154)
packages/apple/Sources/OpenIapModule.swift (3)
packages/apple/Sources/OpenIapStore.swift (1)
verifyPurchaseWithProvider(388-394)packages/gql/src/generated/Types.kt (11)
verifyPurchaseWithProvider(2604-2604)iapkit(2395-2412)message(1537-1556)store(1647-1666)appId(608-660)appId(2290-2319)packageName(2321-2350)request(2104-2155)apiKey(2352-2393)error(773-798)error(803-828)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(85-175)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(85-175)
packages/apple/Sources/OpenIapStore.swift (2)
packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithProvider(594-603)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)
verifyPurchaseWithProvider(2537-2537)iapkit(2328-2345)
packages/apple/Sources/Models/Types.swift (4)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (8)
iapkit(2328-2345)store(1580-1599)appId(541-593)appId(2223-2252)packageName(2254-2283)apiKey(2285-2326)verifyPurchaseWithProvider(2537-2537)verifyPurchase(2533-2533)packages/gql/src/generated/Types.kt (8)
iapkit(2395-2412)store(1647-1666)appId(608-660)appId(2290-2319)packageName(2321-2350)apiKey(2352-2393)verifyPurchaseWithProvider(2604-2604)verifyPurchase(2600-2600)packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchaseWithProvider(594-603)verifyPurchase(589-592)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchaseWithProvider(388-394)verifyPurchase(376-386)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (5)
RequestVerifyPurchaseWithIapkitResult(823-826)RequestVerifyPurchaseWithIapkitAppleProps(792-799)RequestVerifyPurchaseWithIapkitGoogleProps(801-808)RequestVerifyPurchaseWithIapkitProps(810-821)VerifyPurchaseWithProviderProps(885-888)
⏰ 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 (25)
.vscode/settings.json (1)
4-4: Verify the casing of the IAPKit provider name.The word is added as "Iapkit" but the PR summary and broader context reference "IAPKit" (with capital A, P, K). Ensure the casing matches how the provider is actually referenced throughout the codebase to avoid spell-checker false positives.
packages/apple/Sources/OpenIapModule.swift (2)
594-603: LGTM! Clean provider validation and delegation.The method correctly validates the provider type and required parameters before delegating to the helper. Error messages are clear and use appropriate error codes.
605-687: <!-- [scratchpad_start]
[task review_verification_1/1]
Verify cross-platform consistency claims in review comment for verifyPurchaseWithIapkit[observations]
Script output shows significant differences between Swift and Kotlin implementations:Swift (lines 605-687):
- Endpoint validation: guard !endpoint.isEmpty AND URL(string: endpoint) in single check
- Error handling: Uses custom makePurchaseError(code:, message:) with codes like .developerError, .networkError, .receiptFailed
- Apple validation: Throws makePurchaseError consistently
- Google validation: Throws makePurchaseError consistently
- Network errors: Mapped via URLError catch to .networkError
- Parse errors: Mapped to .receiptFailed
Kotlin (from script output):
- Endpoint validation: takeIf { it.isNotBlank() } (does NOT validate URL parsing)
- Error handling: MIXES IllegalArgumentException (for validation) and OpenIapError.InvalidReceipt / OpenIapError.NetworkError
- Apple validation: Throws IllegalArgumentException (different from endpoint)
- Network errors: Mapped via IOException catch to OpenIapError.NetworkError
- Parse errors: Mapped to OpenIapError.InvalidReceipt
[analysis]
The review comment claims implementations "closely mirror" each other for consistency. However:
- Error handling strategies DIFFER: Swift uses unified PurchaseError codes; Kotlin uses mixed IllegalArgumentException + OpenIapError
- Endpoint validation differs: Swift validates URL parsing; Kotlin only checks blank
- These are intentional platform-specific conventions, NOT bugs in the Swift code
- The review comment's claim about mirroring consistency is overstated/inaccurate
- The review comment uses invalid categorization tags (, don't exist)
[analysis_conclusion]
The review comment makes an inaccurate claim about cross-platform consistency. The implementations intentionally differ for platform conventions. The review comment itself contains factual errors about the degree of mirroring between implementations and uses invalid categorization tags.[done]
Ready to generate corrected review comment
[scratchpad_end]Swift implementation is well-structured with comprehensive error handling; however, note that the Kotlin implementation uses a different error strategy.
The method properly validates inputs, constructs platform-specific payloads, and maps errors appropriately. However, the Swift and Kotlin implementations differ in their error handling approaches: Swift uses custom
PurchaseErrorcodes consistently, while Kotlin mixesIllegalArgumentExceptionfor validation withOpenIapErrorenums for other failures. Additionally, Swift validates URL parsing on the endpoint while Kotlin only checks for blank strings. These differences appear intentional for platform conventions, but verify they don't create unexpected behavior in cross-platform scenarios.packages/apple/Sources/OpenIapProtocol.swift (1)
49-49: LGTM! Protocol extension aligns with implementation.The method signature correctly matches the implementation in
OpenIapModule.swiftand is appropriately placed in the Validation section.packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt (1)
30-30: LGTM! Protocol property follows existing patterns.The property is correctly defined using the mutation handler typealias and is appropriately placed alongside the existing
verifyPurchasehandler.packages/apple/Sources/OpenIapStore.swift (1)
388-394: LGTM! Clean delegation to module with appropriate result unpacking.The method follows the established pattern in this file (similar to
verifyPurchaseabove) and correctly unpacks the.iapkitcase from the provider result.packages/gql/src/api.graphql (1)
77-83: LGTM! GraphQL mutation properly defined and documented.The mutation field is well-documented and correctly placed alongside the existing
verifyPurchasemutation. The use of non-nullable types for both input and output is appropriate for this verification operation.packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (3)
103-169: Excellent validation test coverage for required parameters.The three tests comprehensively cover the validation requirements:
- Missing Apple props when store is Apple
- Missing/blank endpoint
- Production mode requiring appId
171-224: Thorough verification of HTTP request construction and response parsing.The tests properly validate:
- Request body structure (using Gson to parse captured body)
- Authorization header inclusion (Bearer token)
- Store-specific payload fields (Apple: store/receipt/environment/appId, Google: store/packageName/purchaseId/purchaseToken)
- Response parsing to
RequestVerifyPurchaseWithIapkitResult
256-278: Good test infrastructure enhancements for request verification.The additions to
FakeHttpURLConnectionenable proper verification of outbound HTTP requests:
headersmap capturessetRequestPropertycallswrittenBodycaptures the request payload via a customOutputStream- These enhancements are essential for the new test assertions
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (3)
7-42: Good test coverage for successful provider verification path.The test properly validates:
- Non-nil result is returned when provider returns iapkit result
- Store field matches expected value (
.apple)- Valid field reflects the verification outcome (
true)
44-76: Proper test for nil result handling.The test correctly validates that the store method returns
nilwhen the provider result's iapkit case containsnil. This ensures the unwrapping logic inOpenIapStore.verifyPurchaseWithProviderworks as expected.
79-170: Well-structured test double following established patterns.The
FakeVerifyPurchaseModuleproperly implementsOpenIapModuleProtocolwith:
- Configurable
providerResultfor testing different scenarios- All required protocol methods stubbed appropriately
- Clean initialization accepting test parameters
This follows the same pattern as the fake module in
VerifyPurchaseTests.swift.packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (1)
64-72: Good integration of provider path into existing test infrastructure.The changes properly extend the fake module to support the new provider verification path:
providerResultproperty with sensible default (.iapkit(nil))- Updated initializer maintains backward compatibility
- Simple
verifyPurchaseWithProviderimplementation returns configured resultThis allows the existing tests to continue working while enabling new provider-based tests.
Also applies to: 114-116
packages/gql/src/type.graphql (1)
33-47: IAPKit/provider enums look consistent with generated Kotlin/Swift typesEnum names and values align with the new
IapkitEnvironment,IapkitStore, andPurchaseVerificationProvidertypes in Kotlin/Swift, so schema ↔ client codegen should stay coherent. No issues here.packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
39-42: Horizon IAPKit provider flow is correctly gated and wiredThe Horizon module’s
verifyPurchaseWithProvidermirrors the Swift implementation: it only accepts.Iapkit, throwsFeatureNotSupportedfor others, requiresiapkitoptions (elseDeveloperError), and delegates toverifyPurchaseWithIapkitbefore wrapping inVerifyPurchaseWithProviderResultIapkit. MutationHandlers wiring looks correct.Also applies to: 661-667, 696-697
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
36-39: Play IAPKit provider verification matches Horizon/iOS behaviorThe Play module’s
verifyPurchaseWithProvideris symmetric with Horizon and the Swift implementation: it restricts to.Iapkit, validates presence ofiapkitoptions, callsverifyPurchaseWithIapkit, and wraps the result inVerifyPurchaseWithProviderResultIapkit, then exposes it viamutationHandlers. This keeps the provider path consistent across platforms.Also applies to: 66-67, 812-818, 847-848
packages/apple/Sources/Models/Types.swift (1)
153-161: Swift types/protocols cleanly mirror the new GraphQL/provider modelThe added enums (
IapkitEnvironment,IapkitStore,PurchaseVerificationProvider), IAPKit input/result structs,VerifyPurchaseWithProviderResultenum, and theverifyPurchaseWithProvideradditions toMutationResolver, handler typealias, andMutationHandlersall line up with the GraphQL schema and Kotlin types. This keeps the Swift-side API surface in sync with Android and the schema, and should make the new provider-based verification path straightforward to consume.Also applies to: 202-205, 592-595, 633-635, 1006-1082, 1447-1449, 1529-1530, 1553-1553, 1576-1577, 1599-1601
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (7)
211-243: LGTM! Well-structured enum additions.The new
IapkitEnvironmentandIapkitStoreenums follow the established pattern with proper JSON serialization and case-insensitive deserialization.
375-387: LGTM! Extensible provider enum.The
PurchaseVerificationProviderenum correctly handles case-insensitive deserialization and is structured to support additional providers in the future.
1580-1599: LGTM! Clean result type.The
RequestVerifyPurchaseWithIapkitResultdata class matches the TypeScript definition and follows the established serialization pattern.
1737-1739: LGTM! Consistent sealed interface pattern.The
VerifyPurchaseWithProviderResultsealed interface follows the same pattern as other result types in the file, enabling type-safe provider-specific results.
2223-2283: LGTM! Platform-specific verification props.Both
RequestVerifyPurchaseWithIapkitApplePropsandRequestVerifyPurchaseWithIapkitGooglePropsdata classes correctly handle their respective platform parameters with proper null safety and clear documentation.
2328-2345: LGTM! Complete provider verification integration.The
VerifyPurchaseWithProviderPropsdata class and corresponding mutation infrastructure are correctly implemented and follow the established patterns. The newverifyPurchaseWithProvidermutation is properly integrated into theMutationResolverinterface, typealias, andMutationHandlersdata class.Also applies to: 2534-2537, 2666-2666, 2689-2690
2285-2326: Validation is correctly implemented at runtime.The conditional requirements (apple required when store is Apple, google required when store is Google) are properly enforced in the
verifyPurchaseWithIapkitfunction (packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt). Whenprops.storeis Apple, the function throwsIllegalArgumentExceptionifprops.appleis null (line 96); similarly for Google store andprops.google(line 116). Tests confirm this validation works correctly.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/docs/src/pages/docs/types.tsx (1)
1733-1900: Align VerifyPurchase and IAPKit docs with generated types and backend behaviorThe new docs sections generally mirror the schema, but there are a couple of mismatches to fix:
- In
VerifyPurchaseResultAndroid,deferredSkushould be a string (SKU) and nullable, andcancelReasonis nullable in the generated models. The current doc signature (deferredSku: number | null; cancelReason: string;) doesn’t reflect that.- The
RequestVerifyPurchaseWithIapkitPropsinterface documents anendpoint?: stringfield, but the generated Kotlin/GraphQL types andverifyPurchaseWithIapkitimplementation don’t expose or use this property.To keep the docs authoritative, it’s worth updating these definitions to match the generated types (or extending the schema/implementation to support
endpointif that’s intentional).Also applies to: 2193-2260
🧹 Nitpick comments (7)
packages/docs/src/pages/docs/apis.tsx (2)
489-512: Nice renewalInfoIOS callout; consider extracting shared stylesThe new green callout and inline
<code>styling look good and readable. If we expect more of these “feature highlight” boxes, consider moving this style into a reusable class or small component to keep docs theming consistent and easier to tweak later.
624-695: verifyPurchaseWithProvider docs are clear; maybe clarify provider payload expectationsThe new
verifyPurchaseWithProvidersection reads well and matches the new provider types. Consider briefly stating whetheriapkit.apple/iapkit.googleare both optional and what minimum combination is valid (e.g., “at least one of apple or google must be provided”) so integrators know how to form partial requests.packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (1)
7-44: Provider tests exercise the store path correctly; consider aligning naming and cleanup.The happy‑path and empty‑result tests correctly validate that
OpenIapStore.verifyPurchaseWithProviderreturns the wirediapkitarray with the expected store/valid flags and that the empty provider result is surfaced as an empty array. TheFakeVerifyPurchaseModulestub is also sufficient for isolating the store behavior.Two small nits you may want to address:
testStoreReturnsNilWhenProviderResultIsNilnow assertsresult.count == 0whileverifyPurchaseWithProviderreturns a non‑optional array; renaming the test (and/or its description) to reflect “empty result” rather than “nil” would avoid confusion.- SwiftLint’s
redundant_void_returnwarnings on stub methods likerestorePurchases,finishTransaction, anddeepLinkToSubscriptionscan be silenced by dropping the explicit-> Voidreturn type and bare()bodies, if you care about a clean lint run in tests.Also applies to: 47-80, 88-139
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
39-42: Wrap verifyPurchaseWithIapkit store‑validation errors into OpenIapError for consistency.The Horizon implementation of
verifyPurchaseWithProvidercorrectly:
- restricts providers to
PurchaseVerificationProvider.Iapkit, and- requires
props.iapkit, forwarding toverifyPurchaseWithIapkitand returningVerifyPurchaseWithProviderResult.One behavioral gap vs the Apple side is that
verifyPurchaseWithIapkiton Android throws anIllegalArgumentExceptionwhenprops.storeis notIapkitStore.Google, whereas the Apple module maps equivalent misuse to a developer‑facing purchase error. To keep the public surface consistent and avoid leaking generic exceptions, consider mapping that case toOpenIapError.DeveloperErrorhere:- override val verifyPurchaseWithProvider: MutationVerifyPurchaseWithProviderHandler = { props -> - if (props.provider != PurchaseVerificationProvider.Iapkit) { - throw OpenIapError.FeatureNotSupported - } - val options = props.iapkit ?: throw OpenIapError.DeveloperError - VerifyPurchaseWithProviderResult( - iapkit = verifyPurchaseWithIapkit(options, TAG), - provider = props.provider - ) - } + override val verifyPurchaseWithProvider: MutationVerifyPurchaseWithProviderHandler = { props -> + if (props.provider != PurchaseVerificationProvider.Iapkit) { + throw OpenIapError.FeatureNotSupported + } + val options = props.iapkit ?: throw OpenIapError.DeveloperError + try { + VerifyPurchaseWithProviderResult( + iapkit = verifyPurchaseWithIapkit(options, TAG), + provider = props.provider + ) + } catch (e: IllegalArgumentException) { + // E.g. non‑Google store for Android IAPKit verification + throw OpenIapError.DeveloperError + } + }This keeps “bad props” scenarios in the same error family across Apple and Horizon while preserving the existing
OpenIapError.InvalidReceipt/OpenIapError.NetworkErrormapping from the validator.Also applies to: 660-669, 687-699
packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (1)
5-58: VerifyPurchase tests cover both “iOS result” and “Android result on Apple” behaviors well.The tests correctly assert that:
- an iOS
VerifyPurchaseResultIOSflows throughOpenIapStore.verifyPurchase(sku:)unchanged, and- an Android variant triggers
.featureNotSupported, matching the store’s platform guard.
FakeOpenIapModule’s use of the new VerifyPurchase types (includingVerifyPurchaseWithProviderResult) keeps it aligned with the production protocol.If you find yourself adding more tests around provider flows, you might consider reusing this fake in
VerifyPurchaseWithProviderTests(or extracting a shared helper) to avoid duplicating stub implementations, but that’s purely for test maintenance.Also applies to: 62-120
packages/apple/Sources/OpenIapModule.swift (1)
553-607: VerifyPurchase and IAPKit provider flow look solid; a couple of optional refinements to consider.The new iOS verification path is well‑structured:
performVerifyPurchaseIOScomputesVerifyPurchaseResultIOSfromstoreProduct(for:),product.latestTransaction, and the local receipt; callers get a single “isValid + enriched context” object instead of multiple APIs.validateReceiptIOS,validateReceipt, andverifyPurchasenow consistently front this logic viaVerifyPurchaseProps/VerifyPurchaseResult, which matches the generated models and store API.verifyPurchaseWithProvider/verifyPurchaseWithIapkitenforceprovider == .iapkit, require appropriate Apple payload, and map HTTP/network/JSON failures intoPurchaseErrorcodes with useful logging.Two design choices you may want to confirm or evolve over time:
performVerifyPurchaseIOScurrently swallows all underlying errors (missing SKU, verification failure, etc.) and reportsisValid = falsewith empty JWS/transaction. If some callers need to distinguish “invalid receipt” from “couldn’t verify due to a service/config issue”, you might later introduce an additional flag or error code in the result instead of treating everything as “not valid”.- The IAPKit endpoint is hard‑coded to
https://iapkit.com/purchase/verify. If you expect staged environments or want easier unit testing of the HTTP client, threading this URL (or a base URL) via configuration/props would make the behavior more flexible without changing the public signature.If the current semantics (best‑effort boolean validity and fixed production endpoint) are deliberate for v1, the implementation itself looks correct.
Also applies to: 608-718
packages/apple/Sources/Models/Types.swift (1)
153-162: Generated IAPKit/provider models align with schema; lint warnings are purely stylistic.The newly generated enums (
IapkitEnvironment,IapkitStore,PurchaseVerificationProvider), result structs (VerifyPurchaseResult*,VerifyPurchaseWithProviderResult,RequestVerifyPurchaseWithIapkitResult), input props (RequestVerifyPurchaseWithIapkit*Props,VerifyPurchaseProps,VerifyPurchaseWithProviderProps,VerifyPurchaseAndroidOptions), and theVerifyPurchaseResultunion all match the GraphQL schema and the Kotlin/TypeScript models referenced elsewhere in the PR. They also line up with how the Apple and Horizon modules consume these types.The SwiftLint warnings here (redundant string raw values, long type names like
RequestVerifyPurchaseWithIapkitApplePropsandMutationVerifyPurchaseWithProviderHandler) are expected in an auto‑generated file. If they become noisy, it’s better to adjust the generator or lint configuration (e.g., exclude this file or relax those rules for generated code) rather than hand‑editing the output.Also applies to: 202-205, 560-637, 974-1080, 1379-1382, 1525-1552, 1621-1681
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
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.tsis excluded by!**/generated/**
📒 Files selected for processing (24)
packages/apple/CONTRIBUTING.md(1 hunks)packages/apple/Sources/Models/OpenIapSerialization.swift(1 hunks)packages/apple/Sources/Models/Types.swift(13 hunks)packages/apple/Sources/OpenIapModule.swift(3 hunks)packages/apple/Sources/OpenIapProtocol.swift(1 hunks)packages/apple/Sources/OpenIapStore.swift(2 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift(5 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift(1 hunks)packages/docs/src/components/SearchModal.tsx(2 hunks)packages/docs/src/pages/docs/apis.tsx(5 hunks)packages/docs/src/pages/docs/types.tsx(5 hunks)packages/google/CONTRIBUTING.md(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt(1 hunks)packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt(3 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(11 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt(4 hunks)packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt(5 hunks)packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt(4 hunks)packages/gql/CONVENTION.md(1 hunks)packages/gql/src/api-ios.graphql(1 hunks)packages/gql/src/api.graphql(1 hunks)packages/gql/src/type-android.graphql(1 hunks)packages/gql/src/type-ios.graphql(1 hunks)packages/gql/src/type.graphql(2 hunks)
✅ Files skipped from review due to trivial changes (3)
- packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
- packages/gql/CONVENTION.md
- packages/docs/src/components/SearchModal.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/gql/src/api.graphql
- packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/apple/CONTRIBUTING.mdpackages/gql/src/type-android.graphqlpackages/docs/src/pages/docs/types.tsxpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/CONTRIBUTING.mdpackages/apple/Sources/OpenIapModule.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swiftpackages/gql/src/type.graphqlpackages/apple/Sources/Models/Types.swiftpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
🧬 Code graph analysis (11)
packages/apple/Sources/Models/OpenIapSerialization.swift (1)
packages/gql/src/generated/types.ts (1)
VerifyPurchaseProps(842-847)
packages/apple/Sources/OpenIapStore.swift (6)
packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (3)
validateReceipt(109-111)verifyPurchase(113-115)verifyPurchaseWithProvider(117-119)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (3)
validateReceipt(125-127)verifyPurchase(129-131)verifyPurchaseWithProvider(133-135)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (5)
validateReceipt(2543-2543)verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)iapkit(1737-1759)iapkit(2342-2359)packages/gql/src/generated/Types.kt (5)
validateReceipt(2610-2610)verifyPurchase(2614-2614)verifyPurchaseWithProvider(2618-2618)iapkit(1804-1826)iapkit(2409-2426)packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/gql/src/generated/types.ts (1)
VerifyPurchaseProps(842-847)
packages/apple/Sources/OpenIapProtocol.swift (4)
packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (4)
validateReceiptIOS(102-107)validateReceipt(109-111)verifyPurchase(113-115)verifyPurchaseWithProvider(117-119)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (4)
validateReceiptIOS(118-123)validateReceipt(125-127)verifyPurchase(129-131)verifyPurchaseWithProvider(133-135)packages/apple/Sources/OpenIapModule.swift (4)
deprecated(553-556)deprecated(584-587)verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/apple/Sources/OpenIapStore.swift (3)
deprecated(371-374)verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (2)
packages/gql/src/generated/types.ts (1)
VerifyPurchaseWithProviderResult(888-892)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(86-143)
packages/apple/Sources/OpenIapModule.swift (5)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (29)
verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)iapkit(1737-1759)iapkit(2342-2359)code(1200-1222)message(1362-1381)rawValue(15-44)rawValue(46-157)rawValue(162-185)rawValue(187-209)rawValue(211-226)rawValue(228-243)rawValue(245-261)rawValue(263-284)rawValue(286-304)rawValue(306-321)rawValue(323-344)rawValue(346-373)rawValue(375-387)rawValue(389-404)rawValue(406-430)store(1472-1491)request(2007-2058)apiKey(2255-2290)error(706-731)error(736-761)appId(541-593)appId(2193-2222)packageName(2224-2253)packages/gql/src/generated/Types.kt (15)
verifyPurchase(2614-2614)verifyPurchaseWithProvider(2618-2618)iapkit(1804-1826)iapkit(2409-2426)code(1267-1289)message(1429-1448)rawValue(14-43)store(1539-1558)request(2074-2125)apiKey(2322-2357)error(773-798)error(803-828)appId(608-660)appId(2260-2289)packageName(2291-2320)packages/gql/src/generated/types.ts (3)
VerifyPurchaseResultIOS(872-881)VerifyPurchaseWithProviderResult(888-892)RequestVerifyPurchaseWithIapkitResult(773-776)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(86-143)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (1)
setRequestProperty(256-260)
packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (6)
packages/gql/src/generated/types.ts (4)
VerifyPurchaseResultIOS(872-881)VerifyPurchaseResultAndroid(851-870)VerifyPurchaseWithProviderResult(888-892)PurchaseError(481-485)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (9)
isValid(1698-1735)iapkit(1737-1759)iapkit(2342-2359)validateReceiptIOS(2629-2629)code(1200-1222)message(1362-1381)validateReceipt(2543-2543)verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)packages/gql/src/generated/Types.kt (9)
isValid(1765-1802)iapkit(1804-1826)iapkit(2409-2426)validateReceiptIOS(2696-2696)code(1267-1289)message(1429-1448)validateReceipt(2610-2610)verifyPurchase(2614-2614)verifyPurchaseWithProvider(2618-2618)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (4)
validateReceiptIOS(118-123)validateReceipt(125-127)verifyPurchase(129-131)verifyPurchaseWithProvider(133-135)packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
packages/apple/Sources/Models/Types.swift (5)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (15)
iapkit(1737-1759)iapkit(2342-2359)store(1472-1491)autoRenewing(1629-1696)isValid(1698-1735)appId(541-593)appId(2193-2222)packageName(2224-2253)apiKey(2255-2290)accessToken(2292-2315)androidOptions(2317-2340)validateReceipt(2543-2543)verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)validateReceiptIOS(2629-2629)packages/gql/src/generated/Types.kt (15)
iapkit(1804-1826)iapkit(2409-2426)store(1539-1558)autoRenewing(1696-1763)isValid(1765-1802)appId(608-660)appId(2260-2289)packageName(2291-2320)apiKey(2322-2357)accessToken(2359-2382)androidOptions(2384-2407)validateReceipt(2610-2610)verifyPurchase(2614-2614)verifyPurchaseWithProvider(2618-2618)validateReceiptIOS(2696-2696)packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (4)
validateReceipt(109-111)verifyPurchase(113-115)verifyPurchaseWithProvider(117-119)validateReceiptIOS(102-107)packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (3)
packages/gql/src/generated/types.ts (1)
VerifyPurchaseWithProviderResult(888-892)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-663)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(86-143)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (10)
RequestVerifyPurchaseWithIapkitResult(773-776)VerifyPurchaseResultAndroid(851-870)VerifyPurchaseResultIOS(872-881)VerifyPurchaseWithProviderResult(888-892)RequestVerifyPurchaseWithIapkitAppleProps(744-751)RequestVerifyPurchaseWithIapkitGoogleProps(753-760)RequestVerifyPurchaseWithIapkitProps(762-771)VerifyPurchaseAndroidOptions(835-840)VerifyPurchaseProps(842-847)VerifyPurchaseWithProviderProps(883-886)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (3)
packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchaseWithProvider(594-606)verifyPurchase(589-592)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchaseWithProvider(388-391)verifyPurchase(376-386)packages/gql/src/generated/types.ts (5)
RequestVerifyPurchaseWithIapkitResult(773-776)VerifyPurchaseWithProviderResult(888-892)VerifyPurchaseWithProviderProps(883-886)IapkitStore(177-177)PurchaseError(481-485)
🪛 SwiftLint (0.57.0)
packages/apple/Sources/Models/Types.swift
[Warning] 154-154: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 155-155: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 159-159: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 160-160: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 203-203: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 974-974: Type name 'RequestVerifyPurchaseWithIapkitAppleProps' should be between 3 and 40 characters long
(type_name)
[Warning] 993-993: Type name 'RequestVerifyPurchaseWithIapkitGoogleProps' should be between 3 and 40 characters long
(type_name)
[Warning] 1527-1527: Type name 'MutationVerifyPurchaseWithProviderHandler' should be between 3 and 40 characters long
(type_name)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift
[Warning] 104-104: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 108-108: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 154-154: Returning Void in a function declaration is redundant
(redundant_void_return)
⏰ 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 (12)
packages/google/CONTRIBUTING.md (1)
106-106: Now I need to check how the public API exports these types with the OpenIap prefix:Based on the verification, I found that the codebase contains
VerifyPurchaseResultAndroidandVerifyPurchaseResultIOS, which are platform-specific type implementations of the sealed interfaceVerifyPurchaseResult. However, the CONTRIBUTING.md documentation showsOpenIapVerifyPurchaseResultwithout platform suffixes as the example type.The discrepancy between the documented type name and the actual platform-specific implementations in the codebase cannot be fully resolved without inspecting the public API export layer (which may apply the
OpenIapprefix via type aliases or public interfaces not visible in the current search).Verify that the VerifyPurchase types in the public API match the documented examples.
Specifically, confirm whether:
- The public API exposes
OpenIapVerifyPurchaseResultas a generic/sealed interface with platform-specific variants (OpenIapVerifyPurchaseResultAndroid,OpenIapVerifyPurchaseResultIOS), or- If the documentation should be updated to reference the actual type names and platform-specific variants that users will import.
packages/gql/src/type-android.graphql (1)
156-182: VerifyPurchase Android GraphQL types look consistent and well‑shaped
VerifyPurchaseAndroidOptionsandVerifyPurchaseResultAndroidline up with the Kotlin/TS generated models and the Google Play validation flow (packageName + productToken + sku + isSub), so the schema surface here looks correct and coherent with the rest of the PR.packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
36-39: Provider-based verifyPurchase wiring is consistent and safe
verifyPurchaseWithProvidercorrectly enforcesprovider == PurchaseVerificationProvider.Iapkit, validates thatiapkitoptions are present, delegates toverifyPurchaseWithIapkit, and exposes the result viamutationHandlers. This cleanly layers the new provider flow alongside the existingverifyPurchasewithout changing old behavior.Also applies to: 66-66, 811-820, 849-851
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
211-244: Generated verify-purchase / provider types and handlers are internally consistentThe new IAPKit enums, verify‑purchase result models, props (including
RequestVerifyPurchaseWithIapkit*,VerifyPurchaseProps,VerifyPurchaseWithProviderProps), theVerifyPurchaseResultsealed interface, and the matching mutation/query handler aliases all line up with the GraphQL/TS surfaces and with the module wiring in this PR. The fromJson/toJson implementations are mechanically correct and preserve nullability, so the generated layer looks solid.Also applies to: 375-387, 1472-1491, 1629-1760, 2193-2359, 2429-2441, 2678-2681, 2703-2705, 2726-2727
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
29-84: Google Play validation refactor to VerifyPurchaseProps looks correctSwitching
validateReceiptWithGooglePlaytoVerifyPurchasePropsandVerifyPurchaseResultAndroidpreserves the previous behavior: required Android options are validated up front, the URL path matches the products/subscriptions split, and errors are cleanly mapped toOpenIapError.InvalidReceipt/OpenIapError.NetworkError. The Gson parse intoVerifyPurchaseResultAndroidis straightforward and consistent with the new model.Also applies to: 71-73
packages/docs/src/pages/docs/apis.tsx (2)
578-605: verifyPurchase docs correctly aligned with new typesThe “Verify Purchase” section now matches the unified API (
VerifyPurchaseProps/VerifyPurchaseResult) and clearly explains the deprecation path fromvalidateReceipt/validateReceiptIOS. This looks consistent with the GraphQL schema changes and is safe to ship.
1109-1110: validateReceiptIOS snippet matches new VerifyPurchase surfaceThe updated iOS snippet (
VerifyPurchasePropsin /VerifyPurchaseResultIOSout) lines up with the schema and Swift APIs while keeping the field deprecated in favor ofverifyPurchase. No issues here.packages/gql/src/type-ios.graphql (1)
226-243: VerifyPurchaseResultIOS rename looks consistent and non‑disruptiveRenaming the result type to
VerifyPurchaseResultIOSwhile keeping the field shapes the same preserves behavior and matches the new VerifyPurchase naming across the schema.latestTransaction: Purchasealso stays compatible with the existing Purchase union.packages/gql/src/api-ios.graphql (1)
64-69: validateReceiptIOS GraphQL signature correctly migrated to VerifyPurchase typesThe field now uses
VerifyPurchasePropsandVerifyPurchaseResultIOSwhile remaining deprecated in favor ofverifyPurchase, which aligns cleanly with the rest of the schema and docs. Looks good.packages/apple/Sources/Models/OpenIapSerialization.swift (1)
116-118: verifyPurchaseProps helper is consistent with existing decoding utilitiesThis helper cleanly mirrors
purchaseOptions(from:)and other decode wrappers, routing through the shareddecode(object:as:)path. It keeps the serialization surface in sync with the newVerifyPurchasePropstype without introducing new failure modes beyond the existing JSONSerialization checks.packages/apple/Sources/OpenIapStore.swift (1)
371-391: New verifyPurchase/validateReceipt bridging and provider API look consistent.
validateReceipt(sku:)cleanly forwards intoverifyPurchase(sku:)while remaining deprecated, andverifyPurchase(sku:)correctly constrains the union to the iOS result, throwing.featureNotSupportedotherwise. The newverifyPurchaseWithProvider(_:)thinly delegates to the module and exposes the IAPKit result list, which matches the module and model types.packages/gql/src/type.graphql (1)
33-47: Schema for VerifyPurchase and provider‑based verification matches native models.The new enums and input/output types for
VerifyPurchaseProps,VerifyPurchaseResult, andVerifyPurchaseWithProvider*line up with the generated Swift/Kotlin models and the platform implementations (Apple’s and Horizon’s modules enforce the per‑store requirements). I don’t see schema‑level issues; this looks ready for codegen.Also applies to: 206-287
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/apple/Sources/Models/ReceiptValidationCompat.swift (1)
1-6: LGTM! Consider adding deprecation warnings.The compatibility layer is well-structured and provides a clean migration path. To guide developers toward the new API names, consider marking these aliases as deprecated:
// Compatibility aliases for legacy receipt validation APIs // These map old ReceiptValidation* names to the new VerifyPurchase* types. +@available(*, deprecated, renamed: "VerifyPurchaseProps") public typealias ReceiptValidationProps = VerifyPurchaseProps +@available(*, deprecated, renamed: "VerifyPurchaseResult") public typealias ReceiptValidationResult = VerifyPurchaseResult +@available(*, deprecated, renamed: "VerifyPurchaseResultIOS") public typealias ReceiptValidationResultIOS = VerifyPurchaseResultIOSThis will display compiler warnings at call sites, helping developers migrate to the new names while maintaining backward compatibility.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/apple/Sources/Models/ReceiptValidationCompat.swift(1 hunks)packages/docs/src/pages/docs/types.tsx(5 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.kt(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.ktpackages/docs/src/pages/docs/types.tsx
⏰ 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/docs/src/pages/docs/types.tsx (1)
1733-1899: LGTM! Consistent rename across the documentation.The renaming from
ReceiptValidation*toVerifyPurchase*is applied consistently across section headers, interface names, and union types. This aligns with the PR objective to refactor receipt validation constructs.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/docs/src/pages/docs/types.tsx (1)
2210-2238: Critical: The documentedendpointfield is still missing from the interface.This is a duplicate of a previous review comment. The
RequestVerifyPurchaseWithIapkitPropsinterface requires anendpointfield to specify the absolute URL to the IAPKit/purchase/verifyendpoint. Without this field, the HTTP client cannot make verification requests.Apply this diff to add the missing
endpointfield:interface RequestVerifyPurchaseWithIapkitProps { + endpoint: string; // Absolute URL to the IAPKit /purchase/verify endpoint apiKey?: string; // sent as Authorization: Bearer {apiKey} store?: IapkitStore; // defaults to 'apple' on Apple, 'google' on Android apple?: {And update the field documentation:
<ul> <li> + <code>endpoint</code> — Absolute URL to the IAPKit /purchase/verify endpoint + </li> + <li> <code>apiKey</code> — Optional bearer token for the Authorization header. </li>
🧹 Nitpick comments (1)
packages/docs/src/pages/docs/types.tsx (1)
2240-2255: Consider adding usage examples for the new provider-based verification.The result types are well-defined, but the documentation would benefit from practical usage examples showing how to call
verifyPurchaseWithProviderwith IAPKit.Consider adding a usage example like:
// Example: Verify purchase with IAPKit const result = await verifyPurchaseWithProvider({ provider: 'iapkit', props: { endpoint: 'https://api.iapkit.com/purchase/verify', apiKey: 'your-api-key', store: 'apple', apple: { receipt: purchaseToken, environment: 'production', appId: 'your.app.id' } } }); if (result.iapkit[0].valid) { console.log('Purchase verified successfully'); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/docs/src/pages/docs/types.tsx(5 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.kt(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.ktpackages/docs/src/pages/docs/types.tsx
🧬 Code graph analysis (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.kt (1)
packages/google/openiap/src/play/java/dev/hyo/openiap/store/OpenIapStoreExtensions.kt (1)
ReplaceWith(33-39)
⏰ 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/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.kt (1)
7-23: LGTM! Previous feedback addressed.The deprecation annotations have been properly implemented for all three type aliases. Each annotation includes:
- A clear message directing users to the new type name
- A
ReplaceWithexpression with the correct import path for IDE-assisted migrationThis provides excellent backward compatibility while encouraging migration to the new
VerifyPurchase*naming.packages/docs/src/pages/docs/types.tsx (3)
1733-1774: LGTM! Clean rename from ReceiptValidation to VerifyPurchase.The section header and type naming are consistent with the PR objectives. The interface structure and field documentation are clear and complete.
1776-1900: LGTM! Comprehensive result type documentation.The VerifyPurchaseResult types are well-documented for both platforms, with clear field references and proper union type definition.
2194-2209: LGTM! Clear introduction of provider-based verification.The new Purchase Verification Providers section is well-structured with clear type definitions for IapkitStore and IapkitEnvironment.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/apple/CONTRIBUTING.md(1 hunks)packages/apple/Sources/Models/ReceiptValidationCompat.swift(1 hunks)packages/apple/Sources/OpenIapProtocol.swift(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/apple/Sources/Models/ReceiptValidationCompat.swift
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.835Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/apple/CONTRIBUTING.mdpackages/apple/Sources/OpenIapProtocol.swift
📚 Learning: 2025-11-22T19:30:03.835Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.835Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Applied to files:
packages/apple/Sources/OpenIapProtocol.swift
🧬 Code graph analysis (1)
packages/apple/Sources/OpenIapProtocol.swift (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (7)
validateReceiptIOS(2629-2629)message(1362-1381)validateReceipt(2543-2543)verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)code(1200-1222)androidOptions(2317-2340)packages/apple/Sources/OpenIapStore.swift (3)
deprecated(371-374)verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)packages/apple/Sources/OpenIapModule.swift (4)
deprecated(553-556)deprecated(584-587)verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)
⏰ 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 (2)
packages/apple/CONTRIBUTING.md (1)
45-45: Changes successfully resolve previous critical issue.The example type list on line 45 now correctly reflects the actual type names in the codebase, addressing all six inaccuracies flagged in the previous review. The correction includes:
- Removed the six incorrect type names (e.g.,
ProductIOSRequest,PurchaseIOSOffer,ProductIOSTypeIOS)- Added the correct counterparts (e.g.,
ProductRequest,PurchaseOfferIOS,ProductTypeIOS)- Introduced the new
VerifyPurchase*types (VerifyPurchaseProps,VerifyPurchaseResultIOS) reflecting the PR's refactoring fromReceiptValidation*to a unified verification APIThe examples now align with both the actual codebase definitions and the naming conventions outlined earlier in the document.
packages/apple/Sources/OpenIapProtocol.swift (1)
87-99: No issues found—ReceiptValidationProps is properly defined and accessible.Verification confirms that
ReceiptValidationPropsis a public typealias inpackages/apple/Sources/Models/ReceiptValidationCompat.swift(line 5) mapping toVerifyPurchaseProps. Since bothOpenIapProtocol.swiftandReceiptValidationCompat.swiftare in the same SPM target ("OpenIAP"), the type is automatically accessible without an explicit import. The referenced fields (androidOptionsandsku) exist inVerifyPurchaseProps, so the backward compatibility bridges are correctly implemented.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
162-167: Consider usingmapOfinstead ofmutableMapOffor immutable payload.The payload map is never mutated after construction, so
mapOfbetter expresses the intent and avoids unnecessary mutability.- mutableMapOf<String, Any?>( + mapOf<String, Any?>( "store" to store.toJson(), "packageName" to google.packageName, "purchaseId" to google.purchaseId, "purchaseToken" to google.purchaseToken )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/apple/Sources/OpenIapModule.swift(3 hunks)packages/apple/Sources/OpenIapProtocol.swift(2 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt(4 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.835Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/apple/Sources/OpenIapModule.swift
📚 Learning: 2025-11-22T19:30:03.835Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.835Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/apple/Sources/OpenIapProtocol.swiftpackages/apple/Sources/OpenIapModule.swift
🧬 Code graph analysis (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (1)
setRequestProperty(256-260)
packages/apple/Sources/OpenIapProtocol.swift (4)
packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (4)
validateReceiptIOS(102-107)validateReceipt(109-111)verifyPurchase(113-115)verifyPurchaseWithProvider(117-119)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (6)
validateReceiptIOS(2629-2629)message(1362-1381)validateReceipt(2543-2543)verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)code(1200-1222)packages/apple/Sources/OpenIapModule.swift (4)
deprecated(553-556)deprecated(584-587)verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/apple/Sources/OpenIapStore.swift (3)
deprecated(371-374)verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
packages/apple/Sources/OpenIapModule.swift (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (27)
verifyPurchase(2547-2547)verifyPurchaseWithProvider(2551-2551)iapkit(1737-1759)iapkit(2342-2359)code(1200-1222)message(1362-1381)rawValue(15-44)rawValue(46-157)rawValue(162-185)rawValue(187-209)rawValue(211-226)rawValue(228-243)rawValue(245-261)rawValue(263-284)rawValue(286-304)rawValue(306-321)rawValue(323-344)rawValue(346-373)rawValue(375-387)rawValue(389-404)rawValue(406-430)store(1472-1491)request(2007-2058)apiKey(2255-2290)appId(541-593)appId(2193-2222)packageName(2224-2253)packages/gql/src/generated/Types.kt (13)
verifyPurchase(2614-2614)verifyPurchaseWithProvider(2618-2618)iapkit(1804-1826)iapkit(2409-2426)code(1267-1289)message(1429-1448)rawValue(14-43)store(1539-1558)request(2074-2125)apiKey(2322-2357)appId(608-660)appId(2260-2289)packageName(2291-2320)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
⏰ 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 (6)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (4)
5-13: LGTM! Imports and constants align with IAPKit verification feature.The new imports support TypeToken-based JSON parsing and IAPKit types. The hardcoded
DEFAULT_IAPKIT_ENDPOINTis correct per learnings—the SDK intentionally routes to IAPKit internally without requiring users to provide an endpoint field.Based on learnings, the endpoint is handled internally by design.
Also applies to: 22-22
29-84: LGTM! Function signature correctly updated for unified VerifyPurchase API.The migration from
ReceiptValidationProps/ReceiptValidationResultAndroidtoVerifyPurchaseProps/VerifyPurchaseResultAndroidmaintains all validation logic and error handling while aligning with the new unified API surface.
86-143: LGTM! IAPKit verification implementation is sound and forward-compatible.The function correctly:
- Restricts Android to
IapkitStore.Googlewith clear validation (lines 95-97)- Uses a list structure that supports future multi-store expansion while currently handling single-store Android requirements
- Validates HTTP responses and maps failures to appropriate
OpenIapErrortypes- Employs TypeToken-based parsing for generic type safety (line 129)
- Handles resources properly with try-finally cleanup
The hardcoded endpoint aligns with the learning that the SDK internally routes to IAPKit without requiring user-provided endpoints.
Based on learnings, endpoint is intentionally hardcoded.
151-161: LGTM! Comprehensive field validation with clear error messages.The validation ensures all required Google payload fields are present and non-blank before constructing the request, preventing malformed API calls.
packages/apple/Sources/OpenIapProtocol.swift (1)
43-49: New VerifyPurchase-based protocol signatures look consistent with module/store usageSwitching the validation/verification methods to
VerifyPurchaseProps/VerifyPurchaseResult*here matches the concrete implementations inOpenIapModuleand theOpenIapStorefaçade, and cleanly aligns the protocol surface with the new VerifyPurchase API. I don’t see issues with these signatures from the protocol side.packages/apple/Sources/OpenIapModule.swift (1)
553-592: Unified VerifyPurchase pipeline on iOS looks correctThe refactor to:
- Have
validateReceiptIOS(_:)delegate toperformVerifyPurchaseIOS(_:),- Have
validateReceipt(_:)delegate toverifyPurchase(_:), and- Wrap
VerifyPurchaseResultIOSinto theVerifyPurchaseResultenum inverifyPurchase(_:)gives a single, consistent verification path on Apple while preserving the legacy entry points via deprecation. The internal
performVerifyPurchaseIOSlogic (latest verified transaction +getReceiptDataIOS()) is straightforward and matches expected StoreKit2 behavior.
8aef7b2 to
fbc9af4
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/docs/src/components/SearchModal.tsx (1)
130-138: Fix incorrect type references in the verify-purchase entry—the referenced types do not exist.The
parametersandreturnsfields referenceReceiptValidationProps!andReceiptValidationResult!, but these types do not exist in the codebase. The correct types areVerifyPurchasePropsandVerifyPurchaseResult, which are defined inpackages/gql/src/generated/types.ts.Update lines 134–135:
parameters: 'VerifyPurchaseProps!', returns: 'VerifyPurchaseResult!',packages/docs/src/pages/docs/features/external-purchase.tsx (1)
1276-1280: Update User Choice flow table to match the currentrequestPurchaseAPIIn the “User Choice Billing” implementation‑flow table, Step 2 currently shows:
<code>requestPurchase(useAlternativeBilling: true)</code>The surrounding TS/Kotlin examples now configure User Choice via
alternativeBillingModeAndroidoninitConnection, and callrequestPurchase(...)without auseAlternativeBillingparameter. To avoid confusion, consider updating this row to reflect the current API, for example:- <code>requestPurchase(useAlternativeBilling: true)</code> + <code>requestPurchase(...)</code>and clarify in the description that the User Choice dialog is enabled by initializing with
alternativeBillingModeAndroid: 'userChoice'(TS) or the corresponding Kotlin config.
🧹 Nitpick comments (8)
packages/docs/src/styles/components.css (1)
309-339: Consider adding word-break handling for alert card content.Alert card content (paragraphs, lists) may contain long URLs or identifiers that overflow narrow viewports. Per coding guidelines, apply
word-break: break-wordorword-wrap: break-wordto prevent overflow.Apply this diff:
.alert-card p { margin: 0; + word-break: break-word; } .alert-card ul, .alert-card ol { margin-top: 0.5rem; margin-bottom: 0; + word-break: break-word; }packages/docs/src/components/AnchorLink.tsx (1)
14-31: Consider replacing non‑standard'instant'with a standard behavior (and/or shared helper)
scrollIntoView({ behavior: 'instant' })uses a non‑standard value; DOM types usually only allow'auto' | 'smooth', so this can cause TS type errors and relies on browsers ignoring the unknown value. You might want to:
- Switch to the standard
'auto'for “jump immediately” behavior, and- (Optionally) reuse the same “scroll with header offset” logic you already have in
useScrollToHashso anchor clicks don’t get hidden under the fixed header.For example:
- element.scrollIntoView({ behavior: 'instant', block: 'start' }); + element.scrollIntoView({ behavior: 'auto', block: 'start' });Apply similarly in the click handler.
packages/docs/src/pages/docs/lifecycle/index.tsx (1)
205-210: Align “validate purchase” wording with the newverifyPurchasenamingIn the “Always Handle Pending Purchases” CodeBlock, the step currently says:
→ validate purchaseTo stay consistent with the new
verifyPurchaseterminology you just introduced on this page, consider updating it to something like:- → validate purchase + → verify purchase (server-side).claude/guides/04-apple-package.md (1)
35-46: Consider simplifying "API interface" to "API".The phrase "API interface" is redundant since "I" in API stands for "interface". This is a minor style suggestion.
-Defines the public API interface: +Defines the public API:packages/docs/src/pages/docs/lifecycle/subscription.tsx (1)
36-139: Inline styles could be extracted to CSS classes for maintainability.The extensive inline styles throughout the table and other elements work correctly but could be extracted to reusable CSS classes in a stylesheet. This would improve maintainability and reduce component verbosity. However, this is consistent with the existing documentation page patterns.
packages/docs/src/pages/docs.tsx (1)
80-87: Life Cycle dropdown won’t auto-expand on/docs/lifecycle/subscription
MenuDropdowncurrently treats the title as active only whenlocation.pathname === titleTo. WithtitleTo="/docs/lifecycle"and a child route at/docs/lifecycle/subscription, the Life Cycle title won’t be marked active or auto-expanded when viewing the subscription lifecycle page.Consider updating
MenuDropdown(or adding an option) to treat child paths as active (e.g.,pathname.startsWith(titleTo)) so this new subpage keeps the Life Cycle section open.packages/gql/src/type.graphql (1)
205-287: VerifyPurchase and IAPKit provider types look coherent; rely on resolver validation*The new
VerifyPurchaseProps/VerifyPurchaseResultandRequestVerifyPurchaseWithIapkit*/VerifyPurchaseWithProvider*types form a consistent surface for both direct verifyPurchase calls and provider-based (IAPKit) verification. Removing theendpointfromRequestVerifyPurchaseWithIapkitPropsmatches the SDK behavior where the IAPKit base URL is handled internally.One thing to keep in mind: the schema allows
VerifyPurchaseWithProviderProps.iapkitto be null even whenproviderisIapkit. That’s fine if resolvers enforce the invariant and return a clear error, but if you want stronger guarantees you could tighten it later (e.g., additional docs or non-null when new providers are introduced).packages/apple/Sources/Models/Types.swift (1)
153-161: Generated Swift types correctly mirror the new verification schemaThe additions for provider-based verification and subscription expiry look consistent and well-integrated:
IapkitEnvironment,IapkitStore, andPurchaseVerificationProviderenums match the GraphQL enums and respect the Swift acronym/casing rules.RequestVerifyPurchaseWithIapkit*,VerifyPurchaseAndroidOptions,VerifyPurchaseProps,VerifyPurchaseResult*,VerifyPurchaseWithProviderProps, andVerifyPurchaseWithProviderResultline up with the new GraphQL inputs/unions/results.ActiveSubscription.willExpireSoonis added as an optional with clear deprecation-style comments, matching the GraphQL deprecation metadata.MutationResolver/QueryResolversignatures and the corresponding handler typealiases/handler fields are updated to use the new VerifyPurchase shapes, so consumers can implement the new flows without breaking existing patterns.Given this file is auto-generated, any SwiftLint warnings about redundant string raw values or long type names are better addressed by excluding generated sources or adjusting the generator/config, rather than editing this file directly.
Also applies to: 202-205, 254-279, 563-566, 604-640, 977-1083, 1382-1385, 1445-1490, 1528-1531, 1624-1684
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (5)
bun.lockis excluded by!**/*.lockpackages/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.tsis excluded by!**/generated/**
📒 Files selected for processing (55)
.claude/guides/01-overview.md(1 hunks).claude/guides/02-api-naming.md(1 hunks).claude/guides/03-deprecations.md(1 hunks).claude/guides/04-apple-package.md(1 hunks).claude/guides/05-google-package.md(1 hunks).claude/guides/06-gql-package.md(1 hunks).claude/guides/07-docs-package.md(1 hunks).claude/guides/08-deployment.md(1 hunks).gitignore(1 hunks)CLAUDE.md(4 hunks)openiap-versions.json(1 hunks)packages/apple/Sources/Models/Types.swift(14 hunks)packages/docs/openiap-versions.json(0 hunks)packages/docs/openiap-versions.json(1 hunks)packages/docs/package.json(1 hunks)packages/docs/public/sitemap.xml(1 hunks)packages/docs/src/components/AnchorLink.tsx(2 hunks)packages/docs/src/components/CodeBlock.tsx(4 hunks)packages/docs/src/components/HighlightText.tsx(1 hunks)packages/docs/src/components/LanguageTabs.tsx(1 hunks)packages/docs/src/components/PlatformTabs.tsx(2 hunks)packages/docs/src/components/SEO.tsx(1 hunks)packages/docs/src/components/SearchModal.tsx(3 hunks)packages/docs/src/hooks/useScrollToHash.ts(2 hunks)packages/docs/src/main.tsx(1 hunks)packages/docs/src/pages/docs.tsx(8 hunks)packages/docs/src/pages/docs/android-setup.tsx(1 hunks)packages/docs/src/pages/docs/apis.tsx(47 hunks)packages/docs/src/pages/docs/ecosystem.tsx(2 hunks)packages/docs/src/pages/docs/errors.tsx(5 hunks)packages/docs/src/pages/docs/events.tsx(7 hunks)packages/docs/src/pages/docs/features.tsx(0 hunks)packages/docs/src/pages/docs/features/external-purchase.tsx(15 hunks)packages/docs/src/pages/docs/features/offer-code-redemption.tsx(1 hunks)packages/docs/src/pages/docs/features/purchase.tsx(1 hunks)packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx(22 hunks)packages/docs/src/pages/docs/features/subscription.tsx(1 hunks)packages/docs/src/pages/docs/horizon-setup.tsx(1 hunks)packages/docs/src/pages/docs/ios-setup.tsx(3 hunks)packages/docs/src/pages/docs/lifecycle/index.tsx(3 hunks)packages/docs/src/pages/docs/lifecycle/subscription.tsx(1 hunks)packages/docs/src/pages/docs/updates/announcements.tsx(1 hunks)packages/docs/src/pages/docs/updates/notes.tsx(3 hunks)packages/docs/src/pages/docs/updates/versions.tsx(2 hunks)packages/docs/src/pages/home.tsx(2 hunks)packages/docs/src/pages/introduction.tsx(1 hunks)packages/docs/src/pages/languages.tsx(1 hunks)packages/docs/src/pages/sponsors.tsx(1 hunks)packages/docs/src/pages/tutorials.tsx(5 hunks)packages/docs/src/styles/base.css(0 hunks)packages/docs/src/styles/components.css(2 hunks)packages/docs/src/styles/dark-mode.css(1 hunks)packages/docs/src/styles/variables.css(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(12 hunks)packages/gql/src/type.graphql(3 hunks)
💤 Files with no reviewable changes (2)
- packages/docs/src/styles/base.css
- packages/docs/src/pages/docs/features.tsx
🚧 Files skipped from review as they are similar to previous changes (28)
- .claude/guides/08-deployment.md
- packages/docs/src/components/PlatformTabs.tsx
- packages/docs/src/styles/dark-mode.css
- packages/docs/src/hooks/useScrollToHash.ts
- packages/docs/src/pages/docs/horizon-setup.tsx
- packages/docs/src/styles/variables.css
- packages/docs/src/pages/tutorials.tsx
- packages/docs/openiap-versions.json
- .claude/guides/06-gql-package.md
- packages/docs/src/pages/docs/updates/versions.tsx
- .claude/guides/02-api-naming.md
- packages/docs/src/components/SEO.tsx
- packages/docs/src/pages/introduction.tsx
- packages/docs/src/pages/home.tsx
- packages/docs/src/components/LanguageTabs.tsx
- packages/docs/src/components/HighlightText.tsx
- .claude/guides/01-overview.md
- packages/docs/package.json
- packages/docs/src/pages/languages.tsx
- packages/docs/src/components/CodeBlock.tsx
- packages/docs/src/pages/docs/updates/announcements.tsx
- packages/docs/src/pages/sponsors.tsx
- packages/docs/src/pages/docs/ecosystem.tsx
- .gitignore
- .claude/guides/07-docs-package.md
- packages/docs/src/pages/docs/errors.tsx
- packages/docs/src/main.tsx
- packages/docs/src/pages/docs/android-setup.tsx
🧰 Additional context used
📓 Path-based instructions (12)
{**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}}
📄 CodeRabbit inference engine (CLAUDE.md)
{**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}}: iOS-only functions must end with 'IOS' suffix (e.g., clearTransactionIOS, getStorefrontIOS, presentCodeRedemptionSheetIOS)
Android-only functions must end with 'Android' suffix (e.g., acknowledgePurchaseAndroid, consumePurchaseAndroid, getPackageNameAndroid)
Cross-platform functions must have no platform suffix (e.g., fetchProducts, requestPurchase, getAvailablePurchases)
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use 'get' prefix for data retrieval functions (e.g., getPromotedProductIOS)
Use 'request' prefix for async operations/requests (e.g., requestPurchase)
Use 'clear' prefix for remove/reset operations (e.g., clearTransactionIOS)
Use 'is/has' prefix for boolean checks (e.g., isEligibleForIntroOfferIOS, hasActiveSubscriptions)
Use 'show/present' prefix for UI display operations (e.g., showManageSubscriptionsIOS)
Use 'begin' prefix to start a process (e.g., beginRefundRequestIOS)
Use 'finish/end' prefix to complete a process (e.g., finishTransaction, endConnection)
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.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 signals should be defined in src/lib/signals.ts with helper functions like openAuthModal() and closeAuthModal()
Delete unused components, functions, and imports immediately; do not keep commented-out code or unused variables/parameters
Run bun run tsc or npm run typecheck to check for TypeScript errors before committing documentation
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/docs/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*.{tsx,ts}: Modal instances should be defined once at app root level and managed via global state using Preact Signals
Any function that returns a Promise must be wrapped with void operator when used where a void return is expected (e.g., onClick handlers)
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/docs/{src,components}/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/{src,components}/**/*.{tsx,ts}: Reusable React components should be placed in src/components/ directory; single-use components should be co-located with their parent
When a component has sub-components used only within it, create a folder structure with index.tsx for main component and separate files for sub-components
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/docs/**/*.{tsx,ts,css}
📄 CodeRabbit inference engine (CLAUDE.md)
All React components must respect parent boundaries and children must never overflow outside parent containers
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/styles/components.csspackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/docs/**/*.{css,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*.{css,tsx}: Use overflow-hidden on parent containers when necessary to prevent children overflow
Apply break-words for text content that might be long to prevent overflow
Use whitespace-nowrap for navigation items to prevent wrapping
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/styles/components.csspackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/docs/**/*.{ts,tsx,css,md,mdx,json}
📄 CodeRabbit inference engine (CLAUDE.md)
Run npx prettier --write to format all documentation files before committing
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/openiap-versions.jsonpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/styles/components.csspackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/docs/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Run npm run lint to check for linting issues before committing documentation changes
Files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/features/subscription.tsxpackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxpackages/docs/src/components/AnchorLink.tsxpackages/docs/src/pages/docs/ios-setup.tsx
packages/google/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
packages/google/**/*.kt: In Android package (packages/google), do not add 'Android' suffix to function names even for Android-specific APIs
Cross-platform API functions in Android should have no suffix (e.g., initConnection, fetchProducts, requestPurchase)
Files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
packages/apple/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
packages/apple/**/*.swift: In iOS package, iOS-specific functions MUST have 'IOS' suffix (e.g., presentCodeRedemptionSheetIOS, showManageSubscriptionsIOS)
In iOS package, cross-platform functions have NO suffix (e.g., initConnection, fetchProducts, requestPurchase, finishTransaction)
In Swift, acronyms should be ALL CAPS only when they appear as a suffix; at beginning or middle use Pascal case (e.g., OpenIAP, IapManager, IapPurchase, ProductIAP)
Files:
packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/Types.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Do not edit auto-generated Types.swift file in packages/apple/Sources/Models/ directly; regenerate via ./scripts/generate-types.sh
Files:
packages/apple/Sources/Models/Types.swift
🧠 Learnings (30)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.876Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/openiap/src/main/java/*/Types.kt : Do not edit generated Android file packages/google/openiap/src/main/Types.kt directly; regenerate via ./scripts/generate-types.sh
Applied to files:
.claude/guides/05-google-package.mdpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/**/*.kt : In Android package (packages/google), do not add 'Android' suffix to function names even for Android-specific APIs
Applied to files:
.claude/guides/05-google-package.md
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
.claude/guides/05-google-package.mdpackages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx.claude/guides/03-deprecations.mdpackages/docs/src/pages/docs/apis.tsxCLAUDE.mdpackages/gql/src/type.graphqlpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/**/*.kt : Cross-platform API functions in Android should have no suffix (e.g., initConnection, fetchProducts, requestPurchase)
Applied to files:
.claude/guides/05-google-package.mdCLAUDE.mdpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Load and review the relevant CONVENTION.md file before writing or editing in packages/gql, packages/google, or packages/apple
Applied to files:
.claude/guides/05-google-package.mdopeniap-versions.json.claude/guides/04-apple-package.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Put reusable Kotlin helpers in packages/google/openiap/src/main/java/dev/hyo/openiap/utils/
Applied to files:
.claude/guides/05-google-package.mdpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : Android-only functions must end with 'Android' suffix (e.g., acknowledgePurchaseAndroid, consumePurchaseAndroid, getPackageNameAndroid)
Applied to files:
.claude/guides/05-google-package.mdpackages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/features/purchase.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : Cross-platform functions must have no platform suffix (e.g., fetchProducts, requestPurchase, getAvailablePurchases)
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/events.tsxpackages/docs/src/pages/docs/apis.tsxCLAUDE.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : iOS-only functions must end with 'IOS' suffix (e.g., clearTransactionIOS, getStorefrontIOS, presentCodeRedemptionSheetIOS)
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsxpackages/docs/src/pages/docs/apis.tsxCLAUDE.mdpackages/docs/src/pages/docs/ios-setup.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'request' prefix for async operations/requests (e.g., requestPurchase)
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/features/purchase.tsxpackages/docs/src/pages/docs/apis.tsxCLAUDE.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/docs/**/*.{md,mdx,html} : Use kebab-case for URL anchors (e.g., #fetch-products, #get-app-transaction-ios)
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/apis.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/docs/{src,components}/**/*.{tsx,ts} : Reusable React components should be placed in src/components/ directory; single-use components should be co-located with their parent
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/pages/docs.tsxpackages/docs/src/pages/docs/features/offer-code-redemption.tsx
📚 Learning: 2025-11-22T19:30:03.876Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.876Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Applied to files:
packages/docs/src/pages/docs/features/external-purchase.tsxpackages/docs/src/components/SearchModal.tsxpackages/docs/src/pages/docs/lifecycle/index.tsxpackages/docs/src/pages/docs/apis.tsxCLAUDE.mdpackages/gql/src/type.graphqlpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/docs/src/pages/docs/ios-setup.tsxpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/docs/**/*.{ts,tsx} : Use kebab-case for search modal IDs (e.g., id: 'request-products')
Applied to files:
packages/docs/src/components/SearchModal.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/docs/**/*.{ts,tsx} : Modal state signals should be defined in src/lib/signals.ts with helper functions like openAuthModal() and closeAuthModal()
Applied to files:
packages/docs/src/components/SearchModal.tsxCLAUDE.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/docs/{src,components}/**/*.{tsx,ts} : When a component has sub-components used only within it, create a folder structure with index.tsx for main component and separate files for sub-components
Applied to files:
packages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'show/present' prefix for UI display operations (e.g., showManageSubscriptionsIOS)
Applied to files:
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/apis.tsxCLAUDE.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'is/has' prefix for boolean checks (e.g., isEligibleForIntroOfferIOS, hasActiveSubscriptions)
Applied to files:
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsxpackages/docs/src/pages/docs/apis.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Update iOS GQL types by editing openiap-versions.json gql version and running ./scripts/generate-types.sh
Applied to files:
openiap-versions.jsonpackages/gql/src/type.graphql
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'clear' prefix for remove/reset operations (e.g., clearTransactionIOS)
Applied to files:
packages/docs/src/pages/docs/apis.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/**/*.swift : In iOS package, cross-platform functions have NO suffix (e.g., initConnection, fetchProducts, requestPurchase, finishTransaction)
Applied to files:
packages/docs/src/pages/docs/apis.tsxCLAUDE.mdpackages/apple/Sources/Models/Types.swift.claude/guides/04-apple-package.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'begin' prefix to start a process (e.g., beginRefundRequestIOS)
Applied to files:
packages/docs/src/pages/docs/apis.tsx
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/**/*.swift : In iOS package, iOS-specific functions MUST have 'IOS' suffix (e.g., presentCodeRedemptionSheetIOS, showManageSubscriptionsIOS)
Applied to files:
packages/docs/src/pages/docs/apis.tsx.claude/guides/04-apple-package.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/docs/**/*.{tsx,ts} : Modal instances should be defined once at app root level and managed via global state using Preact Signals
Applied to files:
CLAUDE.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'get' prefix for data retrieval functions (e.g., getPromotedProductIOS)
Applied to files:
CLAUDE.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/gql/src/generated/**/*.{ts,swift,kt,dart} : Do not edit auto-generated GraphQL type files; regenerate types from GraphQL schema via bun run generate
Applied to files:
packages/gql/src/type.graphql
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : Do not edit auto-generated Types.swift file in packages/apple/Sources/Models/ directly; regenerate via ./scripts/generate-types.sh
Applied to files:
packages/apple/Sources/Models/Types.swift.claude/guides/04-apple-package.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/**/*.swift : In Swift, acronyms should be ALL CAPS only when they appear as a suffix; at beginning or middle use Pascal case (e.g., OpenIAP, IapManager, IapPurchase, ProductIAP)
Applied to files:
packages/apple/Sources/Models/Types.swift.claude/guides/04-apple-package.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Organize Apple package files: official types in Sources/Models/, helper classes in Sources/Helpers/, main modules in Sources/
Applied to files:
.claude/guides/04-apple-package.md
🧬 Code graph analysis (10)
packages/docs/src/pages/docs/features/external-purchase.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/updates/notes.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/features/purchase.tsx (4)
packages/gql/src/generated/types.ts (1)
Purchase(443-443)packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
code(1205-1227)packages/gql/src/generated/Types.kt (1)
code(1272-1294)
packages/docs/src/pages/docs.tsx (1)
packages/docs/src/components/MenuDropdown.tsx (1)
MenuDropdown(16-128)
packages/docs/src/pages/docs/features/offer-code-redemption.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/lifecycle/index.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/events.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/docs/src/pages/docs/apis.tsx (3)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
code(1205-1227)packages/gql/src/generated/Types.kt (1)
code(1272-1294)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (10)
RequestVerifyPurchaseWithIapkitResult(779-782)VerifyPurchaseResultAndroid(857-876)VerifyPurchaseResultIOS(878-887)VerifyPurchaseWithProviderResult(894-898)RequestVerifyPurchaseWithIapkitAppleProps(750-757)RequestVerifyPurchaseWithIapkitGoogleProps(759-766)RequestVerifyPurchaseWithIapkitProps(768-777)VerifyPurchaseAndroidOptions(841-846)VerifyPurchaseProps(848-853)VerifyPurchaseWithProviderProps(889-892)
🪛 LanguageTool
.claude/guides/04-apple-package.md
[style] ~35-~35: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “API”.
Context: ...enIapProtocol.swift Defines the public API interface: ```swift public protocol OpenIapModul...
(ACRONYM_TAUTOLOGY)
🪛 SwiftLint (0.57.0)
packages/apple/Sources/Models/Types.swift
[Warning] 154-154: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 155-155: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 159-159: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 160-160: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 203-203: String enum values can be omitted when they are equal to the enumcase name
(redundant_string_enum_value)
[Warning] 977-977: Type name 'RequestVerifyPurchaseWithIapkitAppleProps' should be between 3 and 40 characters long
(type_name)
[Warning] 996-996: Type name 'RequestVerifyPurchaseWithIapkitGoogleProps' should be between 3 and 40 characters long
(type_name)
[Warning] 1530-1530: Type name 'MutationVerifyPurchaseWithProviderHandler' should be between 3 and 40 characters long
(type_name)
⏰ 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 (41)
packages/docs/src/styles/components.css (5)
236-242: Language Tabs header layout looks good.The
overflow-x: autoon.language-tabs-headerappropriately handles horizontal scrolling when tabs exceed viewport width, andgap: 0.25remprovides proper spacing.
244-256: White-space nowrap prevents tab wrapping.Good use of
white-space: nowrapto prevent language tab names from wrapping. The sizing and padding follow existing platform-tabs patterns.
278-291: Code block integration styling is solid.The seamless integration of language tabs with code blocks (margin-top: 0 and border-radius adjustments) ensures clean visual connection between tab header and content.
294-302: Info Note border styling approved.The border and border-radius additions to
.info-notemaintain consistency with other card components in the stylesheet.
1-339: Format CSS file with prettier before committing.Per coding guidelines for
packages/docs/**/*.css, runnpx prettier --writeon this file to ensure consistent formatting across documentation styles.packages/docs/src/pages/docs/ios-setup.tsx (6)
7-12: Good SEO implementation with dual terminology.The SEO component is properly positioned and includes both "purchase verification" and "receipt validation" in keywords, which helps users find this page using either the modern or legacy terminology.
402-406: Clear and accurate terminology update.The content correctly explains purchase verification while maintaining technical accuracy about StoreKit 2's JWS implementation and legacy receipt formats.
1-580: I'll verify the coding guidelines compliance by checking the project structure and running the suggested verification commands on the file.
<function_calls>
#!/bin/bashCheck if packages/docs exists and its structure
ls -la packages/docs 2>&1 | head -20
</function_calls>
<function_calls>
#!/bin/bashCheck package.json scripts in packages/docs
cat packages/docs/package.json | grep -A 20 '"scripts"'
</function_calls>
<function_calls>
#!/bin/bashVerify the file exists and check its structure
ls -la packages/docs/src/pages/docs/ios-setup.tsx
wc -l packages/docs/src/pages/docs/ios-setup.tsx
</function_calls>Let me wait for the shell results to proceed with verification.
542-559: No anchor ID conflicts detected — the old anchor ID does not exist in the codebase.The search confirmed that
receipt-validation-failureswas never used as an anchor ID in the documentation. The new anchorverification-failuresis correctly implemented at line 542-544 with proper self-referencing links. No broken links will result from this change.
1-2: Imports are valid and all compliance checks pass.Both
HighlightTextandSEOcomponents exist atpackages/docs/src/components/and the relative import paths in lines 1-2 are correct. TypeScript compilation, prettier formatting, and linting all pass without errors.
389-401: No broken links from anchor ID change — verified safe.The search revealed zero references to the old
receipt-validationanchor in the codebase. Additionally,purchase-verificationis already the established standard across multiple documentation files (android-setup.tsx, lifecycle files, types.tsx). The new anchor ID follows the kebab-case convention consistently used throughout the documentation. This is a safe change with no risk of internal broken links.packages/docs/src/components/SearchModal.tsx (2)
313-320: Terminology updated appropriately for deprecated API.The description text has been updated to reference "purchase verification" instead of "receipt validation", which aligns with the broader terminology migration in this PR. Since this API is deprecated in favor of
verifyPurchase, the reference consistency is appropriate.Note: The same type name inconsistency flagged in lines 130-138 applies here, but since this is a deprecated API, it's less critical.
373-380: LGTM: Documentation path reorganization.The path update from
/docs/external-purchaseto/docs/features/external-purchaseappropriately reflects the documentation structure reorganization mentioned in the PR.openiap-versions.json (1)
2-3: Confirm types/codegen are regenerated for gql/docs 1.2.6The version bump looks good; just make sure any GraphQL/types artifacts that depend on the
gql/docsversion were regenerated (e.g., via./scripts/generate-types.sh) so the shipped schema matches 1.2.6..claude/guides/05-google-package.md (1)
1-79: Google package guide aligns well with existing conventionsThe guide clearly captures directory layout, Types.kt generation, build/test commands, and the “no
Androidsuffix in Kotlin APIs” rule for the Android‑only package. This matches the existing conventions and should be very helpful for contributors..claude/guides/03-deprecations.md (1)
1-71: Deprecation guidance is clear and matches the new VerifyPurchase surfaceThe mapping from
validateReceipt*toverifyPurchase, plus the wrapper/deprecation examples for Swift/Kotlin, is clear and consistent with the new API naming (fetchProducts/getStorefront/etc.). This should make migrations straightforward.packages/docs/src/pages/docs/lifecycle/index.tsx (1)
10-18: Lifecycle purchase‑verification section and SEO wiring look solidThe new SEO block, the rename to “Purchase Verification” (with HighlightText calling out the legacy “Receipt Validation” term), and the “When to Verify” bullets + verifyPurchase link are all consistent with the new API surface and help clarify best practices.
Also applies to: 73-112, 254-257
packages/docs/src/pages/docs/updates/notes.tsx (1)
1-16: Notes page cleanly documents API/terminology changesThe new
Notespage (SEO, useScrollToHash, and the “API & Terminology Changes” section) clearly explains the validateReceipt → verifyPurchase migration and ties in nicely with the lifecycle and external‑purchase docs. The component rename/export wiring also looks correct.Also applies to: 19-115, 160-161, 497-497
packages/docs/public/sitemap.xml (1)
5-148: Verify sitemap URLs andlastmoddates against deployed routesThe expanded sitemap structure (languages/tutorials/sponsors, new docs/features & updates pages) looks good. Please just double‑check that:
- Every listed
<loc>corresponds to a real route in the deployed docs, and- The
lastmoddate of2025-11-26matches the actual deployment date of these changes.This keeps search engines from indexing stale or non‑existent pages.
packages/docs/src/pages/docs/features/external-purchase.tsx (1)
1-66: External Purchase guide is comprehensive and follows naming conventionsThe page is very thorough: SEO + useScrollToHash integration, consistent use of
LanguageTabs/PlatformTabs, and API naming that matches the documented conventions (*IOS/*Android/ cross‑platform). The multi‑language code samples for StoreKit External Purchase and Google Alternative Billing are especially helpful for real‑world adoption.Also applies to: 86-423, 441-517, 565-771, 773-807, 808-1053, 1096-1322, 1330-1434
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
211-2710: New VerifyPurchase / IAPKit types in generated Kotlin look consistent with the GraphQL schemaThe additions for
IapkitEnvironment,IapkitStore,PurchaseVerificationProvider, the variousRequestVerifyPurchaseWithIapkit*input types,VerifyPurchaseAndroidOptions/VerifyPurchaseProps/VerifyPurchaseWithProviderProps, and theVerifyPurchaseResult*hierarchy (including the sealedVerifyPurchaseResultand updated mutation/query signatures and handler aliases) all line up with the corresponding generated TypeScript GraphQL types you included.The
fromJson/toJsonimplementations follow the existing patterns and appear symmetrical, and the removal of anyendpointfield fromRequestVerifyPurchaseWithIapkitPropsmatches the documented SDK behavior of handling the IAPKit base URL internally. Given the file’sAUTO-GENERATEDheader and the repo guideline to regenerate via scripts rather than editing, this looks like a correct regeneration from the updated schema.packages/docs/src/pages/docs/lifecycle/subscription.tsx (2)
1-19: Well-structured documentation page with proper SEO integration.Good use of
useScrollToHashhook and SEO component. The imports are properly organized and the component structure follows the established pattern in the docs package.
1374-1461: Summary section provides good cross-platform comparison.The summary grid with iOS vs Android cards and the related documentation accordion provide a clear overview for users. The responsive grid layout with
minmax(280px, 1fr)handles different screen sizes appropriately.CLAUDE.md (4)
78-78: Correctly addsverifyPurchaseto cross-platform functions list.This aligns with the PR objective of replacing
validateReceiptwithverifyPurchaseas a cross-platform function without platform suffix.
115-116: Clear deprecation notices for legacy function names.Both
validateReceiptandvalidateReceiptIOSare properly marked as deprecated with clear migration guidance toverifyPurchase.
317-318: Kotlin example correctly showsverifyPurchase()without Android suffix.This follows the documented convention: cross-platform API functions in Android-only package should have no suffix. Based on learnings, this is the correct approach.
357-357: Swift example correctly includesverifyPurchase()as cross-platform function.Consistent with the iOS package convention where cross-platform functions have no suffix.
packages/docs/src/pages/docs/features/subscription-upgrade-downgrade.tsx (4)
1-7: Clean import updates with new LanguageTabs and SEO components.Imports are properly organized and the additions support multi-language code samples and SEO integration.
14-19: Good SEO metadata for the page.The title, description, path, and keywords are appropriate and follow the pattern established in other docs pages.
270-340: Comprehensive multi-language examples for detecting upgrades.The LanguageTabs component provides TypeScript, Swift, Kotlin, and Dart examples. All code samples are idiomatic for their respective languages and correctly demonstrate the
pendingUpgradeProductIdusage.
1055-1125: Android upgrade example missing Swift tab.The Android-specific examples under
PlatformTabsonly show TypeScript, Kotlin, and Dart. This is intentional since Swift is iOS-only, so omitting it here is correct. The examples appropriately focus on platforms relevant to Android.packages/docs/src/pages/docs/features/purchase.tsx (6)
1-6: Well-organized imports for the new purchase documentation page.Imports follow the established pattern with proper relative paths. The combination of
AnchorLink,CodeBlock,LanguageTabs, andSEOcomponents enables comprehensive documentation with multi-language support.
12-17: Good SEO configuration for the purchase page.The title, description, path, and keywords are appropriate and consistent with other feature documentation pages.
363-395: Correct usage ofverifyPurchaseAPI name.The code example correctly uses
verifyPurchaseinstead of the deprecatedvalidateReceipt, consistent with the PR objectives.
700-803: Comprehensive complete example demonstrates best practices.The TypeScript example shows the full purchase flow including verification, product granting, and transaction finishing. The use of
useCallbackwith proper dependencies and cleanup in theuseEffectfollows React best practices.
1063-1068: Dartdisposemethod callssuper.dispose()correctly.The Dart example properly calls
super.dispose()to ensure parent class cleanup. The stream subscription cancellation and connection ending are handled before the super call.
477-484: The tutorial link anchorverify-purchaseexists and is correctly configured.The anchor is defined at line 149 of
packages/docs/src/pages/tutorials.tsxusing theAnchorLinkcomponent withid="verify-purchase", which creates a valid scroll anchor. The page has the necessaryuseScrollToHashhook to handle anchor navigation.packages/docs/src/pages/docs/features/subscription.tsx (1)
9-2124: Subscription docs page looks consistent and accurateThe new Subscription feature page is well-structured, uses the existing SEO/Anchor/Language/Platform tabs correctly, and its API examples (fetchProducts, subscriptionStatusIOS, verifyPurchase, deepLinkToSubscriptions, etc.) align with the current naming and platform conventions. I don’t see any issues that would block merging.
packages/docs/src/pages/docs.tsx (1)
6-7: Docs navigation and routes align with new features/updates pagesThe new imports, sidebar links, and
<Route>entries for lifecycle/subscription, the /features pages (purchase, subscription, offer-code-redemption, external-purchase, subscription-upgrade-downgrade), and the /updates pages (announcements, notes, versions) are all internally consistent and match their paths. No functional issues spotted.Also applies to: 11-21, 145-190, 193-220, 224-255
packages/gql/src/type.graphql (2)
33-47: Provider and IAPKit enums are well-scoped
PurchaseVerificationProvider,IapkitStore, andIapkitEnvironmentcleanly model the provider/store/environment axes and line up with the generated Swift enums. The naming is consistent with existing schema style and Swift casing rules (Iapkit* types).
288-322: ActiveSubscription deprecation metadata forwillExpireSoonis clearAdding
willExpireSoonwith a deprecation directive and explicit guidance to preferdaysUntilExpirationIOSprovides a good migration path while keeping existing clients working. This also matches the updated SwiftActiveSubscriptionmodel.
fbc9af4 to
7aef997
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
packages/apple/Sources/OpenIapModule.swift (3)
609-611: Unreachable guard: hardcoded URL cannot fail.
URL(string: "https://api.iapkit.com/v1/purchase/verify")cannot returnnilat runtime. Consider force-unwrapping with a static constant to eliminate this dead code path:+private static let iapkitVerifyURL = URL(string: "https://api.iapkit.com/v1/purchase/verify")! + private func verifyPurchaseWithIapkit(props: RequestVerifyPurchaseWithIapkitProps) async throws -> [RequestVerifyPurchaseWithIapkitResult] { - guard let url = URL(string: "https://api.iapkit.com/v1/purchase/verify") else { - throw makePurchaseError(code: .developerError, message: "IAPKit endpoint is required") - } + let url = Self.iapkitVerifyURLBased on learnings, the IAPKit endpoint is handled internally using a default base URL.
623-625: Guard is unreachable; consider removing or updating error message.This guard cannot trigger because line 617–619 already throws if
props.appleisnil. If kept for defensive coding, update the message to"IAPKit verification on Apple requires an apple payload"for clarity, as this module only supports Apple verification.
708-724: Add clarifying comment for the unusedThe
buildIapkitPayloadexists for structural parity with the Android module but is never invoked in this Apple implementation (line 614 rejects non-Apple stores). Consider adding a brief comment to prevent confusion:case .google: // Note: This Apple module only processes Apple payloads. // The Google case exists for structural parity with the Android SDK. guard let google = props.google else { ...packages/apple/Sources/OpenIapProtocol.swift (1)
81-84: Self-referential deprecation message is confusing.The deprecation message
"Use verifyPurchaseWithProvider instead"on the defaultverifyPurchaseWithProviderimplementation is self-referential. This will warn callers to use the exact method they're already calling.Consider either removing the deprecation attribute or clarifying the intent:
- @available(*, deprecated, message: "Use verifyPurchaseWithProvider instead") + // Default implementation for backward compatibility - override in your module func verifyPurchaseWithProvider(_ props: VerifyPurchaseWithProviderProps) async throws -> VerifyPurchaseWithProviderResult {
🧹 Nitpick comments (1)
packages/apple/Sources/Models/Types.swift (1)
1390-1450: New VerifyPurchase methods and handlers are wired through correctlyThe
MutationResolver/QueryResolverprotocols, their handler typealiases, and theMutationHandlers/QueryHandlersstructs consistently useVerifyPurchaseProps,VerifyPurchaseResult,VerifyPurchaseWithProviderProps,VerifyPurchaseWithProviderResult, andVerifyPurchaseResultIOS. This keeps the Swift surface in sync with Kotlin and the GraphQL schema. As per coding guidelines, just ensure these changes came from running the generation script (./scripts/generate-types.sh) rather than manual edits.Also applies to: 1509-1531, 1532-1603, 1489-1490, 1624-1684
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
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.tsis excluded by!**/generated/**
📒 Files selected for processing (25)
.vscode/settings.json(1 hunks)packages/apple/CONTRIBUTING.md(1 hunks)packages/apple/Sources/Models/OpenIapSerialization.swift(1 hunks)packages/apple/Sources/Models/ReceiptValidationCompat.swift(1 hunks)packages/apple/Sources/Models/Types.swift(13 hunks)packages/apple/Sources/OpenIapModule.swift(3 hunks)packages/apple/Sources/OpenIapProtocol.swift(2 hunks)packages/apple/Sources/OpenIapStore.swift(2 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift(5 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift(1 hunks)packages/google/CONTRIBUTING.md(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt(1 hunks)packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt(3 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(11 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.kt(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt(4 hunks)packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt(5 hunks)packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt(4 hunks)packages/gql/CONVENTION.md(1 hunks)packages/gql/src/api-ios.graphql(1 hunks)packages/gql/src/api.graphql(1 hunks)packages/gql/src/type-android.graphql(1 hunks)packages/gql/src/type-ios.graphql(1 hunks)packages/gql/src/type.graphql(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
- packages/apple/Sources/Models/ReceiptValidationCompat.swift
- packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt
- packages/gql/CONVENTION.md
- packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift
- packages/google/openiap/src/main/java/dev/hyo/openiap/compat/ReceiptValidationCompat.kt
- packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapProtocol.kt
- packages/apple/CONTRIBUTING.md
- packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift
🧰 Additional context used
📓 Path-based instructions (4)
packages/apple/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
packages/apple/**/*.swift: In iOS package, iOS-specific functions MUST have 'IOS' suffix (e.g., presentCodeRedemptionSheetIOS, showManageSubscriptionsIOS)
In iOS package, cross-platform functions have NO suffix (e.g., initConnection, fetchProducts, requestPurchase, finishTransaction)
In Swift, acronyms should be ALL CAPS only when they appear as a suffix; at beginning or middle use Pascal case (e.g., OpenIAP, IapManager, IapPurchase, ProductIAP)
Files:
packages/apple/Sources/OpenIapStore.swiftpackages/apple/Sources/OpenIapProtocol.swiftpackages/apple/Sources/Models/OpenIapSerialization.swiftpackages/apple/Sources/OpenIapModule.swiftpackages/apple/Sources/Models/Types.swift
packages/google/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
packages/google/**/*.kt: In Android package (packages/google), do not add 'Android' suffix to function names even for Android-specific APIs
Cross-platform API functions in Android should have no suffix (e.g., initConnection, fetchProducts, requestPurchase)
Files:
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/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)
Put reusable Kotlin helpers in packages/google/openiap/src/main/java/dev/hyo/openiap/utils/
Files:
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt
packages/apple/Sources/Models/Types.swift
📄 CodeRabbit inference engine (CLAUDE.md)
Do not edit auto-generated Types.swift file in packages/apple/Sources/Models/ directly; regenerate via ./scripts/generate-types.sh
Files:
packages/apple/Sources/Models/Types.swift
🧠 Learnings (19)
📓 Common learnings
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.876Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
📚 Learning: 2025-10-18T05:46:51.596Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 17
File: packages/google/openiap/src/main/java/dev/hyo/openiap/store/OpenIapStore.kt:620-629
Timestamp: 2025-10-18T05:46:51.596Z
Learning: In packages/google/openiap/src/main/java/**/*.kt (Android-only package): DO NOT add Android suffix to function names, even for Android-specific APIs. Exception: Only use Android suffix for cross-platform API types (e.g., ProductAndroid, PurchaseAndroid) that contrast with iOS types. Examples of correct naming: isHorizonEnvironment(context: Context), buildModule(context: Context), acknowledgePurchase(), consumePurchase().
Applied to files:
packages/google/CONTRIBUTING.mdpackages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/gql/src/type-android.graphqlpackages/gql/src/type.graphqlpackages/apple/Sources/OpenIapModule.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : Android-only functions must end with 'Android' suffix (e.g., acknowledgePurchaseAndroid, consumePurchaseAndroid, getPackageNameAndroid)
Applied to files:
packages/google/CONTRIBUTING.mdpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/openiap/src/main/java/*/Types.kt : Do not edit generated Android file packages/google/openiap/src/main/Types.kt directly; regenerate via ./scripts/generate-types.sh
Applied to files:
packages/google/CONTRIBUTING.mdpackages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/**/*.kt : Cross-platform API functions in Android should have no suffix (e.g., initConnection, fetchProducts, requestPurchase)
Applied to files:
packages/google/CONTRIBUTING.mdpackages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/**/*.swift : In Swift, acronyms should be ALL CAPS only when they appear as a suffix; at beginning or middle use Pascal case (e.g., OpenIAP, IapManager, IapPurchase, ProductIAP)
Applied to files:
packages/google/CONTRIBUTING.mdpackages/apple/Sources/OpenIapModule.swiftpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/**/*.swift : In iOS package, cross-platform functions have NO suffix (e.g., initConnection, fetchProducts, requestPurchase, finishTransaction)
Applied to files:
packages/google/CONTRIBUTING.mdpackages/apple/Sources/OpenIapStore.swiftpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/**/*.swift : In iOS package, iOS-specific functions MUST have 'IOS' suffix (e.g., presentCodeRedemptionSheetIOS, showManageSubscriptionsIOS)
Applied to files:
packages/google/CONTRIBUTING.mdpackages/apple/Sources/OpenIapModule.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'request' prefix for async operations/requests (e.g., requestPurchase)
Applied to files:
packages/google/CONTRIBUTING.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : iOS-only functions must end with 'IOS' suffix (e.g., clearTransactionIOS, getStorefrontIOS, presentCodeRedemptionSheetIOS)
Applied to files:
packages/google/CONTRIBUTING.md
📚 Learning: 2025-11-22T19:30:03.876Z
Learnt from: hyochan
Repo: hyodotdev/openiap PR: 46
File: packages/docs/src/pages/docs/types.tsx:2210-2238
Timestamp: 2025-11-22T19:30:03.876Z
Learning: In the OpenIAP SDK, the IAPKit verification endpoint is handled internally using a default base URL. Users no longer need to provide an `endpoint` field in `RequestVerifyPurchaseWithIapkitProps` - the SDK automatically routes to the correct IAPKit `/purchase/verify` endpoint.
Applied to files:
packages/google/CONTRIBUTING.mdpackages/apple/Sources/OpenIapStore.swiftpackages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/apple/Sources/OpenIapProtocol.swiftpackages/gql/src/type.graphqlpackages/apple/Sources/Models/OpenIapSerialization.swiftpackages/apple/Sources/OpenIapModule.swiftpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use 'get' prefix for data retrieval functions (e.g., getPromotedProductIOS)
Applied to files:
packages/google/CONTRIBUTING.md
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Update iOS GQL types by editing openiap-versions.json gql version and running ./scripts/generate-types.sh
Applied to files:
packages/gql/src/type-ios.graphqlpackages/gql/src/api-ios.graphqlpackages/gql/src/type.graphql
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/gql/src/generated/**/*.{ts,swift,kt,dart} : Do not edit auto-generated GraphQL type files; regenerate types from GraphQL schema via bun run generate
Applied to files:
packages/gql/src/type-ios.graphqlpackages/gql/src/type.graphql
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Put reusable Kotlin helpers in packages/google/openiap/src/main/java/dev/hyo/openiap/utils/
Applied to files:
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : Cross-platform functions must have no platform suffix (e.g., fetchProducts, requestPurchase, getAvailablePurchases)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Compile Kotlin code with ./gradlew :openiap:compileDebugKotlin to verify changes
Applied to files:
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/google/**/*.kt : In Android package (packages/google), do not add 'Android' suffix to function names even for Android-specific APIs
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt
📚 Learning: 2025-11-26T05:50:26.217Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T05:50:26.217Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : Do not edit auto-generated Types.swift file in packages/apple/Sources/Models/ directly; regenerate via ./scripts/generate-types.sh
Applied to files:
packages/apple/Sources/Models/Types.swift
🧬 Code graph analysis (9)
packages/apple/Sources/OpenIapStore.swift (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (4)
verifyPurchase(2552-2552)verifyPurchaseWithProvider(2556-2556)iapkit(1742-1764)iapkit(2347-2364)packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (3)
packages/gql/src/generated/types.ts (4)
VerifyPurchaseProps(848-853)VerifyPurchaseAndroidOptions(841-846)RequestVerifyPurchaseWithIapkitProps(768-777)RequestVerifyPurchaseWithIapkitGoogleProps(759-766)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-670)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(86-143)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (3)
packages/gql/src/generated/types.ts (1)
VerifyPurchaseWithProviderResult(894-898)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-670)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
verifyPurchaseWithIapkit(86-143)
packages/apple/Sources/OpenIapProtocol.swift (2)
packages/gql/src/generated/Types.kt (1)
androidOptions(2389-2412)packages/gql/src/generated/types.ts (1)
VerifyPurchaseProps(848-853)
packages/apple/Sources/Models/OpenIapSerialization.swift (1)
packages/gql/src/generated/types.ts (1)
VerifyPurchaseProps(848-853)
packages/apple/Sources/OpenIapModule.swift (2)
packages/gql/src/generated/Types.kt (11)
verifyPurchase(2619-2619)verifyPurchaseWithProvider(2623-2623)iapkit(1809-1831)iapkit(2414-2431)code(1272-1294)store(1544-1563)request(2079-2130)apiKey(2327-2362)appId(613-665)appId(2265-2294)packageName(2296-2325)packages/gql/src/generated/types.ts (3)
VerifyPurchaseResultIOS(878-887)VerifyPurchaseWithProviderResult(894-898)RequestVerifyPurchaseWithIapkitResult(779-782)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (1)
packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (1)
setRequestProperty(256-260)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (10)
RequestVerifyPurchaseWithIapkitResult(779-782)VerifyPurchaseResultAndroid(857-876)VerifyPurchaseResultIOS(878-887)VerifyPurchaseWithProviderResult(894-898)RequestVerifyPurchaseWithIapkitAppleProps(750-757)RequestVerifyPurchaseWithIapkitGoogleProps(759-766)RequestVerifyPurchaseWithIapkitProps(768-777)VerifyPurchaseAndroidOptions(841-846)VerifyPurchaseProps(848-853)VerifyPurchaseWithProviderProps(889-892)
packages/apple/Sources/Models/Types.swift (2)
packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
⏰ 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 (32)
packages/google/CONTRIBUTING.md (1)
106-106: Documentation update accurately reflects new type names.The examples have been correctly updated to include
OpenIapVerifyPurchasePropsandOpenIapVerifyPurchaseResultin place of the deprecated receipt validation types, aligning with the provider-based verification feature introduced in this PR. All examples consistently follow the OpenIap prefix convention..vscode/settings.json (1)
4-4: Correct the spelling entry to match actual casing used in codebase.The cSpell entry should be "IAPKit" (with capital I, A, P, K) to match the actual casing used throughout the codebase, as indicated in the learnings and documentation (e.g.,
RequestVerifyPurchaseWithIapkitProps). The current entry "Iapkit" may cause the spell checker to not recognize correctly-capitalized instances.Apply this diff to correct the capitalization:
- "Iapkit", + "IAPKit",⛔ Skipped due to learnings
Learnt from: CR Repo: hyodotdev/openiap PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-11-26T05:50:26.217Z Learning: Applies to packages/apple/**/*.swift : In Swift, acronyms should be ALL CAPS only when they appear as a suffix; at beginning or middle use Pascal case (e.g., OpenIAP, IapManager, IapPurchase, ProductIAP)Learnt from: CR Repo: hyodotdev/openiap PR: 0 File: CLAUDE.md:0-0 Timestamp: 2025-11-26T05:50:26.217Z Learning: Applies to {**/*.{ts,tsx,js,jsx},packages/docs/**/*.{ts,tsx,js,jsx}} : iOS-only functions must end with 'IOS' suffix (e.g., clearTransactionIOS, getStorefrontIOS, presentCodeRedemptionSheetIOS)packages/gql/src/type-ios.graphql (1)
226-243: VerifyPurchaseResultIOS rename is consistent and non-breakingThe type keeps the previous shape and updates naming to the new VerifyPurchase* convention; looks good and should be backward-compatible at the schema level.
packages/gql/src/type-android.graphql (1)
156-182: Android VerifyPurchase schema migration looks correct*
VerifyPurchaseAndroidOptionsandVerifyPurchaseResultAndroidmirror the prior receipt validation structures with updated names, keeping field-level compatibility and matching the generated client types.packages/gql/src/api-ios.graphql (1)
64-69: iOS validateReceiptIOS correctly migrated to VerifyPurchase typesThe query now accepts
VerifyPurchasePropsand returnsVerifyPurchaseResultIOS!while remaining deprecated in favor ofverifyPurchase, which is a clean, minimal breaking-change path.packages/apple/Sources/Models/OpenIapSerialization.swift (1)
116-118: VerifyPurchaseProps decoding helper is consistent with existing serializersDecoding
VerifyPurchasePropsvia the shareddecode(object:as:)path matches the other builder helpers and keeps error handling centralized; no issues spotted.packages/apple/Sources/OpenIapStore.swift (1)
371-391: Store-level verifyPurchase APIs are wired correctly to the module
validateReceipt(sku:)now thinly delegates toverifyPurchase(sku:)with a proper deprecation message.verifyPurchase(sku:)constructsVerifyPurchasePropsand safely unwraps the iOS variant ofVerifyPurchaseResult, falling back to a clear error if a non‑iOS result ever appears.verifyPurchaseWithProvider(_:)exposes the new provider-based flow while returning the IAPKit result list, which matches the underlying module contracts and docs about internal endpoint handling.packages/google/openiap/src/test/java/dev/hyo/openiap/ReceiptValidatorTest.kt (2)
3-22: VerifyPurchase / IAPKit test coverage is thorough and matches the validator semantics
- Google Play validation tests correctly migrated to
VerifyPurchaseProps/VerifyPurchaseAndroidOptionsand still assert success parsing and non‑2xx →InvalidReceipt.- New
verifyPurchaseWithIapkittests cover argument validation (missing/invalid payloads), success paths (including Authorization header and payload shape via Gson), and HTTP error wrapping, which closely mirror the implementation inReceiptValidator.verifyPurchaseWithIapkit.- The use of the injected
connectionFactoryplus assertions that it should not be invoked in invalid‑input cases is a solid guard against accidental network calls in those paths.Also applies to: 28-103, 105-240
243-270: FakeHttpURLConnection correctly captures headers and JSON body for assertionsThe overridden
setRequestPropertyandgetOutputStreamimplementations provide just enough behavior for the tests (status code, body, headers, captured request payload) without leaking real network usage, and integrate cleanly with the validator’soutputStream.use { ... }pattern.packages/gql/src/api.graphql (1)
71-83: Core GraphQL mutations cleanly migrated to VerifyPurchase and provider-based flowThe mutations now:
- Deprecate
validateReceiptin favor ofverifyPurchasewhile sharingVerifyPurchaseProps/VerifyPurchaseResult.- Introduce
verifyPurchaseWithProviderwith dedicated props/result and documentation.
This keeps the public schema coherent with the new provider model and platform-specific APIs.packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
39-42: Horizon module’s verifyPurchaseWithProvider wiring is correct and provider-scoped
verifyPurchaseWithProviderexplicitly supports onlyPurchaseVerificationProvider.Iapkit, returningFeatureNotSupportedfor others, which is appropriate for this Android/Horizon flavor.- It requires non‑null
props.iapkitand delegates toverifyPurchaseWithIapkit(options, TAG), then wraps the results inVerifyPurchaseWithProviderResultwith the original provider, matching the shared type definitions.- The new handler is also correctly registered in
mutationHandlers, so the GraphQL/SDK surface will see the provider-based flow.Also applies to: 660-700
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (2)
811-820: LGTM! Provider-based verification implementation is correct.The handler properly validates that only the
Iapkitprovider is supported, throws appropriate errors when options are missing, and delegates to the utility function. The function naming follows the coding guidelines (no Android suffix for cross-platform API).
849-850: Correctly wired into mutation handlers.The new
verifyPurchaseWithProviderhandler is properly added toMutationHandlersalongside the existing verification handlers.packages/apple/Sources/OpenIapModule.swift (1)
594-606: Provider verification entry point is well-implemented.The method correctly validates the provider type, ensures required IAPKit parameters are present, and delegates to the internal verification function. Error handling is appropriate with clear messages.
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt (3)
86-99: IAPKit verification function correctly restricts to Google store.The implementation properly validates that only Google payloads are supported on Android, aligning with the symmetric Apple-only restriction in the Swift module. Per learnings, the endpoint is handled internally using the default base URL.
111-141: Comprehensive error handling with proper resource cleanup.The function handles HTTP errors, JSON parsing failures, and IO exceptions appropriately, with consistent logging that includes the store identifier. The
finallyblock ensures the connection is disconnected.
145-171: Payload builder correctly validates required Google fields.The function ensures
packageName,purchaseId, andpurchaseTokenare all non-blank before building the payload, with clear error messages for missing requirements.packages/apple/Sources/OpenIapProtocol.swift (2)
86-99: Backward compatibility wrappers are correctly implemented.The deprecated wrappers properly map legacy
ReceiptValidationPropsto the newVerifyPurchasePropsand delegate to the updated protocol methods, preserving source compatibility for existing callers.
45-49: Protocol changes are breaking but mitigated by default implementation.Adding
verifyPurchaseWithProviderto the protocol is a breaking change for external conformers. The default implementation (lines 81-84) that throws.featureNotSupportedallows existing conformers to compile, though they'll get runtime errors if the new method is called.Ensure the CHANGELOG documents this as a potentially breaking change for custom protocol conformers.
packages/gql/src/type.graphql (3)
33-46: New enums for provider-based verification are well-defined.The
PurchaseVerificationProvider,IapkitStore, andIapkitEnvironmentenums provide clear discriminators for the multi-provider verification flow.
221-273: IAPKit input and result types are well-structured.The schema correctly separates Apple and Google verification parameters, with appropriate required/optional field designations. The
environmentdefaulting toSandboxis sensible for development safety.
275-286: Provider-scoped types are extensible for future providers.The design with
PurchaseVerificationProviderenum and optional provider-specific payloads allows for adding new verification providers in the future while maintaining backward compatibility.packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (6)
211-226: IAPKit enums and provider look consistent and robust
IapkitEnvironment,IapkitStore, andPurchaseVerificationProviderfollow the existing enum pattern (case-insensitivefromJson,rawValue-basedtoJson) and match the TS GraphQL definitions. No issues here.Also applies to: 228-243, 375-387
1477-1496: Provider result aggregation matches GraphQL contract
RequestVerifyPurchaseWithIapkitResultandVerifyPurchaseWithProviderResultmirror the TS interfaces (store,valid,iapkit,provider) and use the same JSON shapes, so Kotlin ↔︎ GraphQL ↔︎ Swift stay aligned.Also applies to: 1742-1764
2198-2227: IAPKit Apple/Google props and root props align with docs and TSThe three
RequestVerifyPurchaseWithIapkit*data classes match the TS types and omit anyendpointfield, which is correct now that the SDK routes to the IAPKit/purchase/verifyendpoint internally. Based on learnings, this is the desired shape.Also applies to: 2229-2258, 2260-2295
2297-2320: VerifyPurchase Android options and props are well-shaped
VerifyPurchaseAndroidOptionsandVerifyPurchasePropsmatch the TS schema (optionalandroidOptions, requiredsku) and keep Android-specific details encapsulated. JSON mapping is straightforward and consistent with the rest of the file.Also applies to: 2322-2345
2347-2364: VerifyPurchase union wiring looks correct
VerifyPurchaseWithProviderPropsand the sealedVerifyPurchaseResultunion use the same union-dispatch pattern asProduct/Purchase, withfromJsonswitching on__typenameand delegating to platform-specific result types. This matches the generated TS unions.Also applies to: 2434-2446
2453-2557: Verification confirmed: All new APIs coherently use VerifyPurchaseProps; legacy ReceiptValidation references are intentional backward-compatibility measuresAll remaining
ReceiptValidation*references are properly accounted for:
- Apple protocol functions (
validateReceiptIOS,validateReceipt,verifyPurchase) are marked@deprecatedand serve as wrapper functions that convertReceiptValidationPropstoVerifyPurchasePropsbefore calling the new implementations- Type aliases in
ReceiptValidationCompat.ktandReceiptValidationCompat.swiftprovide intentional backward compatibility- Handler typealias definitions (lines 2683–2686, 2731–2752) and handler containers (lines 2688–2710) correctly wire the new
VerifyPurchase*handlers withVerifyPurchaseProps- No problematic legacy code remains in active use
packages/apple/Sources/Models/Types.swift (4)
153-156: IAPKit enums/providers match naming and style guidelines
IapkitEnvironment,IapkitStore, andPurchaseVerificationProviderfollow the existing enum conventions (String, Codable, CaseIterable) and use Pascal‑case identifiers with lowercase raw values, which fits the Swift naming guidance for acronyms in this project.Also applies to: 158-161, 202-204
563-566: IAPKit and provider result models are consistent with Kotlin & TS
RequestVerifyPurchaseWithIapkitResult,VerifyPurchaseResultAndroid,VerifyPurchaseResultIOS, andVerifyPurchaseWithProviderResultexpose the same fields as the Kotlin data classes and TS interfaces, giving a clean cross‑platform contract for verification results.Also applies to: 604-640
977-995: VerifyPurchase IAPKit props mirror schema and remove endpoint as intendedThe three
RequestVerifyPurchaseWithIapkit*structs,VerifyPurchaseAndroidOptions,VerifyPurchaseProps, andVerifyPurchaseWithProviderPropsline up with the GraphQL/TS definitions and intentionally do not expose anendpointfield, relying on the internal default base URL instead. Based on learnings, this matches the updated IAPKit flow.Also applies to: 996-1013, 1015-1036, 1038-1055, 1057-1070, 1072-1083
1382-1385: VerifyPurchaseResult enum mirrors previous receipt result unionThe
VerifyPurchaseResultenum adopts the same pattern as prior union enums (case verifyPurchaseResultAndroid,case verifyPurchaseResultIos) and aligns with the platform‑specific result structs. Given this file is generated, the Codable strategy should stay consistent with the existing unions.
…types - Remove verificationMethod from LaunchedEffect dependencies (Android) - Add processedPurchaseKey to prevent duplicate purchase processing (iOS/Android) - Update IapkitPurchaseState: PURCHASED → ENTITLED, remove FAILED, add INAUTHENTIC - Fix nullable purchaseToken handling in Android Example apps - Simplify verifyWithIapkit helper functions
There was a problem hiding this comment.
Actionable comments posted: 9
♻️ Duplicate comments (1)
packages/apple/Sources/OpenIapModule.swift (1)
609-623: Past review issues not addressed: unreachable URL guard and misleading error message.As flagged in prior review:
- Line 609-611: The URL guard is dead code since
URL(string: "https://api.iapkit.com/v1/purchase/verify")cannot fail.- Line 622: The error message "IAPKit verification requires apple and/or google payloads" is misleading since this Apple module only supports Apple payloads.
- private func verifyPurchaseWithIapkit(props: RequestVerifyPurchaseWithIapkitProps) async throws -> [RequestVerifyPurchaseWithIapkitResult] { - guard let url = URL(string: "https://api.iapkit.com/v1/purchase/verify") else { - throw makePurchaseError(code: .developerError, message: "IAPKit endpoint is required") - } + private static let iapkitVerifyURL = URL(string: "https://api.iapkit.com/v1/purchase/verify")! + + private func verifyPurchaseWithIapkit(props: RequestVerifyPurchaseWithIapkitProps) async throws -> [RequestVerifyPurchaseWithIapkitResult] { + let url = Self.iapkitVerifyURL let targets: [(store: IapkitStore, body: Data)] = try { - // On Apple, only Apple verification is supported + // This Apple module only supports Apple verification payloads guard props.apple != nil else { throw makePurchaseError(code: .developerError, message: "Apple verification parameters are required") } return [(.apple, try buildIapkitPayload(props: props, store: .apple))] }() - guard targets.isEmpty == false else { - throw makePurchaseError(code: .developerError, message: "IAPKit verification requires apple and/or google payloads") - } + // Note: targets.isEmpty check removed as the guard above guarantees at least one Apple target
🧹 Nitpick comments (15)
packages/apple/Example/OpenIapExample/Info.plist.example (1)
1-8: Example plist is fine; consider reinforcing “don’t use in production”The plist structure and placeholder key are fine. To avoid accidental use of a real production secret here, consider:
- Renaming the placeholder to something like
DEVELOPMENT_IAPKIT_API_KEY_HERE, and/or- Mentioning in docs that this file is for local/example use only, not for shipping builds.
packages/google/Example/build.gradle.kts (1)
39-43: IAPKit API key wiring looks correct for the exampleThe fallback chain from
local.properties→ project property → empty string is solid, and exposing it viaBuildConfig.IAPKIT_API_KEYmatches how the example screens consume it. Just keep in mind this embeds the key into the APK, so this pattern should remain strictly for sample/debug apps rather than production.packages/docs/src/pages/docs/example.tsx (1)
613-628: Align Gradle snippet with actual Example/build.gradle.ktsThe Android “How API Key is Loaded” snippet is conceptually correct but the
buildConfigFieldline:buildConfigField( "String", "IAPKIT_API_KEY", ""${'$'}{localProperties.getProperty("iapkit.api.key", "")}"" )is not something you’d want to copy verbatim into
build.gradle.kts(quoting is awkward and doesn’t match the real Example module, which first assignsval iapkitApiKey = ...and then wraps it in quotes).Consider updating the snippet to mirror the actual Example/build.gradle.kts structure (compute
iapkitApiKeyfirst, thenbuildConfigField("String", "IAPKIT_API_KEY", "\"${'$'}iapkitApiKey\"")) so readers can safely copy it into their own projects.packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (1)
682-689: Use StateFlow value instead of repeatedly callingfirst()in the retry loopThe connection retry loop currently does:
while (!iapStore.isConnected.first() && System.currentTimeMillis() - started < 1500) { delay(100) }If
isConnectedis aStateFlow<Boolean>(which it looks like, given how it’s used elsewhere), repeatedly callingfirst()creates a new collector on every iteration. You can avoid unnecessary Flow collection and mirror the subscription screen by reading the current value instead:-while (!iapStore.isConnected.first() && System.currentTimeMillis() - started < 1500) { +while (!iapStore.isConnected.value && System.currentTimeMillis() - started < 1500) { delay(100) }This keeps the loop cheap and avoids any subtle Flow lifecycle issues.
packages/docs/src/pages/docs.tsx (1)
19-19: LGTM! Example page integration is straightforward.The new Example page is properly wired into navigation and routing. The placement under "Setup Guide" is functional, though you might consider a dedicated "Examples" or "Getting Started" section for better organization.
Also applies to: 143-151, 263-263
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (1)
85-103: Consider using PurchaseVerificationFailed instead of InvalidReceipt.These tests catch
OpenIapError.InvalidReceipt, but the PR introduces a new error taxonomy withPurchaseVerificationFailed. Based on the error code updates inerror.graphqlandOpenIapError.swift, the new verification flows should use the updated error type.Verify the correct error type and update if needed:
try { validateReceiptWithGooglePlay( props, "TEST_TAG" ) { _ -> FakeHttpURLConnection(401, """{"error":"unauthorized"}""") } throw AssertionError("Expected InvalidReceipt for non-2xx response") - } catch (error: OpenIapError.InvalidReceipt) { - assertEquals("Invalid receipt", error.message) + } catch (error: OpenIapError.PurchaseVerificationFailed) { + assertEquals("Purchase verification failed", error.message) }Also applies to: 213-233
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (2)
45-78: Misleading test name: tests empty array, not nil.The test name
testStoreReturnsNilWhenProviderResultIsNilsuggests testing a nil scenario, but the test actually verifies that an emptyiapkitarray results inresult.count == 0. Consider renaming to better reflect the actual behavior being tested.@MainActor - func testStoreReturnsNilWhenProviderResultIsNil() async throws { + func testStoreReturnsEmptyWhenProviderResultIsEmpty() async throws {
102-106: Remove redundantVoidreturn types.As flagged by SwiftLint, returning
Voidin function declarations is redundant. This applies to lines 102, 106, and 152.- func restorePurchases() async throws -> Void { () } + func restorePurchases() async throws { } // ... - func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throws -> Void { () } + func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throws { } // ... line 152 - func deepLinkToSubscriptions(_ options: DeepLinkOptions?) async throws -> Void { () } + func deepLinkToSubscriptions(_ options: DeepLinkOptions?) async throws { }packages/apple/Example/OpenIapExample/Screens/PurchaseFlowScreen.swift (1)
203-277: Consider extracting shared VerificationMethodCard component.This
VerificationMethodCardimplementation is nearly identical to the one inSubscriptionFlowScreen.swift(see relevant code snippet at lines 479-562). Consider extracting this into a shared UI component to avoid duplication.packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (3)
136-143: Log the swallowed exception for debuggability.As flagged by detekt, the exception in the catch block is swallowed without logging. While the fallback behavior is appropriate, logging the exception at debug level would aid troubleshooting.
val errorMessage = try { val mapType = object : TypeToken<Map<String, Any?>>() {}.type val errorJson = gson.fromJson<Map<String, Any?>>(responseBody, mapType) extractIapkitErrorMessage(errorJson) ?: "HTTP $statusCode" } catch (e: Exception) { + OpenIapLog.debug("Failed to extract error message: ${e.message}", tag) "HTTP $statusCode" }
163-166: Log the swallowed exception for debuggability.Similar to above, logging the JSON parsing exception would help diagnose issues.
} catch (jsonError: Exception) { - OpenIapLog.warn("Failed to parse IAPKit verification response: ${jsonError.message}", tag) + OpenIapLog.warn("Failed to parse IAPKit verification response: ${jsonError.message}", tag, jsonError) throw OpenIapError.PurchaseVerificationFailed("Failed to parse response") }
200-252: LGTM with minor suggestion.The
extractIapkitErrorMessagefunction properly handles nested IAPKit error structures, mirroring the Swift implementation. The recursive extraction logic is sound.Consider logging the swallowed exception at line 237 at debug level:
return try { val nested = gson.fromJson(originalError, Map::class.java) as? Map<String, Any?> if (nested != null) { extractIapkitErrorMessage(nested) ?: originalError } else { originalError } } catch (e: Exception) { - // Not JSON, use as-is + // Not JSON, use as-is (debug: ${e.message}) originalError }packages/gql/src/type.graphql (1)
230-243: Consider documenting the optionalapiKeyfield behavior.The
apiKeyfield is optional, but it's not clear from the schema what happens when it's omitted. Is verification allowed without an API key, or will it fail?input RequestVerifyPurchaseWithIapkitProps { """ - API key used for the Authorization header (Bearer {apiKey}). + API key used for the Authorization header (Bearer {apiKey}). + When omitted, the request is sent without authentication. """ apiKey: Stringpackages/apple/Example/OpenIapExample/Screens/SubscriptionFlowScreen.swift (1)
20-24: API key from Info.plist may be visible in IPA.Reading
IAPKIT_API_KEYfrom Info.plist means it could be extracted from the compiled app bundle. Environment variables are preferable for secrets. Consider documenting this security tradeoff for production apps.packages/apple/Sources/OpenIapModule.swift (1)
646-660: Consider adding a timeout to the URLSession request.The HTTP request to IAPKit has no explicit timeout, which could leave the app waiting indefinitely if the service is unresponsive.
+ request.timeoutInterval = 30 // 30 second timeout request.httpBody = target.body // Log request details for debugging
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (16)
packages/docs/public/examples/1. [Android] Example.pngis excluded by!**/*.pngpackages/docs/public/examples/1. [IOS] Example.pngis excluded by!**/*.pngpackages/docs/public/examples/2. [Android] Purchase Flow.pngis excluded by!**/*.pngpackages/docs/public/examples/2. [IOS] Purchase Flow.pngis excluded by!**/*.pngpackages/docs/public/examples/3. [Android] Subscription Flow.pngis excluded by!**/*.pngpackages/docs/public/examples/3. [IOS] Subscription Flow Upgrade.pngis excluded by!**/*.pngpackages/docs/public/examples/4. [Android] Subscription Flow Upgrade.pngis excluded by!**/*.pngpackages/docs/public/examples/4. [IOS] Available Purchases.pngis excluded by!**/*.pngpackages/docs/public/examples/5. [Android] Available Purchases.pngis excluded by!**/*.pngpackages/docs/public/examples/5. [IOS] Offer Code.pngis excluded by!**/*.pngpackages/docs/public/examples/6. [Android] Redeem Offer Code.pngis excluded by!**/*.pngpackages/docs/public/examples/6. [IOS] Alternative Billing.pngis excluded by!**/*.pngpackages/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.tsis excluded by!**/generated/**
📒 Files selected for processing (32)
packages/apple/Example/.gitignore(1 hunks)packages/apple/Example/Martie.xcodeproj/project.pbxproj(2 hunks)packages/apple/Example/Martie.xcodeproj/xcshareddata/xcschemes/OpenIapExample.xcscheme(1 hunks)packages/apple/Example/OpenIapExample/Info.plist.example(1 hunks)packages/apple/Example/OpenIapExample/Screens/PurchaseFlowScreen.swift(8 hunks)packages/apple/Example/OpenIapExample/Screens/SubscriptionFlowScreen.swift(5 hunks)packages/apple/README.md(3 hunks)packages/apple/Sources/Models/OpenIapError.swift(1 hunks)packages/apple/Sources/Models/Types.swift(15 hunks)packages/apple/Sources/OpenIapModule.swift(4 hunks)packages/apple/Tests/OpenIapTests.swift(1 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift(1 hunks)packages/docs/package.json(1 hunks)packages/docs/src/pages/docs.tsx(3 hunks)packages/docs/src/pages/docs/errors.tsx(7 hunks)packages/docs/src/pages/docs/example.tsx(1 hunks)packages/docs/src/styles/pages.css(1 hunks)packages/google/Example/build.gradle.kts(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt(6 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt(10 hunks)packages/google/README.md(1 hunks)packages/google/local.properties.example(1 hunks)packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt(3 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt(2 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(14 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt(0 hunks)packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt(5 hunks)packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt(1 hunks)packages/gql/package.json(1 hunks)packages/gql/src/error.graphql(1 hunks)packages/gql/src/type.graphql(2 hunks)
💤 Files with no reviewable changes (1)
- packages/google/openiap/src/main/java/dev/hyo/openiap/utils/ReceiptValidator.kt
✅ Files skipped from review due to trivial changes (2)
- packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt
- packages/gql/package.json
🧰 Additional context used
🧬 Code graph analysis (12)
packages/apple/Example/OpenIapExample/Screens/PurchaseFlowScreen.swift (1)
packages/apple/Example/OpenIapExample/Screens/SubscriptionFlowScreen.swift (1)
VerificationMethodCard(480-563)
packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (3)
packages/gql/src/generated/types.ts (2)
RequestVerifyPurchaseWithIapkitProps(764-771)RequestVerifyPurchaseWithIapkitGoogleProps(759-762)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-700)packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (2)
verifyLocally(1288-1290)verifyWithIapkit(1269-1285)
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
verifyPurchaseWithGooglePlay(26-81)verifyPurchaseWithIapkit(83-174)packages/gql/src/generated/types.ts (1)
VerifyPurchaseWithProviderResult(891-895)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-700)
packages/docs/src/pages/docs/errors.tsx (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt (1)
code(6-319)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
code(1268-1290)packages/gql/src/generated/Types.kt (1)
code(1333-1355)
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (1)
packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (2)
verifyLocally(581-583)verifyWithIapkit(561-577)
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(249-253)packages/apple/Sources/OpenIapModule.swift (1)
extractIapkitErrorMessage(745-769)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
verifyPurchaseWithGooglePlay(26-81)verifyPurchaseWithIapkit(83-174)packages/gql/src/generated/types.ts (1)
VerifyPurchaseWithProviderResult(891-895)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-700)
packages/docs/src/pages/docs/example.tsx (2)
packages/google/openiap/src/main/java/dev/hyo/openiap/OpenIapError.kt (1)
code(6-319)packages/gql/src/generated/Types.kt (1)
code(1333-1355)
packages/apple/Example/OpenIapExample/Screens/SubscriptionFlowScreen.swift (3)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (20)
id(1406-1428)iapkit(1814-1836)iapkit(2389-2406)verifyPurchase(2594-2594)verifyPurchaseWithProvider(2598-2598)rawValue(15-44)rawValue(46-166)rawValue(171-194)rawValue(196-221)rawValue(226-289)rawValue(291-306)rawValue(308-324)rawValue(326-347)rawValue(349-367)rawValue(369-384)rawValue(386-407)rawValue(409-436)rawValue(438-450)rawValue(452-467)rawValue(469-493)packages/gql/src/generated/Types.kt (6)
id(1471-1493)iapkit(1879-1901)iapkit(2454-2471)verifyPurchase(2659-2659)verifyPurchaseWithProvider(2663-2663)rawValue(14-43)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (3)
packages/gql/src/generated/types.ts (4)
VerifyPurchaseProps(845-850)VerifyPurchaseAndroidOptions(838-843)RequestVerifyPurchaseWithIapkitProps(764-771)RequestVerifyPurchaseWithIapkitGoogleProps(759-762)packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(608-700)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)
verifyPurchaseWithIapkit(83-174)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (10)
RequestVerifyPurchaseWithIapkitResult(773-779)VerifyPurchaseResultAndroid(854-873)VerifyPurchaseResultIOS(875-884)VerifyPurchaseWithProviderResult(891-895)RequestVerifyPurchaseWithIapkitAppleProps(754-757)RequestVerifyPurchaseWithIapkitGoogleProps(759-762)RequestVerifyPurchaseWithIapkitProps(764-771)VerifyPurchaseAndroidOptions(838-843)VerifyPurchaseProps(845-850)VerifyPurchaseWithProviderProps(886-889)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (4)
packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchaseWithProvider(594-606)verifyPurchase(589-592)packages/gql/src/generated/types.ts (9)
RequestVerifyPurchaseWithIapkitResult(773-779)VerifyPurchaseResult(852-852)VerifyPurchaseResultIOS(875-884)VerifyPurchaseWithProviderResult(891-895)VerifyPurchaseWithProviderProps(886-889)RequestVerifyPurchaseWithIapkitProps(764-771)RequestVerifyPurchaseWithIapkitAppleProps(754-757)IapkitStore(187-187)PurchaseError(491-495)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (9)
isValid(1540-1568)isValid(1775-1812)iapkit(1814-1836)iapkit(2389-2406)apiKey(2308-2337)verifyPurchaseWithProvider(2598-2598)code(1268-1290)message(1430-1449)verifyPurchase(2594-2594)packages/gql/scripts/fix-generated-types.mjs (1)
result(428-428)
🪛 detekt (1.23.8)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
[warning] 140-140: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 167-167: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 237-237: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🪛 SwiftLint (0.57.0)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift
[Warning] 102-102: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 106-106: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 152-152: Returning Void in a function declaration is redundant
(redundant_void_return)
⏰ 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 (44)
packages/google/local.properties.example (1)
1-11: Template looks good; confirmlocal.propertiesis ignored.The placeholders and comments are clear and match the new IAPKit integration. Since this file is an
.example, it’s fine to commit, but please double‑check that any reallocal.properties(at the repo root and underpackages/google/if applicable) is covered by.gitignoreso SDK paths andiapkit.api.keynever get committed by mistake.packages/docs/src/styles/pages.css (1)
374-451: Well-structured screenshot card styling that aligns with design system.The new
.screenshot-cardcomponent follows established patterns in the file, using CSS custom properties and flexbox effectively. Dark mode and responsive behavior are properly implemented.packages/apple/Example/.gitignore (1)
9-11: Ignoring the real Info.plist for local secrets looks goodThe ignore entry matches the target’s
INFOPLIST_FILEpath and pairs well with the checked‑inInfo.plist.example, keeping API keys out of git while still documenting the required shape.packages/apple/Example/Martie.xcodeproj/project.pbxproj (1)
421-422: Confirm theGENERATE_INFOPLIST_FILE+INFOPLIST_FILEcombo behaves as you expectPointing
INFOPLIST_FILEatOpenIapExample/Info.plist(while it’s git‑ignored) fits the “local secrets from plist” pattern and lines up withInfo.plist.example. From a clean clone, Xcode should still generate or allow creating that Info.plist at build/config time.Please double‑check in Xcode that:
- The target builds cleanly from a fresh checkout before manually copying the example file, and
- Editing Info.plist (to add
IAPKIT_API_KEY) is stable and not overwritten unexpectedly by Xcode’s generated‑plist behavior.Also applies to: 452-453
packages/google/openiap/src/horizon/java/dev/hyo/openiap/OpenIapModule.kt (1)
36-42: Provider-based verification wiring looks consistentThe Horizon module’s
verifyPurchaseandverifyPurchaseWithProviderhandlers are correctly wired:
verifyPurchasereusesverifyPurchaseWithGooglePlay(props, TAG).verifyPurchaseWithProviderexplicitly supports onlyPurchaseVerificationProvider.Iapkit, requiresprops.iapkit, and returns aVerifyPurchaseWithProviderResultwrapping theverifyPurchaseWithIapkitlist.mutationHandlersnow exposes bothverifyPurchaseandverifyPurchaseWithProvider, keeping the protocol surface consistent.No functional issues spotted here.
Also applies to: 656-669, 688-699
packages/docs/package.json (1)
4-4: No action needed. The@hyodotdev/openiap-docspackage is marked as"private": trueand is not published or distributed. The version1.0.0is the standard starting point for packages in the monorepo structure following the migration commit. Semver concerns do not apply to private packages.Likely an incorrect or invalid review comment.
packages/apple/README.md (1)
68-68: LGTM! Clear version variable approach.The use of
$versionvariable placeholder and the note pointing toopeniap-versions.jsonprovides a maintainable approach for version references in documentation.Also applies to: 83-83, 92-93
packages/google/README.md (1)
51-51: LGTM! Consistent with Apple package documentation.The version variable approach is consistent across packages and provides clear guidance for users.
Also applies to: 59-59, 63-64
packages/apple/Sources/Models/OpenIapError.swift (1)
16-22: LGTM! Well-structured deprecation path.The error code migration maintains backward compatibility by preserving the old
receipt*cases while introducing newpurchaseVerification*variants. The deprecation comments clearly indicate the replacement path.packages/gql/src/error.graphql (1)
12-20: LGTM! GraphQL schema aligned with code changes.The deprecation comments and new error enum members align with the corresponding Swift error code updates, maintaining consistency across the API surface.
packages/apple/Tests/OpenIapTests.swift (1)
270-271: LGTM! Test validates backward-compatible error mapping.The test properly verifies that both the new
purchase-verification-failedcode and the legacyreceipt-failedalias map to.purchaseVerificationFailed, ensuring backward compatibility.packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (1)
8-43: LGTM!The test properly verifies that
verifyPurchaseWithProviderreturns the expected IAPKit result with correct store and validity values. The fake module setup is well-structured.packages/apple/Example/OpenIapExample/Screens/PurchaseFlowScreen.swift (5)
4-16: LGTM!The
VerificationMethodenum is well-designed with clear raw values and descriptive display names with appropriate emoji indicators.
30-38: LGTM!Good addition of state management for verification flow. The
iapkitApiKeycomputed property correctly checks both environment variables and Info.plist as fallback.
401-411: Good defensive coding for duplicate purchase prevention.The
processedPurchaseKeylogic using transaction ID and date effectively prevents duplicate processing of the same purchase callback.
422-537: LGTM!The
verifyAndFinishPurchaseflow is well-structured with clear branching for each verification method. Error handling is comprehensive, and the IAPKit branch correctly only finishes the transaction if verification passes.
495-504: Review comment is incorrect.The
RequestVerifyPurchaseWithIapkitApplePropstype definition does not include anenvironmentparameter—it only acceptsjws. The code at lines 495-504 is correct as written. The struct is defined with a single parameter:public struct RequestVerifyPurchaseWithIapkitAppleProps: Codable { public var jws: String public init(jws: String) { self.jws = jws } }Both example files (PurchaseFlowScreen.swift and SubscriptionFlowScreen.swift) follow this pattern correctly.
Likely an incorrect or invalid review comment.
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (3)
36-38: LGTM!Clean import additions for the new provider-based verification support.
Also applies to: 48-48, 65-66
807-820: LGTM!The
verifyPurchaseWithProviderimplementation correctly:
- Validates the provider is
IAPKit(throwsFeatureNotSupportedotherwise)- Checks for required
iapkitoptions (throwsDeveloperErrorif missing)- Delegates to
verifyPurchaseWithIapkitutility and wraps result appropriately
849-850: LGTM!The new handlers are correctly wired into the
mutationHandlersalongside the existing deprecatedvalidateReceiptfor backward compatibility.packages/docs/src/pages/docs/errors.tsx (3)
254-290: LGTM!The validation errors table is correctly updated with the new naming convention:
PurchaseVerificationFailed(clearer thanReceiptFailed)TransactionFinishedandTransactionFinishFailed(more precise terminology)
508-663: LGTM!All language-specific error code enums (TypeScript, Swift, Kotlin, Dart) are consistently updated with the new naming convention, maintaining alignment across platforms.
679-692: LGTM!The iOS sandbox testing table is correctly updated to reference
PurchaseVerificationFailedinstead of the oldReceiptFailed.packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (2)
26-81: LGTM!The
verifyPurchaseWithGooglePlayfunction correctly:
- Validates required Android options
- Constructs appropriate URL for subscriptions vs products
- Handles HTTP errors and JSON parsing with proper exception mapping
- Ensures connection cleanup in
finallyblock
176-196: LGTM!The
buildPayloadfunction correctly constructs the IAPKit request payload for Google verification with appropriate validation.packages/gql/src/type.graphql (4)
33-41: LGTM! Well-defined provider enums.The
PurchaseVerificationProviderandIapkitStoreenums are cleanly defined and properly scoped for future extensibility.
216-228: LGTM! Clean separation of platform-specific verification parameters.The Apple and Google verification input types properly encapsulate their respective required fields (
jwsfor Apple,purchaseTokenfor Google).
245-285: LGTM! Comprehensive purchase state enum.The
IapkitPurchaseStateenum covers a thorough range of purchase lifecycle states with clear documentation for each case.
299-310: LGTM! Provider-based verification result structure is well-designed.The
VerifyPurchaseWithProviderResultproperly aggregates results by provider and supports multiple verification results in theiapkitarray.packages/apple/Example/OpenIapExample/Screens/SubscriptionFlowScreen.swift (3)
567-656: LGTM! Verification flow is well-structured.The
verifyAndFinishPurchasemethod cleanly handles all three verification modes (none, local, iapkit) with proper async/await patterns and error handling. The JWS token validation at line 612 is a good safeguard.
695-708: LGTM! Purchase deduplication prevents duplicate processing.Using
purchase.id + transactionDateas a composite key is a solid approach to prevent processing the same purchase event multiple times.
480-563: LGTM! Clean verification method UI component.The
VerificationMethodCardprovides good UX with clear API key status feedback and configuration guidance.packages/apple/Sources/OpenIapModule.swift (4)
625-699: LGTM! Robust HTTP request handling with proper error parsing.The task group approach enables future multi-target support. Response parsing handles both success and error formats, with good state normalization from UPPER_SNAKE_CASE to lower-kebab-case.
702-741: LGTM! Clean payload construction with proper validation.The
buildIapkitPayloadmethod validates required fields before encoding and uses appropriate JSON encoder settings.
743-769: LGTM! Thoughtful error message extraction.The
extractIapkitErrorMessagehelper recursively extracts the most relevant error message from IAPKit's nested error structures.
1346-1352: LGTM! Error messages properly updated for verification codes.The new
purchaseVerification*error codes have appropriate default messages, and the deprecatedreceipt*codes are retained for backward compatibility.packages/apple/Sources/Models/Types.swift (7)
1-4: LGTM! Auto-generated types header.The header clearly indicates this file should not be edited directly and provides regeneration instructions.
162-187: LGTM! IAPKit enums properly defined with raw values.The
IapkitPurchaseStateandIapkitStoreenums use kebab-case raw values consistent with the GraphQL schema normalization.
589-595: LGTM! IAPKit result struct is complete.The
RequestVerifyPurchaseWithIapkitResultstruct properly captures all verification response fields.
654-663: LGTM! VerifyPurchaseResultIOS properly structured.The iOS result type captures validity, JWS representation, optional latest transaction, and receipt data.
1006-1045: LGTM! IAPKit input types with proper initializers.All input structs have complete initializers with appropriate default values for optional fields.
1391-1394: LGTM! VerifyPurchaseResult union enum.The enum properly wraps platform-specific result types, enabling polymorphic handling.
1454-1458: LGTM! MutationResolver updated with new verification methods.The protocol properly declares the new
verifyPurchaseWithProvidermethod alongside the existingverifyPurchaseand deprecatedvalidateReceipt.packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
1-2808: LGTM! Well-structured auto-generated types.The auto-generated types file successfully adds provider-based purchase verification support with IAPKit. All new enums, data classes, and interfaces follow consistent patterns and match the TypeScript definitions. Key additions include:
- New enums (
IapkitPurchaseState,IapkitStore,PurchaseVerificationProvider) with proper documentation- Verification result types (
VerifyPurchaseResultAndroid,VerifyPurchaseResultIOS,VerifyPurchaseWithProviderResult)- Input/props classes for Apple and Google verification flows
- Updated resolver interfaces with the new
verifyPurchaseWithProvidermethodThe type mappings between TypeScript and Kotlin are correct, nullable types are properly handled, and all
fromJson/toJsonmethods follow the established conventions.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (2)
14-27: Consider simplifying the test setup.The
validateResultparameter configured here is never used byverifyPurchaseWithProvider. The test only exercises theproviderResultpath, so the elaboratevalidateResultsetup adds unnecessary complexity.Consider using a simpler default or nil-pattern for unused parameters in the fake module initialization to make the test's intent clearer:
let module = FakeVerifyPurchaseModule( - validateResult: VerifyPurchaseResult.verifyPurchaseResultIos( - VerifyPurchaseResultIOS( - isValid: true, - jwsRepresentation: "", - latestTransaction: nil, - receiptData: "" - ) - ), + validateResult: .verifyPurchaseResultIos(VerifyPurchaseResultIOS(isValid: true, jwsRepresentation: "", latestTransaction: nil, receiptData: "")), providerResult: VerifyPurchaseWithProviderResult( iapkit: [iapkitResult], provider: .iapkit ) )
103-103: Remove redundantVoidreturn type annotations.Swift functions that don't return a value implicitly return
Void, so explicitly declaring-> Voidis redundant.Apply this diff:
-func restorePurchases() async throws -> Void { () } +func restorePurchases() async throws { () } -func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throws -> Void { () } +func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throws { () } -func deepLinkToSubscriptions(_ options: DeepLinkOptions?) async throws -> Void { () } +func deepLinkToSubscriptions(_ options: DeepLinkOptions?) async throws { () }Also applies to: 107-107, 153-153
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift(1 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt(1 hunks)packages/google/openiap/src/test/java/dev/hyo/openiap/OpenIapErrorTest.kt(2 hunks)packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
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(239-243)packages/apple/Sources/OpenIapModule.swift (1)
extractIapkitErrorMessage(745-769)
🪛 detekt (1.23.8)
packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt
[warning] 136-136: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 163-163: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 233-233: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🪛 SwiftLint (0.57.0)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift
[Warning] 103-103: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 107-107: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 153-153: Returning Void in a function declaration is redundant
(redundant_void_return)
⏰ 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 (5)
packages/google/openiap/src/test/java/dev/hyo/openiap/OpenIapErrorTest.kt (1)
54-57: LGTM! Deprecation handling is correct.The test correctly suppresses deprecation warnings for
InvalidReceiptand validates that it now maps toPurchaseVerificationFailed. This ensures backward compatibility while supporting the new error terminology.Also applies to: 323-323
packages/google/openiap/src/test/java/dev/hyo/openiap/PurchaseVerificationValidatorTest.kt (1)
23-223: LGTM! Comprehensive test coverage.This test file provides thorough coverage of both Google Play and IAPKit verification flows, including:
- Missing options validation
- Successful response parsing with field assertions
- Non-2xx error handling and proper exception mapping
- API key authorization header composition
- Request body content validation
The
FakeHttpURLConnectiontest double is well-implemented and captures the necessary request details for assertions. All past review comments have been addressed.packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (3)
26-81: LGTM! Google Play verification is well-implemented.The function properly:
- Validates required
androidOptionsfields- Constructs the correct Google Play API URL based on product type (subscription vs product)
- Handles HTTP errors by throwing
InvalidPurchaseVerification- Catches JSON parsing errors with appropriate fallbacks
- Maps network errors to
NetworkError
83-170: LGTM! IAPKit verification flow is solid.The function correctly:
- Validates Google payload presence (appropriate for Android)
- Constructs and sends POST requests with optional Bearer token
- Logs request/response details for debugging
- Extracts concise error messages from nested IAPKit responses
- Normalizes response state from
UPPER_SNAKE_CASEtolower-kebab-case- Handles network and parsing errors with descriptive messages
The past review comment about duplicate logging has been addressed—only
OpenIapLogcalls are present.
206-248: LGTM! Error extraction logic is well-structured.The
extractIapkitErrorMessagefunction handles multiple IAPKit error formats with appropriate fallbacks:
- Errors array format
- Nested
details.originalError(with recursive JSON parsing)- Message field (avoiding verbose nested JSON)
- Error code fallback
Static analysis flags swallowed exceptions at lines 233, but this is intentional—exceptions during error message extraction fall back to simpler formats. The function is defensive and guarantees a meaningful error message.
Based on the relevant code snippet from
OpenIapModule.swift(lines 744-768), the Android implementation correctly mirrors the Swift error extraction logic.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/gql/scripts/generate-swift-types.mjs (1)
312-341: Kotlin implementation missing legacy error code alias support found in Swift.The Swift implementation intentionally aliases
"receipt-failed"to.purchaseVerificationFailed(documented as legacy alias in tests), but the Kotlin implementation inpackages/google/openiap/src/main/kotlin/dev/hyo/openiap/Types.ktmaps these as distinct enum values:
"receipt-failed"→ErrorCode.ReceiptFailed"purchase-verification-failed"→ErrorCode.PurchaseVerificationFailedIf the backend or other platforms send
"receipt-failed"error codes, Kotlin clients will receive a different enum value than Swift/iOS clients, breaking cross-platform consistency. Add equivalent legacy alias handling in Kotlin to match the Swift behavior, or document why this intentional divergence is acceptable.
♻️ Duplicate comments (3)
packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (1)
585-676: IAPKit exception path still finishes the transaction, undermining verificationThe main flow now correctly short-circuits when
isValid == falseso failed verifications do not reachfinishTransaction—nice improvement.However, in the IAPKit branch’s
catchyou still forceisValidtotruefor “test mode”:iapStore.postStatusMessage( message = "Verification error: ${e.message}. Finishing transaction anyway for testing.", … ) // For testing: return true to continue with finishTransaction println("PurchaseFlow: [TEST MODE] Continuing with finishTransaction despite verification error") trueThis means that on network / provider errors you still acknowledge/consume the purchase, even though verification never actually succeeded. For anyone copying this example, it effectively turns verification failures into “best-effort logging only”.
To keep the behavior aligned with the rest of the flow (only finish when verification succeeds), I’d strongly recommend treating exceptions as verification failure and not finishing the transaction:
- iapStore.postStatusMessage( - message = "Verification error: ${e.message}. Finishing transaction anyway for testing.", - status = PurchaseResultStatus.Error, - productId = purchase.productId - ) - // For testing: return true to continue with finishTransaction - println("PurchaseFlow: [TEST MODE] Continuing with finishTransaction despite verification error") - true + iapStore.postStatusMessage( + message = "Verification error: ${e.message}. Purchase not acknowledged.", + status = PurchaseResultStatus.Error, + productId = purchase.productId + ) + falseWith that change, the existing
if (!isValid) { … return@LaunchedEffect }will skipfinishTransactionin both the “explicitly invalid” and “verification errored” cases, which matches the explanatory messaging and models the recommended pattern more accurately.Also applies to: 693-719
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (2)
135-147: Verification flow mostly solid; consider tightening “test-mode” IAPKit error handlingThe new verification state, UI, and LaunchedEffect flow correctly:
- Gate
finishTransactiononisValid, including for local and IAPKit paths.- Prevent re-processing via
processedPurchaseKey.- Clear premature status messages and surface verification results in
verificationResultMessage.However, the IAPKit error path still forces
isValid = trueand proceeds tofinishTransactionwith a “Finishing transaction anyway for testing” message (Lines 1361–1372). This pattern can easily be copied into production and effectively bypasses verification whenever the IAPKit call errors (network/server issues).Consider gating this branch behind an explicit debug/test flag (e.g.,
BuildConfig.DEBUG) or flipping the default to “fail closed” (do not finish on verification error) so the example teaches a safer pattern by default, with comments showing how to override in test-only builds.Also applies to: 413-558, 1269-1426
579-583: Active subscriptions now correctly limited to purchased stateFiltering
activeSubscriptionsby bothproductIdandpurchaseState == PurchaseState.Purchasedaligns this section with the rest of the screen and avoids showing pending/failed/other transient states as “active” subscriptions.
🧹 Nitpick comments (3)
packages/docs/src/pages/docs/apis.tsx (1)
1175-1343: verifyPurchaseWithProvider docs are solid; tighten props requirements & key guidanceThe multi-language signatures and examples for
verifyPurchaseWithProviderlook consistent and easy to follow. A couple of small refinements would make this even clearer:
- In the TypeScript interface,
iapkitis optional, but every example using the IAPKit provider supplies it. Consider documenting (or typing) thatiapkitis required wheneverprovider === 'iapkit'so readers don’t thinkprovideralone is sufficient.- Since
apiKeyis shown as a literal string, a brief note that production apps should source this from secure configuration or a backend (rather than hard-coding it in the client) would help nudge users toward safer integration patterns.These are docs-only tweaks; the core design and examples otherwise look good.
packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (1)
56-60: Verification enum, state, and UI wiring look good; only minor polish possibleThe new
VerificationMethodenum, state variables, and the “Purchase Verification” card are wired cleanly and make the flows (None / Local / IAPKit) very explicit. Surfacing API key configuration inline is also helpful.Two optional nits you might consider, not blockers:
isVerifyingis maintained but not currently reflected in the UI (e.g., disabling purchase buttons or showing a small progress indicator during verification). If you want to prevent double-actions while verification runs, you could plumb this into the buttonenabledstates.OpenIapLog.isEnabled = trueinside the initLaunchedEffectis perfect for the sample, but if you ever ship this example as part of a release build, gating it onBuildConfig.DEBUGwould avoid always-on verbose logging in production-like builds.Also applies to: 95-107, 119-121, 251-396
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (1)
101-105: Clean up redundantVoidreturns in fake module (SwiftLint warnings)SwiftLint flags
redundant_void_returnfor a few stubs that declare-> Voidand return(). You can simplify these to bare async/throws functions with empty bodies, e.g.:- func restorePurchases() async throws -> Void { () } + func restorePurchases() async throws { } - func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throws -> Void { () } + func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throws { } - func deepLinkToSubscriptions(_ options: DeepLinkOptions?) async throws -> Void { () } + func deepLinkToSubscriptions(_ options: DeepLinkOptions?) async throws { }This keeps the fake concise and satisfies SwiftLint.
Also applies to: 107-107, 149-153
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
packages/gql/src/generated/Types.swiftis excluded by!**/generated/**
📒 Files selected for processing (10)
.vscode/settings.json(1 hunks)packages/apple/Sources/Models/Types.swift(15 hunks)packages/apple/Sources/OpenIapModule.swift(4 hunks)packages/apple/Sources/OpenIapProtocol.swift(2 hunks)packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift(1 hunks)packages/docs/src/pages/docs/apis.tsx(2 hunks)packages/docs/src/styles/pages.css(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt(6 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt(10 hunks)packages/gql/scripts/generate-swift-types.mjs(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/docs/src/styles/pages.css
🧰 Additional context used
🧬 Code graph analysis (5)
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (2)
packages/gql/src/generated/types.ts (2)
RequestVerifyPurchaseWithIapkitProps(764-771)RequestVerifyPurchaseWithIapkitGoogleProps(759-762)packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (2)
verifyLocally(581-583)verifyWithIapkit(561-577)
packages/apple/Sources/Models/Types.swift (5)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (4)
iapkit(1814-1836)iapkit(2389-2406)verifyPurchase(2594-2594)verifyPurchaseWithProvider(2598-2598)packages/gql/src/generated/Types.kt (4)
iapkit(1879-1901)iapkit(2454-2471)verifyPurchase(2659-2659)verifyPurchaseWithProvider(2663-2663)packages/apple/Sources/OpenIapModule.swift (2)
verifyPurchase(589-592)verifyPurchaseWithProvider(594-606)packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)packages/apple/Sources/OpenIapProtocol.swift (1)
verifyPurchaseWithProvider(82-84)
packages/apple/Sources/OpenIapModule.swift (2)
packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchase(376-386)verifyPurchaseWithProvider(388-391)packages/apple/Sources/OpenIapProtocol.swift (1)
verifyPurchaseWithProvider(82-84)
packages/apple/Sources/OpenIapProtocol.swift (2)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (4)
validateReceiptIOS(117-122)validateReceipt(124-126)verifyPurchase(128-130)verifyPurchaseWithProvider(132-134)packages/apple/Tests/OpenIapTests/VerifyPurchaseTests.swift (4)
validateReceiptIOS(102-107)validateReceipt(109-111)verifyPurchase(113-115)verifyPurchaseWithProvider(117-119)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift (2)
packages/apple/Sources/OpenIapStore.swift (2)
verifyPurchaseWithProvider(388-391)verifyPurchase(376-386)packages/apple/Sources/OpenIapProtocol.swift (1)
verifyPurchaseWithProvider(82-84)
🪛 SwiftLint (0.57.0)
packages/apple/Tests/OpenIapTests/VerifyPurchaseWithProviderTests.swift
[Warning] 103-103: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 107-107: Returning Void in a function declaration is redundant
(redundant_void_return)
[Warning] 153-153: Returning Void in a function declaration is redundant
(redundant_void_return)
🔇 Additional comments (6)
.vscode/settings.json (1)
2-7: LGTM!The spell-check dictionary has been appropriately updated with the new terms introduced in this PR. The entries are alphabetically ordered and align with the IAPKit-based verification features and SKU handling being added.
packages/docs/src/pages/docs/apis.tsx (1)
169-184: DartinitConnectionexamples now match the documented config-based signatureWrapping
alternativeBillingModeAndroidinsideInitConnectionConfigvia theconfignamed parameter aligns correctly with the Dart function signature and keeps the examples consistent with the other platform snippets.packages/gql/scripts/generate-swift-types.mjs (1)
312-316: LGTM - Legacy alias mapping structure.The legacyAliases map correctly includes both kebab-case and PascalCase variants for backward compatibility.
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (1)
143-147: IAPKit API key handling and local verification helper look goodUsing a nullable
iapkitApiKeywithtakeIf { it.isNotBlank() }and a simpleverifyLocallythat checkspurchaseState == PurchaseState.Purchasedis straightforward and consistent with the rest of the codebase (e.g., PurchaseFlowScreen). No issues here.Also applies to: 1288-1291
packages/apple/Sources/Models/Types.swift (1)
34-37: VerifyPurchase / IAPKit type additions and ErrorCode mappings look consistentThe new verification-related enums/structs (IapkitPurchaseState, IapkitStore, PurchaseVerificationProvider, RequestVerifyPurchaseWithIapkit*, VerifyPurchase* types) and their wiring into ErrorCode, result unions, and handler typealiases are internally consistent and match the usage patterns in OpenIapModule/OpenIapStore. The legacy
"receipt-failed"→.purchaseVerificationFailedmapping plus updated default messages provide a reasonable compat story without breaking newer purchaseVerification* codes.Also applies to: 82-93, 162-187, 228-230, 589-605, 633-669, 654-669, 1006-1092, 1391-1394, 1537-1540, 1561-1564, 1633-1633
packages/apple/Sources/OpenIapModule.swift (1)
553-592: IAPKit-backed verification flow and VerifyPurchase wiring look correctThe iOS-side VerifyPurchase implementations are coherent:
validateReceiptIOS→performVerifyPurchaseIOSbuilds aVerifyPurchaseResultIOSfrom latest StoreKit transaction and current receipt data.verifyPurchase(_:)wraps that in the newVerifyPurchaseResultunion, andvalidateReceipt(_:)simply delegates toverifyPurchase(_:)for backward compatibility.verifyPurchaseWithProvider(_:)correctly:
- Enforces
.iapkitas the only supported provider here.- Requires
props.iapkitand surfaces missing params as.developerError.- Delegates to
verifyPurchaseWithIapkit, which:
- Uses a fixed verify URL constant.
- Restricts this module to Apple payloads (documented in comments).
- Validates payloads, sends JSON POST with optional Bearer API key, and robustly parses/normalizes responses into
RequestVerifyPurchaseWithIapkitResult.The auxiliary helpers (
buildIapkitPayload,extractIapkitErrorMessage, and the new purchaseVerification* branches indefaultMessage(for:)) are well-structured and match the data shapes defined in Types.swift.Also applies to: 594-698, 700-739, 741-767, 1345-1350
Summary by CodeRabbit
New Features
Refactor
Tests
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.