expo-iap 2.5.0 → 2.5.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n sync,\n validateReceiptIos,\n validateReceiptAndroid,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n clearCurrentPurchase: () => void;\n clearCurrentPurchaseError: () => void;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: (skus: string[]) => Promise<void>;\n getPurchaseHistories: (skus: string[]) => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n requestPurchase: typeof requestPurchaseInternal;\n validateReceipt: (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => Promise<any>;\n restorePurchases: () => Promise<void>; // 구매 복원 함수 추가\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (\n purchase: ProductPurchase | SubscriptionPurchase,\n ) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n\n // Helper function to merge arrays with duplicate checking\n const mergeWithDuplicateCheck = useCallback(\n <T>(\n existingItems: T[],\n newItems: T[],\n getKey: (item: T) => string,\n ): T[] => {\n const merged = [...existingItems];\n newItems.forEach((newItem) => {\n const isDuplicate = merged.some(\n (existingItem) => getKey(existingItem) === getKey(newItem),\n );\n if (!isDuplicate) {\n merged.push(newItem);\n }\n });\n return merged;\n },\n [],\n );\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIos?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<SubscriptionProduct[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const clearCurrentPurchase = useCallback(() => {\n setCurrentPurchase(undefined);\n }, []);\n\n const clearCurrentPurchaseError = useCallback(() => {\n setCurrentPurchaseError(undefined);\n }, []);\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n const newProducts = await getProducts(skus);\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n newProducts,\n (product) => product.id,\n ),\n );\n },\n [mergeWithDuplicateCheck],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n const newSubscriptions = await getSubscriptions(skus);\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n newSubscriptions,\n (subscription) => subscription.id,\n ),\n );\n },\n [mergeWithDuplicateCheck],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n clearCurrentPurchase();\n }\n if (purchase.id === currentPurchaseError?.productId) {\n clearCurrentPurchaseError();\n }\n }\n },\n [\n currentPurchase?.id,\n currentPurchaseError?.productId,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n ],\n );\n\n const requestPurchaseWithReset = useCallback(\n async (requestObj: Parameters<typeof requestPurchaseInternal>[0]) => {\n clearCurrentPurchase();\n clearCurrentPurchaseError();\n\n try {\n return await requestPurchaseInternal(requestObj);\n } catch (error) {\n throw error;\n }\n },\n [clearCurrentPurchase, clearCurrentPurchaseError],\n );\n\n const refreshSubscriptionStatus = useCallback(\n async (productId: string) => {\n try {\n if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {\n await getSubscriptionsInternal([productId]);\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n },\n [getAvailablePurchasesInternal, getSubscriptionsInternal],\n );\n\n const restorePurchases = useCallback(async (): Promise<void> => {\n try {\n if (Platform.OS === 'ios') {\n await sync().catch((error) => {\n if (optionsRef.current?.onSyncError) {\n optionsRef.current.onSyncError(error);\n } else {\n console.warn('Error restoring purchases:', error);\n }\n });\n }\n await getAvailablePurchasesInternal();\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, [getAvailablePurchasesInternal]);\n\n const validateReceipt = useCallback(\n async (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => {\n if (Platform.OS === 'ios') {\n return await validateReceiptIos(sku);\n } else if (Platform.OS === 'android') {\n if (\n !androidOptions ||\n !androidOptions.packageName ||\n !androidOptions.productToken ||\n !androidOptions.accessToken\n ) {\n throw new Error(\n 'Android validation requires packageName, productToken, and accessToken',\n );\n }\n return await validateReceiptAndroid({\n packageName: androidOptions.packageName,\n productId: sku,\n productToken: androidOptions.productToken,\n accessToken: androidOptions.accessToken,\n isSub: androidOptions.isSub,\n });\n } else {\n throw new Error('Platform not supported');\n }\n },\n [],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n\n if ('expirationDateIos' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products are handled through regular purchase updates\n subscriptionsRef.current.promotedProductsIos = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n // Add to promoted products if it's a promoted transaction (avoid duplicates)\n setPromotedProductsIOS((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n [purchase as ProductPurchase],\n (product) => product.transactionId || product.id,\n ),\n );\n\n // Refresh subscription status if it's a subscription purchase\n if ('expirationDateIos' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n },\n );\n }\n }\n }, [refreshSubscriptionStatus, mergeWithDuplicateCheck]);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n };\n}\n"]}
1
+ {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n} from './';\nimport {sync, validateReceiptIos} from './modules/ios';\nimport {validateReceiptAndroid} from './modules/android';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n clearCurrentPurchase: () => void;\n clearCurrentPurchaseError: () => void;\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<PurchaseResult | boolean>;\n getAvailablePurchases: (skus: string[]) => Promise<void>;\n getPurchaseHistories: (skus: string[]) => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n requestPurchase: (params: {\n request: any;\n type?: 'inapp' | 'subs';\n }) => Promise<any>;\n validateReceipt: (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => Promise<any>;\n restorePurchases: () => Promise<void>; // 구매 복원 함수 추가\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (\n purchase: ProductPurchase | SubscriptionPurchase,\n ) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n\n // Helper function to merge arrays with duplicate checking\n const mergeWithDuplicateCheck = useCallback(\n <T>(\n existingItems: T[],\n newItems: T[],\n getKey: (item: T) => string,\n ): T[] => {\n const merged = [...existingItems];\n newItems.forEach((newItem) => {\n const isDuplicate = merged.some(\n (existingItem) => getKey(existingItem) === getKey(newItem),\n );\n if (!isDuplicate) {\n merged.push(newItem);\n }\n });\n return merged;\n },\n [],\n );\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIos?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<SubscriptionProduct[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const clearCurrentPurchase = useCallback(() => {\n setCurrentPurchase(undefined);\n }, []);\n\n const clearCurrentPurchaseError = useCallback(() => {\n setCurrentPurchaseError(undefined);\n }, []);\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await getProducts(skus);\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n result,\n (product) => product.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await getSubscriptions(skus);\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n result,\n (subscription) => subscription.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching subscriptions:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases();\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<PurchaseResult | boolean> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n clearCurrentPurchase();\n }\n if (purchase.id === currentPurchaseError?.productId) {\n clearCurrentPurchaseError();\n }\n }\n },\n [\n currentPurchase?.id,\n currentPurchaseError?.productId,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n ],\n );\n\n const requestPurchaseWithReset = useCallback(\n async (requestObj: {request: any; type?: 'inapp' | 'subs'}) => {\n clearCurrentPurchase();\n clearCurrentPurchaseError();\n\n try {\n return await requestPurchaseInternal(requestObj);\n } catch (error) {\n throw error;\n }\n },\n [clearCurrentPurchase, clearCurrentPurchaseError],\n );\n\n const refreshSubscriptionStatus = useCallback(\n async (productId: string) => {\n try {\n if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {\n await getSubscriptionsInternal([productId]);\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n },\n [getAvailablePurchasesInternal, getSubscriptionsInternal],\n );\n\n const restorePurchases = useCallback(async (): Promise<void> => {\n try {\n if (Platform.OS === 'ios') {\n await sync().catch((error) => {\n if (optionsRef.current?.onSyncError) {\n optionsRef.current.onSyncError(error);\n } else {\n console.warn('Error restoring purchases:', error);\n }\n });\n }\n await getAvailablePurchasesInternal();\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, [getAvailablePurchasesInternal]);\n\n const validateReceipt = useCallback(\n async (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => {\n if (Platform.OS === 'ios') {\n return await validateReceiptIos(sku);\n } else if (Platform.OS === 'android') {\n if (\n !androidOptions ||\n !androidOptions.packageName ||\n !androidOptions.productToken ||\n !androidOptions.accessToken\n ) {\n throw new Error(\n 'Android validation requires packageName, productToken, and accessToken',\n );\n }\n return await validateReceiptAndroid({\n packageName: androidOptions.packageName,\n productId: sku,\n productToken: androidOptions.productToken,\n accessToken: androidOptions.accessToken,\n isSub: androidOptions.isSub,\n });\n } else {\n throw new Error('Platform not supported');\n }\n },\n [],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n\n if ('expirationDateIos' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products are handled through regular purchase updates\n subscriptionsRef.current.promotedProductsIos = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n // Add to promoted products if it's a promoted transaction (avoid duplicates)\n setPromotedProductsIOS((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n [purchase as ProductPurchase],\n (product) => product.transactionId || product.id,\n ),\n );\n\n // Refresh subscription status if it's a subscription purchase\n if ('expirationDateIos' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n },\n );\n }\n }\n }, [refreshSubscriptionStatus, mergeWithDuplicateCheck]);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=smartPurchase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smartPurchase.d.ts","sourceRoot":"","sources":["../../src/utils/smartPurchase.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=smartPurchase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smartPurchase.js","sourceRoot":"","sources":["../../src/utils/smartPurchase.ts"],"names":[],"mappings":"","sourcesContent":[""]}
@@ -56,7 +56,7 @@ func serializeTransaction(_ transaction: Transaction, jwsRepresentationIos: Stri
56
56
 
57
57
  "quantityIos": transaction.purchasedQuantity,
58
58
  "originalTransactionDateIos": transaction.originalPurchaseDate.timeIntervalSince1970 * 1000,
59
- "originalTransactionIdentifierIos": transaction.originalID,
59
+ "originalTransactionIdentifierIos": String(transaction.originalID),
60
60
  "appAccountToken": transaction.appAccountToken?.uuidString,
61
61
 
62
62
  "appBundleIdIos": transaction.appBundleID,
@@ -186,32 +186,7 @@ public class ExpoIapModule: Module {
186
186
  Name("ExpoIap")
187
187
 
188
188
  Constants([
189
- "ERROR_CODES": [
190
- "E_UNKNOWN": IapErrorCode.unknown,
191
- "E_SERVICE_ERROR": IapErrorCode.serviceError,
192
- "E_USER_CANCELLED": IapErrorCode.userCancelled,
193
- "E_USER_ERROR": IapErrorCode.userError,
194
- "E_ITEM_UNAVAILABLE": IapErrorCode.itemUnavailable,
195
- "E_REMOTE_ERROR": IapErrorCode.remoteError,
196
- "E_NETWORK_ERROR": IapErrorCode.networkError,
197
- "E_RECEIPT_FAILED": IapErrorCode.receiptFailed,
198
- "E_RECEIPT_FINISHED_FAILED": IapErrorCode.receiptFinishedFailed,
199
- "E_NOT_PREPARED": IapErrorCode.notPrepared,
200
- "E_NOT_ENDED": IapErrorCode.notEnded,
201
- "E_ALREADY_OWNED": IapErrorCode.alreadyOwned,
202
- "E_DEVELOPER_ERROR": IapErrorCode.developerError,
203
- "E_PURCHASE_ERROR": IapErrorCode.purchaseError,
204
- "E_SYNC_ERROR": IapErrorCode.syncError,
205
- "E_DEFERRED_PAYMENT": IapErrorCode.deferredPayment,
206
- "E_TRANSACTION_VALIDATION_FAILED": IapErrorCode.transactionValidationFailed,
207
- "E_BILLING_RESPONSE_JSON_PARSE_ERROR": IapErrorCode.billingResponseJsonParseError,
208
- "E_INTERRUPTED": IapErrorCode.interrupted,
209
- "E_IAP_NOT_AVAILABLE": IapErrorCode.iapNotAvailable,
210
- "E_ACTIVITY_UNAVAILABLE": IapErrorCode.activityUnavailable,
211
- "E_ALREADY_PREPARED": IapErrorCode.alreadyPrepared,
212
- "E_PENDING": IapErrorCode.pending,
213
- "E_CONNECTION_CLOSED": IapErrorCode.connectionClosed,
214
- ]
189
+ "ERROR_CODES": IapErrorCode.toDictionary()
215
190
  ])
216
191
 
217
192
  Events(IapEvent.PurchaseUpdated, IapEvent.PurchaseError)
package/ios/Types.swift CHANGED
@@ -14,6 +14,7 @@ public enum StoreError: Error {
14
14
 
15
15
  // Error codes for IAP operations - centralized error code management
16
16
  struct IapErrorCode {
17
+ // Constants for code usage - safe pattern without force unwrapping
17
18
  static let unknown = "E_UNKNOWN"
18
19
  static let serviceError = "E_SERVICE_ERROR"
19
20
  static let userCancelled = "E_USER_CANCELLED"
@@ -38,6 +39,39 @@ struct IapErrorCode {
38
39
  static let alreadyPrepared = "E_ALREADY_PREPARED"
39
40
  static let pending = "E_PENDING"
40
41
  static let connectionClosed = "E_CONNECTION_CLOSED"
42
+
43
+ // Cached dictionary for Constants export - using constants as keys to avoid duplication
44
+ private static let _cachedDictionary: [String: String] = [
45
+ unknown: unknown,
46
+ serviceError: serviceError,
47
+ userCancelled: userCancelled,
48
+ userError: userError,
49
+ itemUnavailable: itemUnavailable,
50
+ remoteError: remoteError,
51
+ networkError: networkError,
52
+ receiptFailed: receiptFailed,
53
+ receiptFinishedFailed: receiptFinishedFailed,
54
+ notPrepared: notPrepared,
55
+ notEnded: notEnded,
56
+ alreadyOwned: alreadyOwned,
57
+ developerError: developerError,
58
+ purchaseError: purchaseError,
59
+ syncError: syncError,
60
+ deferredPayment: deferredPayment,
61
+ transactionValidationFailed: transactionValidationFailed,
62
+ billingResponseJsonParseError: billingResponseJsonParseError,
63
+ interrupted: interrupted,
64
+ iapNotAvailable: iapNotAvailable,
65
+ activityUnavailable: activityUnavailable,
66
+ alreadyPrepared: alreadyPrepared,
67
+ pending: pending,
68
+ connectionClosed: connectionClosed
69
+ ]
70
+
71
+ // Return cached dictionary - no allocation on repeated calls
72
+ static func toDictionary() -> [String: String] {
73
+ return _cachedDictionary
74
+ }
41
75
  }
42
76
 
43
77
  // Based on https://stackoverflow.com/a/40135192/570612
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "build": "expo-module build",
9
9
  "clean": "expo-module clean",
10
10
  "lint": "eslint --ext .ts,.tsx,.js,.jsx src",
11
- "lint:eslint": "eslint --fix '**/*.{ts,tsx}'",
11
+ "lint:eslint": "eslint --fix 'src/**/*.{ts,tsx}' 'plugin/src/**/*.{ts,tsx}'",
12
12
  "lint:prettier": "prettier --write \"**/*.{md,js,jsx,ts,tsx}\"",
13
13
  "lint:tsc": "tsc -p tsconfig.json --noEmit --skipLibCheck",
14
14
  "lint:ci": "yarn lint:tsc && yarn lint:eslint && yarn lint:prettier",
@@ -20,7 +20,11 @@
20
20
  "lint:kotlin": "ktlint --format ./android",
21
21
  "build:plugin": "tsc --build plugin",
22
22
  "clean:plugin": "expo-module clean plugin",
23
- "lint:plugin": "eslint plugin/src/*"
23
+ "lint:plugin": "eslint plugin/src/*",
24
+ "docs:start": "cd docs && bun run start",
25
+ "docs:build": "cd docs && bun run build",
26
+ "docs:serve": "cd docs && bun run serve",
27
+ "docs:install": "cd docs && bun install --yarn"
24
28
  },
25
29
  "keywords": [
26
30
  "react-native",
@@ -18,9 +18,8 @@ export type ChangeEventPayload = {
18
18
  value: string;
19
19
  };
20
20
 
21
- /**
22
- * Base product type with common properties shared between iOS and Android
23
- */
21
+ export type ProductType = 'inapp' | 'subs';
22
+
24
23
  export type ProductBase = {
25
24
  id: string;
26
25
  title: string;
@@ -32,12 +31,6 @@ export type ProductBase = {
32
31
  price?: number;
33
32
  };
34
33
 
35
- // Define literal platform types for better type discrimination
36
- export type IosPlatform = {platform: 'ios'};
37
- export type AndroidPlatform = {platform: 'android'};
38
- export type ProductType = 'inapp' | 'subs';
39
-
40
- // Common base purchase type
41
34
  export type PurchaseBase = {
42
35
  id: string;
43
36
  transactionId?: string;
@@ -45,35 +38,49 @@ export type PurchaseBase = {
45
38
  transactionReceipt: string;
46
39
  };
47
40
 
48
- // Union type for platform-specific product types with proper discriminators
41
+ // Define literal platform types for better type discrimination
42
+ export type IosPlatform = {platform: 'ios'};
43
+ export type AndroidPlatform = {platform: 'android'};
44
+
45
+ // Platform-agnostic unified product types (public API)
49
46
  export type Product =
50
47
  | (ProductAndroid & AndroidPlatform)
51
48
  | (ProductIos & IosPlatform);
52
49
 
53
- // Union type for platform-specific purchase types with proper discriminators
50
+ export type SubscriptionProduct =
51
+ | (SubscriptionProductAndroid & AndroidPlatform)
52
+ | (SubscriptionProductIos & IosPlatform);
53
+
54
+ // Internal platform-specific types (used for native interop only)
55
+ export type RequestPurchaseProps =
56
+ | RequestPurchaseIosProps
57
+ | RequestPurchaseAndroidProps;
58
+
59
+ export type RequestSubscriptionProps =
60
+ | RequestSubscriptionAndroidProps
61
+ | RequestSubscriptionIosProps;
62
+
63
+ // ============================================================================
64
+ // Legacy Types (For backward compatibility with useIap hook)
65
+ // ============================================================================
66
+
67
+ // Re-export platform-specific purchase types for legacy compatibility
68
+ export type {ProductPurchaseAndroid} from './types/ExpoIapAndroid.types';
69
+ export type {ProductPurchaseIos} from './types/ExpoIapIos.types';
70
+
71
+ // Union type for platform-specific purchase types (legacy support)
54
72
  export type ProductPurchase =
55
73
  | (ProductPurchaseAndroid & AndroidPlatform)
56
74
  | (ProductPurchaseIos & IosPlatform);
57
75
 
58
- // Union type for platform-specific subscription purchase types with proper discriminators
76
+ // Union type for platform-specific subscription purchase types (legacy support)
59
77
  export type SubscriptionPurchase =
60
78
  | (ProductPurchaseAndroid & AndroidPlatform & {autoRenewingAndroid: boolean})
61
79
  | (ProductPurchaseIos & IosPlatform);
62
80
 
63
81
  export type Purchase = ProductPurchase | SubscriptionPurchase;
64
82
 
65
- export type RequestPurchaseProps =
66
- | RequestPurchaseIosProps
67
- | RequestPurchaseAndroidProps;
68
-
69
- export type SubscriptionProduct =
70
- | (SubscriptionProductAndroid & AndroidPlatform)
71
- | (SubscriptionProductIos & IosPlatform);
72
-
73
- export type RequestSubscriptionProps =
74
- | RequestSubscriptionAndroidProps
75
- | RequestSubscriptionIosProps;
76
-
83
+ // Legacy result type
77
84
  export type PurchaseResult = {
78
85
  responseCode?: number;
79
86
  debugMessage?: string;
@@ -81,7 +88,6 @@ export type PurchaseResult = {
81
88
  message?: string;
82
89
  purchaseTokenAndroid?: string;
83
90
  };
84
-
85
91
  /**
86
92
  * Centralized error codes for expo-iap
87
93
  * These are mapped to platform-specific error codes and provide consistent error handling
@@ -291,3 +297,49 @@ export const ErrorCodeUtils = {
291
297
  return errorCode in ErrorCodeMapping[platform];
292
298
  },
293
299
  };
300
+
301
+ // ============================================================================
302
+ // Enhanced Unified Request Types
303
+ // ============================================================================
304
+
305
+ /**
306
+ * Unified request props that work on both iOS and Android platforms
307
+ * iOS will use 'sku', Android will use 'skus' (or convert sku to skus array)
308
+ */
309
+ export interface UnifiedRequestPurchaseProps {
310
+ // Universal properties - works on both platforms
311
+ readonly sku?: string; // Single SKU (iOS native, Android fallback)
312
+ readonly skus?: string[]; // Multiple SKUs (Android native, iOS uses first item)
313
+
314
+ // iOS-specific properties (ignored on Android)
315
+ readonly andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
316
+ readonly appAccountToken?: string;
317
+ readonly quantity?: number;
318
+ readonly withOffer?: import('./types/ExpoIapIos.types').PaymentDiscount;
319
+
320
+ // Android-specific properties (ignored on iOS)
321
+ readonly obfuscatedAccountIdAndroid?: string;
322
+ readonly obfuscatedProfileIdAndroid?: string;
323
+ readonly isOfferPersonalized?: boolean;
324
+ }
325
+
326
+ /**
327
+ * Unified subscription request props
328
+ */
329
+ export interface UnifiedRequestSubscriptionProps
330
+ extends UnifiedRequestPurchaseProps {
331
+ // Android subscription-specific properties
332
+ readonly purchaseTokenAndroid?: string;
333
+ readonly replacementModeAndroid?: number;
334
+ readonly subscriptionOffers?: {
335
+ sku: string;
336
+ offerToken: string;
337
+ }[];
338
+ }
339
+
340
+ // ============================================================================
341
+ // ============================================================================
342
+ // Type Guards and Utility Functions
343
+ // ============================================================================
344
+
345
+ // Note: Type guard functions are exported from index.ts to avoid conflicts
package/src/useIap.ts CHANGED
@@ -9,10 +9,9 @@ import {
9
9
  finishTransaction as finishTransactionInternal,
10
10
  getSubscriptions,
11
11
  requestPurchase as requestPurchaseInternal,
12
- sync,
13
- validateReceiptIos,
14
- validateReceiptAndroid,
15
12
  } from './';
13
+ import {sync, validateReceiptIos} from './modules/ios';
14
+ import {validateReceiptAndroid} from './modules/android';
16
15
  import {useCallback, useEffect, useState, useRef} from 'react';
17
16
  import {
18
17
  Product,
@@ -43,12 +42,15 @@ type UseIap = {
43
42
  }: {
44
43
  purchase: Purchase;
45
44
  isConsumable?: boolean;
46
- }) => Promise<string | boolean | PurchaseResult | void>;
45
+ }) => Promise<PurchaseResult | boolean>;
47
46
  getAvailablePurchases: (skus: string[]) => Promise<void>;
48
47
  getPurchaseHistories: (skus: string[]) => Promise<void>;
49
48
  getProducts: (skus: string[]) => Promise<void>;
50
49
  getSubscriptions: (skus: string[]) => Promise<void>;
51
- requestPurchase: typeof requestPurchaseInternal;
50
+ requestPurchase: (params: {
51
+ request: any;
52
+ type?: 'inapp' | 'subs';
53
+ }) => Promise<any>;
52
54
  validateReceipt: (
53
55
  sku: string,
54
56
  androidOptions?: {
@@ -136,34 +138,47 @@ export function useIAP(options?: UseIAPOptions): UseIap {
136
138
 
137
139
  const getProductsInternal = useCallback(
138
140
  async (skus: string[]): Promise<void> => {
139
- const newProducts = await getProducts(skus);
140
- setProducts((prevProducts) =>
141
- mergeWithDuplicateCheck(
142
- prevProducts,
143
- newProducts,
144
- (product) => product.id,
145
- ),
146
- );
141
+ try {
142
+ const result = await getProducts(skus);
143
+ setProducts((prevProducts) =>
144
+ mergeWithDuplicateCheck(
145
+ prevProducts,
146
+ result,
147
+ (product) => product.id,
148
+ ),
149
+ );
150
+ } catch (error) {
151
+ console.error('Error fetching products:', error);
152
+ }
147
153
  },
148
154
  [mergeWithDuplicateCheck],
149
155
  );
150
156
 
151
157
  const getSubscriptionsInternal = useCallback(
152
158
  async (skus: string[]): Promise<void> => {
153
- const newSubscriptions = await getSubscriptions(skus);
154
- setSubscriptions((prevSubscriptions) =>
155
- mergeWithDuplicateCheck(
156
- prevSubscriptions,
157
- newSubscriptions,
158
- (subscription) => subscription.id,
159
- ),
160
- );
159
+ try {
160
+ const result = await getSubscriptions(skus);
161
+ setSubscriptions((prevSubscriptions) =>
162
+ mergeWithDuplicateCheck(
163
+ prevSubscriptions,
164
+ result,
165
+ (subscription) => subscription.id,
166
+ ),
167
+ );
168
+ } catch (error) {
169
+ console.error('Error fetching subscriptions:', error);
170
+ }
161
171
  },
162
172
  [mergeWithDuplicateCheck],
163
173
  );
164
174
 
165
175
  const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {
166
- setAvailablePurchases(await getAvailablePurchases());
176
+ try {
177
+ const result = await getAvailablePurchases();
178
+ setAvailablePurchases(result);
179
+ } catch (error) {
180
+ console.error('Error fetching available purchases:', error);
181
+ }
167
182
  }, []);
168
183
 
169
184
  const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {
@@ -177,7 +192,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
177
192
  }: {
178
193
  purchase: ProductPurchase;
179
194
  isConsumable?: boolean;
180
- }): Promise<string | boolean | PurchaseResult | void> => {
195
+ }): Promise<PurchaseResult | boolean> => {
181
196
  try {
182
197
  return await finishTransactionInternal({
183
198
  purchase,
@@ -203,7 +218,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
203
218
  );
204
219
 
205
220
  const requestPurchaseWithReset = useCallback(
206
- async (requestObj: Parameters<typeof requestPurchaseInternal>[0]) => {
221
+ async (requestObj: {request: any; type?: 'inapp' | 'subs'}) => {
207
222
  clearCurrentPurchase();
208
223
  clearCurrentPurchaseError();
209
224
 
File without changes
@@ -1,172 +0,0 @@
1
- # Error Code Management in expo-iap
2
-
3
- ## Overview
4
-
5
- expo-iap now provides a centralized error code management system that ensures consistent error handling across iOS and Android platforms. This system addresses the issue where different platforms used different error code formats (numeric codes on iOS, string codes on Android) and provides a unified approach.
6
-
7
- ## Problem Solved
8
-
9
- Previously, users experienced inconsistent error codes:
10
-
11
- - iOS returned numeric error codes (e.g., "2" for user cancellation)
12
- - Android returned string error codes (e.g., "E_USER_CANCELLED" for user cancellation)
13
- - The TypeScript enum didn't align with platform-specific implementations
14
-
15
- ## New Error Code System
16
-
17
- ### Centralized ErrorCode Enum
18
-
19
- All error codes are now defined in a single `ErrorCode` enum:
20
-
21
- ```typescript
22
- export enum ErrorCode {
23
- E_UNKNOWN = 'E_UNKNOWN',
24
- E_USER_CANCELLED = 'E_USER_CANCELLED',
25
- E_USER_ERROR = 'E_USER_ERROR',
26
- E_ITEM_UNAVAILABLE = 'E_ITEM_UNAVAILABLE',
27
- E_REMOTE_ERROR = 'E_REMOTE_ERROR',
28
- E_NETWORK_ERROR = 'E_NETWORK_ERROR',
29
- E_SERVICE_ERROR = 'E_SERVICE_ERROR',
30
- // ... and more
31
- }
32
- ```
33
-
34
- ### Platform-Specific Mappings
35
-
36
- The system automatically maps between the centralized enum and platform-specific codes:
37
-
38
- ```typescript
39
- export const ErrorCodeMapping = {
40
- ios: {
41
- [ErrorCode.E_USER_CANCELLED]: 2,
42
- [ErrorCode.E_SERVICE_ERROR]: 1,
43
- // ... more mappings
44
- },
45
- android: {
46
- [ErrorCode.E_USER_CANCELLED]: 'E_USER_CANCELLED',
47
- [ErrorCode.E_SERVICE_ERROR]: 'E_SERVICE_ERROR',
48
- // ... more mappings
49
- },
50
- };
51
- ```
52
-
53
- ## Usage
54
-
55
- ### Basic Error Handling
56
-
57
- ```typescript
58
- import {ErrorCode, PurchaseError} from 'expo-iap';
59
-
60
- // Handle purchase errors consistently
61
- const handleError = (error: PurchaseError) => {
62
- switch (error.code) {
63
- case ErrorCode.E_USER_CANCELLED:
64
- console.log('User cancelled the purchase');
65
- break;
66
- case ErrorCode.E_ITEM_UNAVAILABLE:
67
- console.log('Item is not available');
68
- break;
69
- default:
70
- console.log('Purchase failed:', error.message);
71
- }
72
- };
73
- ```
74
-
75
- ### Creating Errors from Platform Data
76
-
77
- ```typescript
78
- import {PurchaseError} from 'expo-iap';
79
-
80
- // Create properly typed errors from platform-specific data
81
- const error = PurchaseError.fromPlatformError(rawErrorData, 'ios');
82
- ```
83
-
84
- ### Error Code Utilities
85
-
86
- ```typescript
87
- import {ErrorCodeUtils, ErrorCode} from 'expo-iap';
88
-
89
- // Convert platform-specific codes to standard enum
90
- const errorCode = ErrorCodeUtils.fromPlatformCode(2, 'ios');
91
- // Returns ErrorCode.E_USER_CANCELLED
92
-
93
- // Convert standard enum to platform-specific code
94
- const iosCode = ErrorCodeUtils.toPlatformCode(
95
- ErrorCode.E_USER_CANCELLED,
96
- 'ios',
97
- );
98
- // Returns 2
99
-
100
- // Check platform support
101
- const isSupported = ErrorCodeUtils.isValidForPlatform(
102
- ErrorCode.E_USER_CANCELLED,
103
- 'ios',
104
- );
105
- // Returns true
106
- ```
107
-
108
- ## Migration Guide
109
-
110
- ### Before (Inconsistent)
111
-
112
- ```typescript
113
- // iOS would return numeric codes
114
- if (error.code === '2') {
115
- // Handle user cancellation
116
- }
117
-
118
- // Android would return string codes
119
- if (error.code === 'E_USER_CANCELLED') {
120
- // Handle user cancellation
121
- }
122
- ```
123
-
124
- ### After (Consistent)
125
-
126
- ```typescript
127
- import {ErrorCode} from 'expo-iap';
128
-
129
- // Works consistently across platforms
130
- if (error.code === ErrorCode.E_USER_CANCELLED) {
131
- // Handle user cancellation on both iOS and Android
132
- }
133
- ```
134
-
135
- ## Implementation Details
136
-
137
- ### iOS Changes
138
-
139
- - Removed the `IapErrors` enum from `Types.swift` entirely to eliminate native error code duplication
140
- - Updated all error throwing in `ExpoIapModule.swift` to use string error codes directly (e.g., "E_USER_CANCELLED", "E_SERVICE_ERROR")
141
- - Fixed user cancellation error to return `E_USER_CANCELLED` instead of the previous numeric code "2"
142
- - Native StoreKit errors are now passed through directly while custom errors use standardized string codes
143
-
144
- ### Android Changes
145
-
146
- - Android implementation already used proper string codes
147
- - No changes needed to Android error handling
148
-
149
- ### TypeScript Changes
150
-
151
- - Added comprehensive `ErrorCode` enum with all error types
152
- - Added `ErrorCodeMapping` for platform-specific code conversion
153
- - Added `ErrorCodeUtils` utility functions for error code management
154
- - Enhanced `PurchaseError` class with platform-aware error creation
155
-
156
- ## Benefits
157
-
158
- 1. **Consistency**: Same error codes across iOS and Android
159
- 2. **Type Safety**: Full TypeScript support with proper enums
160
- 3. **Maintainability**: Single source of truth for error codes
161
- 4. **Debugging**: Better error messages and debugging information
162
- 5. **Future-Proof**: Easy to add new error codes consistently
163
-
164
- ## Breaking Changes
165
-
166
- This is a **non-breaking change** for most users:
167
-
168
- - Existing error handling will continue to work
169
- - New error code system provides additional functionality
170
- - Users can migrate gradually to the new system
171
-
172
- The only potential breaking change is for iOS users who were checking for specific numeric error codes like "2" for user cancellation, which now correctly returns `ErrorCode.E_USER_CANCELLED`.