Skip to content

Conversation

hyochan
Copy link
Owner

@hyochan hyochan commented Sep 10, 2025

Upgrade OpenIAP Apple to 1.1.9 and align iOS error handling with the unified OpenIapError model. Replace legacy error type usages in Swift and add missing E_RECEIPT_FINISHED in TypeScript to stay in sync with Open IAP docs. Ensure direct string codes from native are recognized in JS.\n\nNo breaking changes. Includes https://github.com/hyodotdev/openiap-apple/releases/tag/1.1.9.

Summary by CodeRabbit

  • New Features

    • iOS: Purchase flow now supports discount offers.
    • Added a public error code to indicate a finished receipt state.
  • Bug Fixes

    • Improved and more precise error reporting (includes SKU/product context and explicit empty-SKU errors).
    • Native platform error codes are preserved when already recognized.
    • Added a user-friendly message for the finished-receipt case.
  • Chores

    • Updated iOS in-app purchase dependency to the latest patch version.

Copy link

coderabbitai bot commented Sep 10, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

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

⌛ How to resolve this issue?

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

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8170012 and 78d7063.

📒 Files selected for processing (1)
  • src/ExpoIap.types.ts (4 hunks)

Walkthrough

Bumps iOS openiap dependency; migrates Swift error handling from OpenIapFailure to OpenIapError with new constructors and SKU/receipt contexts; adds discount-offer parsing in purchase flow; introduces E_RECEIPT_FINISHED and fast-pass platform-code mapping with a user-facing message.

Changes

Cohort / File(s) Change Summary
iOS Podspec Version Bump
ios/ExpoIap.podspec
Update dependency: openiap from ~> 1.1.8~> 1.1.9.
iOS Error API Migration & Purchase Enhancements
ios/ExpoIapModule.swift
Replace OpenIapFailure usage with OpenIapError; add emptySkuList() and make(...) constructors (E_SKU_NOT_FOUND with productId, E_RECEIPT_FAILED, E_PURCHASE_ERROR); standardize catch-wrapping to OpenIapError; add discount-offer parsing (withOfferOpenIapDiscountOffer); map SKU-not-found/receipt failures to new error codes.
TypeScript Error Codes & Mapping
src/ExpoIap.types.ts
Add ErrorCode.E_RECEIPT_FINISHED; add OPENIAP_ERROR_CODE_SET fast-lookup and short-circuit ErrorCodeUtils.fromPlatformCode to passthrough valid E_* strings; update platform mappings to include E_RECEIPT_FINISHED.
User-Facing Error Mapping
src/utils/errorMapping.ts
Add user-friendly message for ErrorCode.E_RECEIPT_FINISHED → "Receipt already finished".

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant JS as App (JS)
  participant TS as ExpoIap (TS)
  participant iOS as ExpoIapModule (iOS)
  participant Store as App Store

  JS->>TS: requestPurchase({ sku, withOffer? })
  TS->>iOS: requestPurchase(sku, offer?)
  iOS->>iOS: Parse withOffer → OpenIapDiscountOffer (optional)
  iOS->>Store: Initiate purchase (sku, offer?)
  alt Success
    Store-->>iOS: Transaction
    iOS-->>TS: Success result
    TS-->>JS: Resolve
  else Error
    Store-->>iOS: Error
    iOS->>iOS: Wrap/Map → OpenIapError.make(...) / emptySkuList()
    iOS-->>TS: Error { code, message }
    TS->>TS: fromPlatformCode: passthrough E_* if valid
    TS-->>JS: Reject with standardized ErrorCode
  end
Loading
sequenceDiagram
  autonumber
  participant iOS as ExpoIapModule (iOS)
  participant TS as ExpoIap (TS)
  participant UI as UI Layer

  iOS-->>TS: Error { code: "E_RECEIPT_FINISHED" }
  TS->>TS: fromPlatformCode recognizes valid E_* → passthrough
  TS->>UI: getUserFriendlyErrorMessage("E_RECEIPT_FINISHED")
  UI-->>UI: "Receipt already finished"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

፦ refactor, 📱 iOS

Poem

I hop through git with carrot cheer,
Errors renamed, the path is clear.
Offers tucked in a tiny sleeve,
Receipts that finish, none to grieve.
Thump-thump — I ship it with a twitchy ear. 🐰✨

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title Check ✅ Passed The title succinctly captures the primary changes—upgrading the OpenIAP dependency to version 1.1.9 and aligning error codes—which directly reflect the main objectives of the pull request without unnecessary detail. It is clear, concise, and focused on the most significant update, making it easy for reviewers to understand the intent at a glance.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/openiap-1.1.9

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @hyochan, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness and consistency of error handling within the application's in-app purchase module. It involves updating a core iOS dependency and standardizing error reporting mechanisms across both Swift and TypeScript components, ensuring that error codes are uniformly recognized and handled throughout the system.

Highlights

  • OpenIAP Dependency Upgrade: The OpenIAP dependency for iOS has been upgraded from version 1.1.8 to 1.1.9 in the podspec.
  • iOS Error Handling Unification: Legacy OpenIapFailure error types in Swift have been replaced with the unified OpenIapError model, ensuring consistent error reporting and better alignment with the new version.
  • New Error Code Introduction: The E_RECEIPT_FINISHED error code has been added to the TypeScript error definitions and its corresponding user-friendly message mapping.
  • Native Error Code Recognition: The JavaScript error utility now directly recognizes standardized string error codes (e.g., E_...) sent from native modules, improving error propagation and consistency.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

codecov bot commented Sep 10, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 43.82%. Comparing base (c3745d7) to head (78d7063).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #191   +/-   ##
=======================================
  Coverage   43.82%   43.82%           
=======================================
  Files           6        6           
  Lines         632      632           
  Branches      274      274           
=======================================
  Hits          277      277           
  Misses        350      350           
  Partials        5        5           
Flag Coverage Δ
example 43.82% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c3745d7...78d7063. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request successfully upgrades the OpenIAP dependency and aligns error handling across the module. The migration to the unified OpenIapError model in the Swift code is a significant improvement for consistency and robustness. The corresponding changes in TypeScript to handle new error codes and recognize standardized string codes from the native side are well-implemented. Overall, these changes enhance the library's maintainability and error handling capabilities. I have one minor suggestion for a performance optimization.

@hyochan hyochan added 🚽 migration Activities due to changes in framework 🧹 chore labels Sep 10, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (3)
src/ExpoIap.types.ts (3)

160-195: Add Android mapping for E_RECEIPT_FINISHED to avoid falling back to E_UNKNOWN.
Without this, toPlatformCode(E_RECEIPT_FINISHED, 'android') returns 'E_UNKNOWN'.

Apply:

   android: {
     [ErrorCode.E_UNKNOWN]: 'E_UNKNOWN',
@@
     [ErrorCode.E_RECEIPT_FAILED]: 'E_RECEIPT_FAILED',
+    [ErrorCode.E_RECEIPT_FINISHED]: 'E_RECEIPT_FINISHED',
     [ErrorCode.E_RECEIPT_FINISHED_FAILED]: 'E_RECEIPT_FINISHED_FAILED',

333-342: Android fallback should return the standardized code string when unmapped.
Prevents accidental downgrades to 'E_UNKNOWN' for newly added codes.

   toPlatformCode: (
     errorCode: ErrorCode,
     platform: 'ios' | 'android',
   ): string | number => {
     if (platform === 'ios') {
       const native = NATIVE_ERROR_CODES?.[errorCode];
       if (native !== undefined) return native;
     }
-    const mapping = ErrorCodeMapping[platform] as Record<
-      ErrorCode,
-      string | number
-    >;
-    return mapping[errorCode] ?? (platform === 'ios' ? 0 : 'E_UNKNOWN');
+    const mapping = ErrorCodeMapping[platform] as Record<ErrorCode, string | number>;
+    if (platform === 'android') {
+      // Fallback to standardized string to avoid drift
+      return (mapping[errorCode] as string | undefined) ?? errorCode;
+    }
+    return mapping[errorCode] ?? 0;

350-356: Validate against dynamic iOS native codes too.
Currently isValidForPlatform ignores NATIVE_ERROR_CODES, so new iOS codes read from native are marked invalid.

   isValidForPlatform: (
     errorCode: ErrorCode,
     platform: 'ios' | 'android',
   ): boolean => {
-    return errorCode in ErrorCodeMapping[platform];
+    if (platform === 'ios') {
+      return (
+        errorCode in ErrorCodeMapping.ios ||
+        NATIVE_ERROR_CODES?.[errorCode] !== undefined
+      );
+    }
+    return errorCode in ErrorCodeMapping.android;
   },
🧹 Nitpick comments (3)
src/ExpoIap.types.ts (1)

247-263: Replace any with a typed native error shape for stricter safety.
Keeps to “no any types” guidance and clarifies expected payload from native.

-export class PurchaseError implements Error {
+type NativePurchaseErrorData = {
+  message?: string;
+  responseCode?: number;
+  debugMessage?: string;
+  code?: string | number;
+  productId?: string;
+};
+
+export class PurchaseError implements Error {
@@
-  static fromPlatformError(
-    errorData: any,
+  static fromPlatformError(
+    errorData: NativePurchaseErrorData,
     platform: 'ios' | 'android',
   ): PurchaseError {
src/utils/errorMapping.ts (1)

77-79: Added message for E_RECEIPT_FINISHED — good.
Consider also covering E_RECEIPT_FINISHED_FAILED for completeness.

   case ErrorCode.E_RECEIPT_FINISHED:
     return 'Receipt already finished';
+  case ErrorCode.E_RECEIPT_FINISHED_FAILED:
+    return 'Failed to finish receipt';
ios/ExpoIapModule.swift (1)

146-151: Missing 'sku' mapped to E_PURCHASE_ERROR — acceptable; consider E_DEVELOPER_ERROR.
Semantically “invalid arguments” could map to E_DEVELOPER_ERROR, but not blocking.

-throw OpenIapError.make(code: OpenIapError.E_PURCHASE_ERROR, message: "Missing required 'sku'")
+throw OpenIapError.make(code: OpenIapError.E_DEVELOPER_ERROR, message: "Missing required 'sku'")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3745d7 and 5e1641a.

📒 Files selected for processing (4)
  • ios/ExpoIap.podspec (1 hunks)
  • ios/ExpoIapModule.swift (6 hunks)
  • src/ExpoIap.types.ts (2 hunks)
  • src/utils/errorMapping.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Platform-specific functions must use suffixes: IOS -> functionNameIOS, Android -> functionNameAndroid
Cross-platform functions must not use platform suffixes and should abstract differences internally (e.g., via Platform.select)
Explicit type definitions for all public APIs
Provide function overloads to improve IntelliSense for public APIs when appropriate
Use discriminated unions for platform-specific types and conditional returns
No any types in production code; maintain strict type safety
Naming: PascalCase for interfaces, types, enums, and classes
Naming: camelCase for functions, variables, methods, and properties
Use a result-style error handling pattern returning {success: true|false, ...}
Use Platform.select to handle platform differences in cross-platform code
Provide TypeScript-first API with unified structures across iOS and Android
Use type guards (e.g., isPurchaseResult) for advanced runtime type checks
Add JSDoc comments for all public APIs; document why, platform differences, and examples
Receipt validation should use platform-specific parameters (iOS: SKU only; Android: packageName, productToken, accessToken)

**/*.{ts,tsx}: Platform-specific functions must use platform suffixes: functionNameIOS() for iOS and functionNameAndroid() for Android
Cross-platform functions that abstract platform differences should not have platform suffixes and should use Platform.select()
Use platform-specific type names with suffixes (e.g., ProductIOS, ProductAndroid, PurchaseErrorIOS)
Use cross-platform type names without suffixes (e.g., Product, Purchase, PurchaseError) when types are shared
Include proper error handling in implementations (e.g., try/catch with meaningful errors) for public APIs
Add JSDoc comments for all public API functions and types
Use Platform.select({...}) for platform branching in implementation code
Receipt validation must use platform-specific parameters: iOS only needs SKU; Android requires packageName, productToken, and a...

Files:

  • src/utils/errorMapping.ts
  • src/ExpoIap.types.ts
src/**/*.ts

📄 CodeRabbit inference engine (.cursorrules)

File names should be kebab-case (except React component files)

Files:

  • src/utils/errorMapping.ts
  • src/ExpoIap.types.ts
ios/ExpoIap.podspec

📄 CodeRabbit inference engine (CLAUDE.md)

Do not modify platform versions in ios/ExpoIap.podspec; iOS and tvOS MUST remain at 13.4

Files:

  • ios/ExpoIap.podspec
src/ExpoIap.types.ts

📄 CodeRabbit inference engine (.cursorrules)

Core public type definitions should be in src/ExpoIap.types.ts

Files:

  • src/ExpoIap.types.ts
ios/**/*.swift

📄 CodeRabbit inference engine (CLAUDE.md)

ios/**/*.swift: Use status.loadings.purchasing.contains(productId) to check if a specific product is being purchased
Use status.isLoading to check if any operation is in progress

Files:

  • ios/ExpoIapModule.swift
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/types/ExpoIapIos.types.ts : iOS-specific type definitions belong in src/types/ExpoIapIos.types.ts
Learnt from: CR
PR: hyochan/expo-iap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T03:20:18.305Z
Learning: Applies to **/*.{ts,tsx} : General IAP types must use Iap prefix (e.g., IapPurchase; not IAPPurchase)
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/ExpoIapModule.ts : Native module interface should be in src/ExpoIapModule.ts
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/types/ExpoIapAndroid.types.ts : Android-specific type definitions belong in src/types/ExpoIapAndroid.types.ts
Learnt from: CR
PR: hyochan/expo-iap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T03:20:18.305Z
Learning: Applies to ios/ExpoIap.podspec : Do not modify platform versions in ios/ExpoIap.podspec; iOS and tvOS MUST remain at 13.4
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/ExpoIap.types.ts : Core public type definitions should be in src/ExpoIap.types.ts
📚 Learning: 2025-09-08T03:20:18.305Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T03:20:18.305Z
Learning: Applies to ios/ExpoIap.podspec : Do not modify platform versions in ios/ExpoIap.podspec; iOS and tvOS MUST remain at 13.4

Applied to files:

  • ios/ExpoIap.podspec
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/ExpoIap.types.ts : Core public type definitions should be in src/ExpoIap.types.ts

Applied to files:

  • src/ExpoIap.types.ts
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/types/ExpoIapIos.types.ts : iOS-specific type definitions belong in src/types/ExpoIapIos.types.ts

Applied to files:

  • src/ExpoIap.types.ts
  • ios/ExpoIapModule.swift
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/types/ExpoIapAndroid.types.ts : Android-specific type definitions belong in src/types/ExpoIapAndroid.types.ts

Applied to files:

  • src/ExpoIap.types.ts
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/ExpoIapModule.ts : Native module interface should be in src/ExpoIapModule.ts

Applied to files:

  • src/ExpoIap.types.ts
  • ios/ExpoIapModule.swift
📚 Learning: 2025-09-08T03:20:18.305Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T03:20:18.305Z
Learning: Implementations must conform to OpenIAP specification (APIs, Types, Events, Errors) per official docs

Applied to files:

  • ios/ExpoIapModule.swift
🔇 Additional comments (7)
ios/ExpoIap.podspec (1)

24-24: Dependency bump to 1.1.9 looks good; platform mins unchanged (13.4) per policy.
No other podspec concerns.

src/ExpoIap.types.ts (1)

298-303: Short-circuit for standardized string codes — LGTM.
Accepting 'E_*' strings directly reduces drift between native and TS.

ios/ExpoIapModule.swift (5)

108-111: Empty SKU list -> OpenIapError.emptySkuList() — LGTM.
Aligned with OpenIAP 1.1.9 error model.


199-204: Re-throw native OpenIapError unchanged; wrap others — LGTM.
Prevents double-wrapping and preserves native code/message.


281-283: Receipt validation fallback -> E_RECEIPT_FAILED — LGTM.
Consistent with TS ErrorCode.


407-409: currentEntitlementIOS -> E_SKU_NOT_FOUND on failure — LGTM.
Adds useful productId context to the error.


419-421: latestTransactionIOS -> E_SKU_NOT_FOUND on failure — LGTM.
Consistent with entitlement path.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/ExpoIap.types.ts (2)

99-101: E_RECEIPT_FINISHED enum added — LGTM

Enum addition is correct and matches PR intent.


147-148: Missing mapping flagged earlier appears resolved

Prior comment about adding E_RECEIPT_FINISHED to platform mappings is now addressed here. Nice.

Also applies to: 174-175

🧹 Nitpick comments (3)
src/ExpoIap.types.ts (3)

128-132: Good call on using a Set for O(1) lookups; tighten the typing

Make it a ReadonlySet to preserve type safety and prevent mutation.

-// Fast lookup set for validating standardized error code strings
-const OPENIAP_ERROR_CODE_SET: Set<string> = new Set(
-  Object.values(ErrorCode) as string[],
-);
+// Fast lookup set for validating standardized error code strings
+const OPENIAP_ERROR_CODE_SET: ReadonlySet<ErrorCode> = new Set(
+  Object.values(ErrorCode) as ErrorCode[],
+);

Optionally add a type guard (outside this range) to avoid casts:

export const isErrorCode = (v: unknown): v is ErrorCode =>
  typeof v === 'string' && (OPENIAP_ERROR_CODE_SET as Set<ErrorCode>).has(v as ErrorCode);

305-310: Fast-path for standardized string codes — LGTM; minor type-safety nit

This is correct and efficient. If adding the type guard above, you can drop the cast.

-    if (typeof platformCode === 'string' && platformCode.startsWith('E_')) {
-      if (OPENIAP_ERROR_CODE_SET.has(platformCode)) {
-        return platformCode as ErrorCode;
-      }
-    }
+    if (isErrorCode(platformCode)) {
+      return platformCode;
+    }

291-293: Return type should include number for iOS native codes

Native iOS codes are numeric; widen the return type.

-  getNativeErrorCode: (errorCode: ErrorCode): string => {
+  getNativeErrorCode: (errorCode: ErrorCode): string | number => {
     return NATIVE_ERROR_CODES[errorCode] || errorCode;
   },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93238d2 and 8170012.

📒 Files selected for processing (1)
  • src/ExpoIap.types.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Platform-specific functions must use suffixes: IOS -> functionNameIOS, Android -> functionNameAndroid
Cross-platform functions must not use platform suffixes and should abstract differences internally (e.g., via Platform.select)
Explicit type definitions for all public APIs
Provide function overloads to improve IntelliSense for public APIs when appropriate
Use discriminated unions for platform-specific types and conditional returns
No any types in production code; maintain strict type safety
Naming: PascalCase for interfaces, types, enums, and classes
Naming: camelCase for functions, variables, methods, and properties
Use a result-style error handling pattern returning {success: true|false, ...}
Use Platform.select to handle platform differences in cross-platform code
Provide TypeScript-first API with unified structures across iOS and Android
Use type guards (e.g., isPurchaseResult) for advanced runtime type checks
Add JSDoc comments for all public APIs; document why, platform differences, and examples
Receipt validation should use platform-specific parameters (iOS: SKU only; Android: packageName, productToken, accessToken)

**/*.{ts,tsx}: Platform-specific functions must use platform suffixes: functionNameIOS() for iOS and functionNameAndroid() for Android
Cross-platform functions that abstract platform differences should not have platform suffixes and should use Platform.select()
Use platform-specific type names with suffixes (e.g., ProductIOS, ProductAndroid, PurchaseErrorIOS)
Use cross-platform type names without suffixes (e.g., Product, Purchase, PurchaseError) when types are shared
Include proper error handling in implementations (e.g., try/catch with meaningful errors) for public APIs
Add JSDoc comments for all public API functions and types
Use Platform.select({...}) for platform branching in implementation code
Receipt validation must use platform-specific parameters: iOS only needs SKU; Android requires packageName, productToken, and a...

Files:

  • src/ExpoIap.types.ts
src/ExpoIap.types.ts

📄 CodeRabbit inference engine (.cursorrules)

Core public type definitions should be in src/ExpoIap.types.ts

Files:

  • src/ExpoIap.types.ts
src/**/*.ts

📄 CodeRabbit inference engine (.cursorrules)

File names should be kebab-case (except React component files)

Files:

  • src/ExpoIap.types.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
PR: hyochan/expo-iap#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T03:20:18.305Z
Learning: Implementations must conform to OpenIAP specification (APIs, Types, Events, Errors) per official docs
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/types/ExpoIapAndroid.types.ts : Android-specific type definitions belong in src/types/ExpoIapAndroid.types.ts

Applied to files:

  • src/ExpoIap.types.ts
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/types/ExpoIapIos.types.ts : iOS-specific type definitions belong in src/types/ExpoIapIos.types.ts

Applied to files:

  • src/ExpoIap.types.ts
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to src/ExpoIap.types.ts : Core public type definitions should be in src/ExpoIap.types.ts

Applied to files:

  • src/ExpoIap.types.ts
📚 Learning: 2025-08-27T12:21:45.068Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-27T12:21:45.068Z
Learning: Applies to **/*.{ts,tsx} : Receipt validation must use platform-specific parameters: iOS only needs SKU; Android requires packageName, productToken, and accessToken (use validateReceiptIOS/validateReceiptAndroid accordingly)

Applied to files:

  • src/ExpoIap.types.ts
📚 Learning: 2025-08-27T12:21:23.121Z
Learnt from: CR
PR: hyochan/expo-iap#0
File: .cursorrules:0-0
Timestamp: 2025-08-27T12:21:23.121Z
Learning: Applies to **/*.{ts,tsx} : Receipt validation should use platform-specific parameters (iOS: SKU only; Android: packageName, productToken, accessToken)

Applied to files:

  • src/ExpoIap.types.ts
🔇 Additional comments (2)
src/ExpoIap.types.ts (2)

174-176: Android mapping added for E_RECEIPT_FINISHED — LGTM

String code passthrough aligns with Android side and your JS handling.


147-149: iOS mapping for E_RECEIPT_FINISHED — confirm native parity
Mapping to 24 is in ErrorCodeMapping. I couldn’t locate the iOS native definition in this repo—please verify that ExpoIapModule.ERROR_CODES.E_RECEIPT_FINISHED is 24 in the iOS implementation.

@hyochan hyochan changed the title Upgrade OpenIAP to 1.1.9 and align error codes chore(ios): upgrade OpenIAP to 1.1.9 and align error codes Sep 10, 2025
@hyochan hyochan added the 📱 iOS Related to iOS label Sep 10, 2025
@hyochan hyochan merged commit d11c5a5 into main Sep 10, 2025
6 of 7 checks passed
@hyochan hyochan deleted the chore/openiap-1.1.9 branch September 10, 2025 04:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🧹 chore 📱 iOS Related to iOS 🚽 migration Activities due to changes in framework
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant