expo-iap 3.0.7 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CLAUDE.md +14 -2
  2. package/CONTRIBUTING.md +19 -0
  3. package/README.md +18 -6
  4. package/android/build.gradle +24 -1
  5. package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +69 -0
  6. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +190 -59
  7. package/build/index.d.ts +32 -111
  8. package/build/index.d.ts.map +1 -1
  9. package/build/index.js +198 -243
  10. package/build/index.js.map +1 -1
  11. package/build/modules/android.d.ts +7 -12
  12. package/build/modules/android.d.ts.map +1 -1
  13. package/build/modules/android.js +15 -12
  14. package/build/modules/android.js.map +1 -1
  15. package/build/modules/ios.d.ts +35 -36
  16. package/build/modules/ios.d.ts.map +1 -1
  17. package/build/modules/ios.js +101 -35
  18. package/build/modules/ios.js.map +1 -1
  19. package/build/types.d.ts +107 -82
  20. package/build/types.d.ts.map +1 -1
  21. package/build/types.js +1 -0
  22. package/build/types.js.map +1 -1
  23. package/build/useIAP.d.ts +7 -12
  24. package/build/useIAP.d.ts.map +1 -1
  25. package/build/useIAP.js +49 -23
  26. package/build/useIAP.js.map +1 -1
  27. package/build/utils/errorMapping.d.ts +32 -23
  28. package/build/utils/errorMapping.d.ts.map +1 -1
  29. package/build/utils/errorMapping.js +117 -22
  30. package/build/utils/errorMapping.js.map +1 -1
  31. package/ios/ExpoIap.podspec +3 -2
  32. package/ios/ExpoIapHelper.swift +96 -0
  33. package/ios/ExpoIapLog.swift +127 -0
  34. package/ios/ExpoIapModule.swift +218 -340
  35. package/openiap-versions.json +5 -0
  36. package/package.json +2 -2
  37. package/plugin/build/withIAP.js +6 -4
  38. package/plugin/src/withIAP.ts +14 -4
  39. package/scripts/update-types.mjs +20 -1
  40. package/src/index.ts +280 -356
  41. package/src/modules/android.ts +25 -23
  42. package/src/modules/ios.ts +138 -48
  43. package/src/types.ts +139 -91
  44. package/src/useIAP.ts +91 -58
  45. package/src/utils/errorMapping.ts +203 -23
  46. package/.copilot-instructions.md +0 -321
  47. package/.cursorrules +0 -321
  48. package/build/purchase-error.d.ts +0 -67
  49. package/build/purchase-error.d.ts.map +0 -1
  50. package/build/purchase-error.js +0 -166
  51. package/build/purchase-error.js.map +0 -1
  52. package/src/purchase-error.ts +0 -265
@@ -1,8 +1,123 @@
1
1
  /**
2
- * Error mapping utilities for expo-iap
3
- * Provides helper functions for handling platform-specific errors
2
+ * Error mapping utilities for expo-iap.
3
+ * Provides helpers for working with platform-specific error codes
4
+ * and constructing structured purchase errors.
4
5
  */
6
+ import { NATIVE_ERROR_CODES } from '../ExpoIapModule';
5
7
  import { ErrorCode } from '../types';
8
+ const toStandardizedCode = (errorCode) => errorCode.startsWith('E_') ? errorCode : `E_${errorCode}`;
9
+ const normalizePlatform = (platform) => typeof platform === 'string' && platform.toLowerCase() === 'ios'
10
+ ? 'ios'
11
+ : 'android';
12
+ const COMMON_ERROR_CODE_MAP = {
13
+ [ErrorCode.Unknown]: toStandardizedCode(ErrorCode.Unknown),
14
+ [ErrorCode.UserCancelled]: toStandardizedCode(ErrorCode.UserCancelled),
15
+ [ErrorCode.UserError]: toStandardizedCode(ErrorCode.UserError),
16
+ [ErrorCode.ItemUnavailable]: toStandardizedCode(ErrorCode.ItemUnavailable),
17
+ [ErrorCode.RemoteError]: toStandardizedCode(ErrorCode.RemoteError),
18
+ [ErrorCode.NetworkError]: toStandardizedCode(ErrorCode.NetworkError),
19
+ [ErrorCode.ServiceError]: toStandardizedCode(ErrorCode.ServiceError),
20
+ [ErrorCode.ReceiptFailed]: toStandardizedCode(ErrorCode.ReceiptFailed),
21
+ [ErrorCode.ReceiptFinished]: toStandardizedCode(ErrorCode.ReceiptFinished),
22
+ [ErrorCode.ReceiptFinishedFailed]: toStandardizedCode(ErrorCode.ReceiptFinishedFailed),
23
+ [ErrorCode.NotPrepared]: toStandardizedCode(ErrorCode.NotPrepared),
24
+ [ErrorCode.NotEnded]: toStandardizedCode(ErrorCode.NotEnded),
25
+ [ErrorCode.AlreadyOwned]: toStandardizedCode(ErrorCode.AlreadyOwned),
26
+ [ErrorCode.DeveloperError]: toStandardizedCode(ErrorCode.DeveloperError),
27
+ [ErrorCode.BillingResponseJsonParseError]: toStandardizedCode(ErrorCode.BillingResponseJsonParseError),
28
+ [ErrorCode.DeferredPayment]: toStandardizedCode(ErrorCode.DeferredPayment),
29
+ [ErrorCode.Interrupted]: toStandardizedCode(ErrorCode.Interrupted),
30
+ [ErrorCode.IapNotAvailable]: toStandardizedCode(ErrorCode.IapNotAvailable),
31
+ [ErrorCode.PurchaseError]: toStandardizedCode(ErrorCode.PurchaseError),
32
+ [ErrorCode.SyncError]: toStandardizedCode(ErrorCode.SyncError),
33
+ [ErrorCode.TransactionValidationFailed]: toStandardizedCode(ErrorCode.TransactionValidationFailed),
34
+ [ErrorCode.ActivityUnavailable]: toStandardizedCode(ErrorCode.ActivityUnavailable),
35
+ [ErrorCode.AlreadyPrepared]: toStandardizedCode(ErrorCode.AlreadyPrepared),
36
+ [ErrorCode.Pending]: toStandardizedCode(ErrorCode.Pending),
37
+ [ErrorCode.ConnectionClosed]: toStandardizedCode(ErrorCode.ConnectionClosed),
38
+ [ErrorCode.InitConnection]: toStandardizedCode(ErrorCode.InitConnection),
39
+ [ErrorCode.ServiceDisconnected]: toStandardizedCode(ErrorCode.ServiceDisconnected),
40
+ [ErrorCode.QueryProduct]: toStandardizedCode(ErrorCode.QueryProduct),
41
+ [ErrorCode.SkuNotFound]: toStandardizedCode(ErrorCode.SkuNotFound),
42
+ [ErrorCode.SkuOfferMismatch]: toStandardizedCode(ErrorCode.SkuOfferMismatch),
43
+ [ErrorCode.ItemNotOwned]: toStandardizedCode(ErrorCode.ItemNotOwned),
44
+ [ErrorCode.BillingUnavailable]: toStandardizedCode(ErrorCode.BillingUnavailable),
45
+ [ErrorCode.FeatureNotSupported]: toStandardizedCode(ErrorCode.FeatureNotSupported),
46
+ [ErrorCode.EmptySkuList]: toStandardizedCode(ErrorCode.EmptySkuList),
47
+ };
48
+ export const ErrorCodeMapping = {
49
+ ios: COMMON_ERROR_CODE_MAP,
50
+ android: COMMON_ERROR_CODE_MAP,
51
+ };
52
+ const OPENIAP_ERROR_CODE_SET = new Set(Object.values(ErrorCode).map((code) => toStandardizedCode(code)));
53
+ export const createPurchaseError = (props) => {
54
+ const error = new Error(props.message);
55
+ error.name = '[expo-iap]: PurchaseError';
56
+ error.responseCode = props.responseCode;
57
+ error.debugMessage = props.debugMessage;
58
+ error.code = props.code;
59
+ error.productId = props.productId;
60
+ error.platform = props.platform;
61
+ return error;
62
+ };
63
+ export const createPurchaseErrorFromPlatform = (errorData, platform) => {
64
+ const normalizedPlatform = normalizePlatform(platform);
65
+ const errorCode = errorData.code
66
+ ? ErrorCodeUtils.fromPlatformCode(errorData.code, normalizedPlatform)
67
+ : ErrorCode.Unknown;
68
+ return createPurchaseError({
69
+ message: errorData.message ?? 'Unknown error occurred',
70
+ responseCode: errorData.responseCode,
71
+ debugMessage: errorData.debugMessage,
72
+ code: errorCode,
73
+ productId: errorData.productId,
74
+ platform,
75
+ });
76
+ };
77
+ export const ErrorCodeUtils = {
78
+ getNativeErrorCode: (errorCode) => {
79
+ const standardized = toStandardizedCode(errorCode);
80
+ return (NATIVE_ERROR_CODES[standardized] ?? standardized);
81
+ },
82
+ fromPlatformCode: (platformCode, _platform) => {
83
+ if (typeof platformCode === 'string' && platformCode.startsWith('E_')) {
84
+ if (OPENIAP_ERROR_CODE_SET.has(platformCode)) {
85
+ const match = Object.entries(COMMON_ERROR_CODE_MAP).find(([, value]) => value === platformCode);
86
+ if (match) {
87
+ return match[0];
88
+ }
89
+ }
90
+ }
91
+ for (const [standardized, nativeCode] of Object.entries((NATIVE_ERROR_CODES || {}))) {
92
+ if (nativeCode === platformCode &&
93
+ OPENIAP_ERROR_CODE_SET.has(standardized)) {
94
+ const match = Object.entries(COMMON_ERROR_CODE_MAP).find(([, mappedCode]) => mappedCode === standardized);
95
+ if (match) {
96
+ return match[0];
97
+ }
98
+ }
99
+ }
100
+ for (const [errorCode, mappedCode] of Object.entries(COMMON_ERROR_CODE_MAP)) {
101
+ if (mappedCode === platformCode) {
102
+ return errorCode;
103
+ }
104
+ }
105
+ return ErrorCode.Unknown;
106
+ },
107
+ toPlatformCode: (errorCode, _platform) => {
108
+ const standardized = toStandardizedCode(errorCode);
109
+ const native = NATIVE_ERROR_CODES[standardized];
110
+ return native ?? COMMON_ERROR_CODE_MAP[errorCode] ?? 'E_UNKNOWN';
111
+ },
112
+ isValidForPlatform: (errorCode, platform) => {
113
+ const standardized = toStandardizedCode(errorCode);
114
+ if (NATIVE_ERROR_CODES[standardized] !==
115
+ undefined) {
116
+ return true;
117
+ }
118
+ return standardized in ErrorCodeMapping[normalizePlatform(platform)];
119
+ },
120
+ };
6
121
  const ERROR_CODES = new Set(Object.values(ErrorCode));
7
122
  const normalizeErrorCode = (code) => {
8
123
  if (!code) {
@@ -28,19 +143,9 @@ function extractCode(error) {
28
143
  }
29
144
  return undefined;
30
145
  }
31
- /**
32
- * Checks if an error is a user cancellation
33
- * @param error Error object or error code
34
- * @returns True if the error represents user cancellation
35
- */
36
146
  export function isUserCancelledError(error) {
37
147
  return extractCode(error) === ErrorCode.UserCancelled;
38
148
  }
39
- /**
40
- * Checks if an error is related to network connectivity
41
- * @param error Error object or error code
42
- * @returns True if the error is network-related
43
- */
44
149
  export function isNetworkError(error) {
45
150
  const networkErrors = [
46
151
  ErrorCode.NetworkError,
@@ -52,11 +157,6 @@ export function isNetworkError(error) {
52
157
  const code = extractCode(error);
53
158
  return !!code && networkErrors.includes(code);
54
159
  }
55
- /**
56
- * Checks if an error is recoverable (user can retry)
57
- * @param error Error object or error code
58
- * @returns True if the error is potentially recoverable
59
- */
60
160
  export function isRecoverableError(error) {
61
161
  const recoverableErrors = [
62
162
  ErrorCode.NetworkError,
@@ -71,11 +171,6 @@ export function isRecoverableError(error) {
71
171
  const code = extractCode(error);
72
172
  return !!code && recoverableErrors.includes(code);
73
173
  }
74
- /**
75
- * Gets a user-friendly error message for display
76
- * @param error Error object or error code
77
- * @returns User-friendly error message
78
- */
79
174
  export function getUserFriendlyErrorMessage(error) {
80
175
  const errorCode = extractCode(error);
81
176
  switch (errorCode) {
@@ -1 +1 @@
1
- {"version":3,"file":"errorMapping.js","sourceRoot":"","sources":["../../src/utils/errorMapping.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Error mapping utilities for expo-iap\n * Provides helper functions for handling platform-specific errors\n */\n\nimport {ErrorCode} from '../types';\n\ntype ErrorLike = string | {code?: ErrorCode | string; message?: string};\n\nconst ERROR_CODES = new Set<string>(Object.values(ErrorCode));\n\nconst normalizeErrorCode = (code?: string | null): string | undefined => {\n if (!code) {\n return undefined;\n }\n\n if (ERROR_CODES.has(code)) {\n return code;\n }\n\n if (code.startsWith('E_')) {\n const trimmed = code.substring(2);\n if (ERROR_CODES.has(trimmed)) {\n return trimmed;\n }\n }\n\n return code;\n};\n\nfunction extractCode(error: unknown): string | undefined {\n if (typeof error === 'string') {\n return normalizeErrorCode(error);\n }\n\n if (error && typeof error === 'object' && 'code' in error) {\n return normalizeErrorCode((error as {code?: string}).code);\n }\n\n return undefined;\n}\n\n/**\n * Checks if an error is a user cancellation\n * @param error Error object or error code\n * @returns True if the error represents user cancellation\n */\nexport function isUserCancelledError(error: unknown): boolean {\n return extractCode(error) === ErrorCode.UserCancelled;\n}\n\n/**\n * Checks if an error is related to network connectivity\n * @param error Error object or error code\n * @returns True if the error is network-related\n */\nexport function isNetworkError(error: unknown): boolean {\n const networkErrors: ErrorCode[] = [\n ErrorCode.NetworkError,\n ErrorCode.RemoteError,\n ErrorCode.ServiceError,\n ErrorCode.ServiceDisconnected,\n ErrorCode.BillingUnavailable,\n ];\n\n const code = extractCode(error);\n return !!code && (networkErrors as string[]).includes(code);\n}\n\n/**\n * Checks if an error is recoverable (user can retry)\n * @param error Error object or error code\n * @returns True if the error is potentially recoverable\n */\nexport function isRecoverableError(error: unknown): boolean {\n const recoverableErrors: ErrorCode[] = [\n ErrorCode.NetworkError,\n ErrorCode.RemoteError,\n ErrorCode.ServiceError,\n ErrorCode.Interrupted,\n ErrorCode.ServiceDisconnected,\n ErrorCode.BillingUnavailable,\n ErrorCode.QueryProduct,\n ErrorCode.InitConnection,\n ];\n\n const code = extractCode(error);\n return !!code && (recoverableErrors as string[]).includes(code);\n}\n\n/**\n * Gets a user-friendly error message for display\n * @param error Error object or error code\n * @returns User-friendly error message\n */\nexport function getUserFriendlyErrorMessage(error: ErrorLike): string {\n const errorCode = extractCode(error);\n\n switch (errorCode) {\n case ErrorCode.UserCancelled:\n return 'Purchase was cancelled by user';\n case ErrorCode.NetworkError:\n return 'Network connection error. Please check your internet connection and try again.';\n case ErrorCode.ReceiptFinished:\n return 'Receipt already finished';\n case ErrorCode.ServiceDisconnected:\n return 'Billing service disconnected. Please try again.';\n case ErrorCode.BillingUnavailable:\n return 'Billing is unavailable on this device or account.';\n case ErrorCode.ItemUnavailable:\n return 'This item is not available for purchase';\n case ErrorCode.ItemNotOwned:\n return \"You don't own this item\";\n case ErrorCode.AlreadyOwned:\n return 'You already own this item';\n case ErrorCode.SkuNotFound:\n return 'Requested product could not be found';\n case ErrorCode.SkuOfferMismatch:\n return 'Selected offer does not match the SKU';\n case ErrorCode.DeferredPayment:\n return 'Payment is pending approval';\n case ErrorCode.NotPrepared:\n return 'In-app purchase is not ready. Please try again later.';\n case ErrorCode.ServiceError:\n return 'Store service error. Please try again later.';\n case ErrorCode.FeatureNotSupported:\n return 'This feature is not supported on this device.';\n case ErrorCode.TransactionValidationFailed:\n return 'Transaction could not be verified';\n case ErrorCode.ReceiptFailed:\n return 'Receipt processing failed';\n case ErrorCode.EmptySkuList:\n return 'No product IDs provided';\n case ErrorCode.InitConnection:\n return 'Failed to initialize billing connection';\n case ErrorCode.QueryProduct:\n return 'Failed to query products. Please try again later.';\n default: {\n if (error && typeof error === 'object' && 'message' in error) {\n return (\n (error as {message?: string}).message ??\n 'An unexpected error occurred'\n );\n }\n return 'An unexpected error occurred';\n }\n }\n}\n"]}
1
+ {"version":3,"file":"errorMapping.js","sourceRoot":"","sources":["../../src/utils/errorMapping.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Error mapping utilities for expo-iap.\n * Provides helpers for working with platform-specific error codes\n * and constructing structured purchase errors.\n */\n\nimport {NATIVE_ERROR_CODES} from '../ExpoIapModule';\nimport {ErrorCode, IapPlatform} from '../types';\n\nexport interface PurchaseErrorProps {\n message: string;\n responseCode?: number;\n debugMessage?: string;\n code?: ErrorCode;\n productId?: string;\n platform?: IapPlatform;\n}\n\ntype PlatformErrorData = {\n code?: string | number;\n message?: string;\n responseCode?: number;\n debugMessage?: string;\n productId?: string;\n};\n\nexport type PurchaseError = Error & PurchaseErrorProps;\n\nconst toStandardizedCode = (errorCode: ErrorCode): string =>\n errorCode.startsWith('E_') ? errorCode : `E_${errorCode}`;\n\nconst normalizePlatform = (platform: IapPlatform): 'ios' | 'android' =>\n typeof platform === 'string' && platform.toLowerCase() === 'ios'\n ? 'ios'\n : 'android';\n\nconst COMMON_ERROR_CODE_MAP: Record<ErrorCode, string> = {\n [ErrorCode.Unknown]: toStandardizedCode(ErrorCode.Unknown),\n [ErrorCode.UserCancelled]: toStandardizedCode(ErrorCode.UserCancelled),\n [ErrorCode.UserError]: toStandardizedCode(ErrorCode.UserError),\n [ErrorCode.ItemUnavailable]: toStandardizedCode(ErrorCode.ItemUnavailable),\n [ErrorCode.RemoteError]: toStandardizedCode(ErrorCode.RemoteError),\n [ErrorCode.NetworkError]: toStandardizedCode(ErrorCode.NetworkError),\n [ErrorCode.ServiceError]: toStandardizedCode(ErrorCode.ServiceError),\n [ErrorCode.ReceiptFailed]: toStandardizedCode(ErrorCode.ReceiptFailed),\n [ErrorCode.ReceiptFinished]: toStandardizedCode(ErrorCode.ReceiptFinished),\n [ErrorCode.ReceiptFinishedFailed]: toStandardizedCode(\n ErrorCode.ReceiptFinishedFailed,\n ),\n [ErrorCode.NotPrepared]: toStandardizedCode(ErrorCode.NotPrepared),\n [ErrorCode.NotEnded]: toStandardizedCode(ErrorCode.NotEnded),\n [ErrorCode.AlreadyOwned]: toStandardizedCode(ErrorCode.AlreadyOwned),\n [ErrorCode.DeveloperError]: toStandardizedCode(ErrorCode.DeveloperError),\n [ErrorCode.BillingResponseJsonParseError]: toStandardizedCode(\n ErrorCode.BillingResponseJsonParseError,\n ),\n [ErrorCode.DeferredPayment]: toStandardizedCode(ErrorCode.DeferredPayment),\n [ErrorCode.Interrupted]: toStandardizedCode(ErrorCode.Interrupted),\n [ErrorCode.IapNotAvailable]: toStandardizedCode(ErrorCode.IapNotAvailable),\n [ErrorCode.PurchaseError]: toStandardizedCode(ErrorCode.PurchaseError),\n [ErrorCode.SyncError]: toStandardizedCode(ErrorCode.SyncError),\n [ErrorCode.TransactionValidationFailed]: toStandardizedCode(\n ErrorCode.TransactionValidationFailed,\n ),\n [ErrorCode.ActivityUnavailable]: toStandardizedCode(\n ErrorCode.ActivityUnavailable,\n ),\n [ErrorCode.AlreadyPrepared]: toStandardizedCode(ErrorCode.AlreadyPrepared),\n [ErrorCode.Pending]: toStandardizedCode(ErrorCode.Pending),\n [ErrorCode.ConnectionClosed]: toStandardizedCode(ErrorCode.ConnectionClosed),\n [ErrorCode.InitConnection]: toStandardizedCode(ErrorCode.InitConnection),\n [ErrorCode.ServiceDisconnected]: toStandardizedCode(\n ErrorCode.ServiceDisconnected,\n ),\n [ErrorCode.QueryProduct]: toStandardizedCode(ErrorCode.QueryProduct),\n [ErrorCode.SkuNotFound]: toStandardizedCode(ErrorCode.SkuNotFound),\n [ErrorCode.SkuOfferMismatch]: toStandardizedCode(ErrorCode.SkuOfferMismatch),\n [ErrorCode.ItemNotOwned]: toStandardizedCode(ErrorCode.ItemNotOwned),\n [ErrorCode.BillingUnavailable]: toStandardizedCode(\n ErrorCode.BillingUnavailable,\n ),\n [ErrorCode.FeatureNotSupported]: toStandardizedCode(\n ErrorCode.FeatureNotSupported,\n ),\n [ErrorCode.EmptySkuList]: toStandardizedCode(ErrorCode.EmptySkuList),\n};\n\nexport const ErrorCodeMapping = {\n ios: COMMON_ERROR_CODE_MAP,\n android: COMMON_ERROR_CODE_MAP,\n} as const;\n\nconst OPENIAP_ERROR_CODE_SET: Set<string> = new Set(\n Object.values(ErrorCode).map((code) => toStandardizedCode(code)),\n);\n\nexport const createPurchaseError = (\n props: PurchaseErrorProps,\n): PurchaseError => {\n const error = new Error(props.message) as PurchaseError;\n error.name = '[expo-iap]: PurchaseError';\n error.responseCode = props.responseCode;\n error.debugMessage = props.debugMessage;\n error.code = props.code;\n error.productId = props.productId;\n error.platform = props.platform;\n return error;\n};\n\nexport const createPurchaseErrorFromPlatform = (\n errorData: PlatformErrorData,\n platform: IapPlatform,\n): PurchaseError => {\n const normalizedPlatform = normalizePlatform(platform);\n const errorCode = errorData.code\n ? ErrorCodeUtils.fromPlatformCode(errorData.code, normalizedPlatform)\n : ErrorCode.Unknown;\n\n return createPurchaseError({\n message: errorData.message ?? 'Unknown error occurred',\n responseCode: errorData.responseCode,\n debugMessage: errorData.debugMessage,\n code: errorCode,\n productId: errorData.productId,\n platform,\n });\n};\n\nexport const ErrorCodeUtils = {\n getNativeErrorCode: (errorCode: ErrorCode): string => {\n const standardized = toStandardizedCode(errorCode);\n return (\n (NATIVE_ERROR_CODES as Record<string, string | undefined>)[\n standardized\n ] ?? standardized\n );\n },\n fromPlatformCode: (\n platformCode: string | number,\n _platform: IapPlatform,\n ): ErrorCode => {\n if (typeof platformCode === 'string' && platformCode.startsWith('E_')) {\n if (OPENIAP_ERROR_CODE_SET.has(platformCode)) {\n const match = Object.entries(COMMON_ERROR_CODE_MAP).find(\n ([, value]) => value === platformCode,\n );\n if (match) {\n return match[0] as ErrorCode;\n }\n }\n }\n\n for (const [standardized, nativeCode] of Object.entries(\n (NATIVE_ERROR_CODES || {}) as Record<string, string | number>,\n )) {\n if (\n nativeCode === platformCode &&\n OPENIAP_ERROR_CODE_SET.has(standardized)\n ) {\n const match = Object.entries(COMMON_ERROR_CODE_MAP).find(\n ([, mappedCode]) => mappedCode === standardized,\n );\n if (match) {\n return match[0] as ErrorCode;\n }\n }\n }\n\n for (const [errorCode, mappedCode] of Object.entries(\n COMMON_ERROR_CODE_MAP,\n )) {\n if (mappedCode === platformCode) {\n return errorCode as ErrorCode;\n }\n }\n\n return ErrorCode.Unknown;\n },\n toPlatformCode: (\n errorCode: ErrorCode,\n _platform: IapPlatform,\n ): string | number => {\n const standardized = toStandardizedCode(errorCode);\n const native = (NATIVE_ERROR_CODES as Record<string, string | number>)[\n standardized\n ];\n return native ?? COMMON_ERROR_CODE_MAP[errorCode] ?? 'E_UNKNOWN';\n },\n isValidForPlatform: (\n errorCode: ErrorCode,\n platform: IapPlatform,\n ): boolean => {\n const standardized = toStandardizedCode(errorCode);\n if (\n (NATIVE_ERROR_CODES as Record<string, unknown>)[standardized] !==\n undefined\n ) {\n return true;\n }\n return standardized in ErrorCodeMapping[normalizePlatform(platform)];\n },\n};\n\n// ---------------------------------------------------------------------------\n// Convenience helpers for interpreting error objects\n// ---------------------------------------------------------------------------\n\ntype ErrorLike = string | {code?: ErrorCode | string; message?: string};\n\nconst ERROR_CODES = new Set<string>(Object.values(ErrorCode));\n\nconst normalizeErrorCode = (code?: string | null): string | undefined => {\n if (!code) {\n return undefined;\n }\n\n if (ERROR_CODES.has(code)) {\n return code;\n }\n\n if (code.startsWith('E_')) {\n const trimmed = code.substring(2);\n if (ERROR_CODES.has(trimmed)) {\n return trimmed;\n }\n }\n\n return code;\n};\n\nfunction extractCode(error: unknown): string | undefined {\n if (typeof error === 'string') {\n return normalizeErrorCode(error);\n }\n\n if (error && typeof error === 'object' && 'code' in error) {\n return normalizeErrorCode((error as {code?: string}).code);\n }\n\n return undefined;\n}\n\nexport function isUserCancelledError(error: unknown): boolean {\n return extractCode(error) === ErrorCode.UserCancelled;\n}\n\nexport function isNetworkError(error: unknown): boolean {\n const networkErrors: ErrorCode[] = [\n ErrorCode.NetworkError,\n ErrorCode.RemoteError,\n ErrorCode.ServiceError,\n ErrorCode.ServiceDisconnected,\n ErrorCode.BillingUnavailable,\n ];\n\n const code = extractCode(error);\n return !!code && (networkErrors as string[]).includes(code);\n}\n\nexport function isRecoverableError(error: unknown): boolean {\n const recoverableErrors: ErrorCode[] = [\n ErrorCode.NetworkError,\n ErrorCode.RemoteError,\n ErrorCode.ServiceError,\n ErrorCode.Interrupted,\n ErrorCode.ServiceDisconnected,\n ErrorCode.BillingUnavailable,\n ErrorCode.QueryProduct,\n ErrorCode.InitConnection,\n ];\n\n const code = extractCode(error);\n return !!code && (recoverableErrors as string[]).includes(code);\n}\n\nexport function getUserFriendlyErrorMessage(error: ErrorLike): string {\n const errorCode = extractCode(error);\n\n switch (errorCode) {\n case ErrorCode.UserCancelled:\n return 'Purchase was cancelled by user';\n case ErrorCode.NetworkError:\n return 'Network connection error. Please check your internet connection and try again.';\n case ErrorCode.ReceiptFinished:\n return 'Receipt already finished';\n case ErrorCode.ServiceDisconnected:\n return 'Billing service disconnected. Please try again.';\n case ErrorCode.BillingUnavailable:\n return 'Billing is unavailable on this device or account.';\n case ErrorCode.ItemUnavailable:\n return 'This item is not available for purchase';\n case ErrorCode.ItemNotOwned:\n return \"You don't own this item\";\n case ErrorCode.AlreadyOwned:\n return 'You already own this item';\n case ErrorCode.SkuNotFound:\n return 'Requested product could not be found';\n case ErrorCode.SkuOfferMismatch:\n return 'Selected offer does not match the SKU';\n case ErrorCode.DeferredPayment:\n return 'Payment is pending approval';\n case ErrorCode.NotPrepared:\n return 'In-app purchase is not ready. Please try again later.';\n case ErrorCode.ServiceError:\n return 'Store service error. Please try again later.';\n case ErrorCode.FeatureNotSupported:\n return 'This feature is not supported on this device.';\n case ErrorCode.TransactionValidationFailed:\n return 'Transaction could not be verified';\n case ErrorCode.ReceiptFailed:\n return 'Receipt processing failed';\n case ErrorCode.EmptySkuList:\n return 'No product IDs provided';\n case ErrorCode.InitConnection:\n return 'Failed to initialize billing connection';\n case ErrorCode.QueryProduct:\n return 'Failed to query products. Please try again later.';\n default: {\n if (error && typeof error === 'object' && 'message' in error) {\n return (\n (error as {message?: string}).message ??\n 'An unexpected error occurred'\n );\n }\n return 'An unexpected error occurred';\n }\n }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  require 'json'
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+ versions = JSON.parse(File.read(File.join(__dir__, '..', 'openiap-versions.json')))
4
5
 
5
6
  Pod::Spec.new do |s|
6
7
  s.name = 'ExpoIap'
@@ -16,12 +17,12 @@ Pod::Spec.new do |s|
16
17
  # Even though StoreKit 2 requires iOS/tvOS 15.0+, keep both at 13.4 for compatibility with affected Expo SDKs
17
18
  # The iOS/tvOS 15.0+ requirement is enforced at build time in source code via @available annotations
18
19
  s.platforms = { :ios => '13.4', :tvos => '13.4' }
19
- s.swift_version = '5.4'
20
+ s.swift_version = '5.9'
20
21
  s.source = { git: 'https://github.com/hyochan/expo-iap' }
21
22
  s.static_framework = true
22
23
 
23
24
  s.dependency 'ExpoModulesCore'
24
- s.dependency 'openiap', '1.1.12'
25
+ s.dependency 'openiap', versions['apple']
25
26
 
26
27
  # Swift/Objective-C compatibility
27
28
  s.pod_target_xcconfig = {
@@ -0,0 +1,96 @@
1
+ import Foundation
2
+ import OpenIAP
3
+
4
+ enum ExpoIapHelper {
5
+ static func sanitizeDictionary(_ dictionary: [String: Any?]) -> [String: Any] {
6
+ var result: [String: Any] = [:]
7
+ for (key, value) in dictionary {
8
+ if let value {
9
+ result[key] = value
10
+ }
11
+ }
12
+ return result
13
+ }
14
+
15
+ static func sanitizeArray(_ array: [[String: Any?]]) -> [[String: Any]] {
16
+ array.map { sanitizeDictionary($0) }
17
+ }
18
+
19
+ // Overloads to support already-sanitized payloads (e.g., serialized OpenIAP responses)
20
+ static func sanitizeDictionary(_ dictionary: [String: Any]) -> [String: Any] {
21
+ dictionary
22
+ }
23
+
24
+ static func sanitizeArray(_ array: [[String: Any]]) -> [[String: Any]] {
25
+ array
26
+ }
27
+
28
+ static func parseProductQueryType(_ rawValue: String?) -> ProductQueryType {
29
+ guard let raw = rawValue?.trimmingCharacters(in: .whitespacesAndNewlines), !raw.isEmpty else {
30
+ return .all
31
+ }
32
+ switch raw.lowercased() {
33
+ case "inapp", ProductQueryType.inApp.rawValue:
34
+ return .inApp
35
+ case ProductQueryType.subs.rawValue:
36
+ return .subs
37
+ case ProductQueryType.all.rawValue:
38
+ return .all
39
+ default:
40
+ return .all
41
+ }
42
+ }
43
+
44
+ static func decodeProductRequest(from payload: [String: Any]) throws -> ProductRequest {
45
+ if let skus = payload["skus"] as? [String], !skus.isEmpty {
46
+ let type = parseProductQueryType(payload["type"] as? String)
47
+ return try OpenIapSerialization.productRequest(skus: skus, type: type)
48
+ }
49
+
50
+ let indexedSkus = payload.keys
51
+ .compactMap { Int($0) }
52
+ .sorted()
53
+ .compactMap { payload[String($0)] as? String }
54
+
55
+ if !indexedSkus.isEmpty {
56
+ return try OpenIapSerialization.productRequest(skus: indexedSkus, type: .all)
57
+ }
58
+
59
+ if let request = try? OpenIapSerialization.decode(object: payload, as: ProductRequest.self) {
60
+ return request
61
+ }
62
+
63
+ throw PurchaseError.emptySkuList()
64
+ }
65
+
66
+ static func decodeRequestPurchaseProps(from payload: [String: Any]) throws -> RequestPurchaseProps {
67
+ if payload["requestPurchase"] != nil || payload["requestSubscription"] != nil {
68
+ return try OpenIapSerialization.decode(object: payload, as: RequestPurchaseProps.self)
69
+ }
70
+
71
+ if let request = payload["request"] {
72
+ let parsedType = parseProductQueryType(payload["type"] as? String)
73
+ let purchaseType: ProductQueryType = parsedType == .all ? .inApp : parsedType
74
+ var normalized: [String: Any] = ["type": purchaseType.rawValue]
75
+ switch purchaseType {
76
+ case .subs:
77
+ normalized["requestSubscription"] = request
78
+ case .inApp:
79
+ normalized["requestPurchase"] = request
80
+ case .all:
81
+ break
82
+ }
83
+ return try OpenIapSerialization.decode(object: normalized, as: RequestPurchaseProps.self)
84
+ }
85
+
86
+ if payload["sku"] != nil {
87
+ let normalized: [String: Any] = [
88
+ "type": ProductQueryType.inApp.rawValue,
89
+ "requestPurchase": ["ios": payload]
90
+ ]
91
+ return try OpenIapSerialization.decode(object: normalized, as: RequestPurchaseProps.self)
92
+ }
93
+
94
+ throw PurchaseError.make(code: .developerError, message: "Invalid request payload")
95
+ }
96
+ }
@@ -0,0 +1,127 @@
1
+ import Foundation
2
+ #if canImport(os)
3
+ import os
4
+ #endif
5
+
6
+ enum ExpoIapLog {
7
+ enum Level: String {
8
+ case debug
9
+ case info
10
+ case warn
11
+ case error
12
+ }
13
+
14
+ private static var isEnabled: Bool = {
15
+ #if DEBUG
16
+ true
17
+ #else
18
+ false
19
+ #endif
20
+ }()
21
+
22
+ private static var customHandler: ((Level, String) -> Void)?
23
+
24
+ static func setEnabled(_ enabled: Bool) {
25
+ isEnabled = enabled
26
+ }
27
+
28
+ static func setHandler(_ handler: ((Level, String) -> Void)?) {
29
+ customHandler = handler
30
+ }
31
+
32
+ static func debug(_ message: String) { log(.debug, message) }
33
+ static func info(_ message: String) { log(.info, message) }
34
+ static func warn(_ message: String) { log(.warn, message) }
35
+ static func error(_ message: String) { log(.error, message) }
36
+
37
+ static func payload(_ name: String, payload: Any?) {
38
+ debug("\(name) payload: \(stringify(payload))")
39
+ }
40
+
41
+ static func result(_ name: String, value: Any?) {
42
+ debug("\(name) result: \(stringify(value))")
43
+ }
44
+
45
+ static func failure(_ name: String, error: Error) {
46
+ ExpoIapLog.error("\(name) failed: \(error.localizedDescription)")
47
+ }
48
+
49
+ private static func log(_ level: Level, _ message: String) {
50
+ guard isEnabled else { return }
51
+
52
+ if let handler = customHandler {
53
+ handler(level, message)
54
+ return
55
+ }
56
+
57
+ #if canImport(os)
58
+ let logger = Logger(subsystem: "dev.hyo.expo-iap", category: "ExpoIap")
59
+ let formatted = "[ExpoIap] \(message)"
60
+ switch level {
61
+ case .debug:
62
+ logger.debug("\(formatted, privacy: .public)")
63
+ case .info:
64
+ logger.info("\(formatted, privacy: .public)")
65
+ case .warn:
66
+ logger.warning("\(formatted, privacy: .public)")
67
+ case .error:
68
+ logger.error("\(formatted, privacy: .public)")
69
+ }
70
+ #else
71
+ NSLog("[ExpoIap][%@] %@", level.rawValue.uppercased(), message)
72
+ #endif
73
+ }
74
+
75
+ private static func stringify(_ value: Any?) -> String {
76
+ guard let sanitized = sanitize(value) else {
77
+ return "null"
78
+ }
79
+
80
+ if JSONSerialization.isValidJSONObject(sanitized),
81
+ let data = try? JSONSerialization.data(withJSONObject: sanitized, options: []) {
82
+ return String(data: data, encoding: .utf8) ?? String(describing: sanitized)
83
+ }
84
+
85
+ return String(describing: sanitized)
86
+ }
87
+
88
+ private static func sanitize(_ value: Any?) -> Any? {
89
+ guard let value else { return nil }
90
+
91
+ if let dictionary = value as? [String: Any] {
92
+ return sanitizeDictionary(dictionary)
93
+ }
94
+
95
+ if let optionalDictionary = value as? [String: Any?] {
96
+ var compact: [String: Any] = [:]
97
+ for (key, optionalValue) in optionalDictionary {
98
+ if let optionalValue {
99
+ compact[key] = optionalValue
100
+ }
101
+ }
102
+ return sanitizeDictionary(compact)
103
+ }
104
+
105
+ if let array = value as? [Any] {
106
+ return array.compactMap { sanitize($0) }
107
+ }
108
+
109
+ if let optionalArray = value as? [Any?] {
110
+ return optionalArray.compactMap { sanitize($0) }
111
+ }
112
+
113
+ return value
114
+ }
115
+
116
+ private static func sanitizeDictionary(_ dictionary: [String: Any]) -> [String: Any] {
117
+ var sanitized: [String: Any] = [:]
118
+ for (key, value) in dictionary {
119
+ if key.lowercased().contains("token") {
120
+ sanitized[key] = "hidden"
121
+ } else if let sanitizedValue = sanitize(value) {
122
+ sanitized[key] = sanitizedValue
123
+ }
124
+ }
125
+ return sanitized
126
+ }
127
+ }