expo-iap 3.0.8 → 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.
- package/CLAUDE.md +2 -2
- package/CONTRIBUTING.md +19 -0
- package/README.md +18 -6
- package/android/build.gradle +24 -1
- package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +69 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +190 -59
- package/build/index.d.ts +20 -47
- package/build/index.d.ts.map +1 -1
- package/build/index.js +94 -137
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +2 -1
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +16 -1
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +29 -16
- package/build/modules/ios.js.map +1 -1
- package/build/types.d.ts +8 -6
- package/build/types.d.ts.map +1 -1
- package/build/types.js.map +1 -1
- package/build/useIAP.d.ts +1 -1
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +12 -15
- package/build/useIAP.js.map +1 -1
- package/build/utils/errorMapping.d.ts +32 -23
- package/build/utils/errorMapping.d.ts.map +1 -1
- package/build/utils/errorMapping.js +117 -22
- package/build/utils/errorMapping.js.map +1 -1
- package/ios/ExpoIap.podspec +3 -2
- package/ios/ExpoIapHelper.swift +96 -0
- package/ios/ExpoIapLog.swift +127 -0
- package/ios/ExpoIapModule.swift +218 -340
- package/openiap-versions.json +5 -0
- package/package.json +2 -2
- package/plugin/build/withIAP.js +6 -4
- package/plugin/src/withIAP.ts +14 -4
- package/scripts/update-types.mjs +20 -1
- package/src/index.ts +122 -165
- package/src/modules/android.ts +2 -1
- package/src/modules/ios.ts +31 -19
- package/src/types.ts +8 -6
- package/src/useIAP.ts +17 -25
- package/src/utils/errorMapping.ts +203 -23
- package/build/purchase-error.d.ts +0 -67
- package/build/purchase-error.d.ts.map +0 -1
- package/build/purchase-error.js +0 -166
- package/build/purchase-error.js.map +0 -1
- package/build/utils/purchase.d.ts +0 -9
- package/build/utils/purchase.d.ts.map +0 -1
- package/build/utils/purchase.js +0 -34
- package/build/utils/purchase.js.map +0 -1
- package/src/purchase-error.ts +0 -265
- package/src/utils/purchase.ts +0 -52
package/build/useIAP.js
CHANGED
|
@@ -49,6 +49,13 @@ export function useIAP(options) {
|
|
|
49
49
|
}
|
|
50
50
|
return type;
|
|
51
51
|
}, []);
|
|
52
|
+
const canonicalProductType = useCallback((value) => {
|
|
53
|
+
if (!value) {
|
|
54
|
+
return 'in-app';
|
|
55
|
+
}
|
|
56
|
+
const normalized = value.trim().toLowerCase().replace(/[_-]/g, '');
|
|
57
|
+
return normalized === 'subs' ? 'subs' : 'in-app';
|
|
58
|
+
}, []);
|
|
52
59
|
const toPurchaseInput = useCallback((purchase) => ({
|
|
53
60
|
id: purchase.id,
|
|
54
61
|
ids: purchase.ids ?? undefined,
|
|
@@ -60,16 +67,6 @@ export function useIAP(options) {
|
|
|
60
67
|
quantity: purchase.quantity,
|
|
61
68
|
transactionDate: purchase.transactionDate,
|
|
62
69
|
}), []);
|
|
63
|
-
const getSubscriptionsInternal = useCallback(async (skus) => {
|
|
64
|
-
try {
|
|
65
|
-
const result = await fetchProducts({ skus, type: 'subs' });
|
|
66
|
-
const subscriptionsResult = (result ?? []);
|
|
67
|
-
setSubscriptions((prevSubscriptions) => mergeWithDuplicateCheck(prevSubscriptions, subscriptionsResult, (subscription) => subscription.id));
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
console.error('Error fetching subscriptions:', error);
|
|
71
|
-
}
|
|
72
|
-
}, [mergeWithDuplicateCheck]);
|
|
73
70
|
const fetchProductsInternal = useCallback(async (params) => {
|
|
74
71
|
try {
|
|
75
72
|
const queryType = normalizeProductQueryType(params.type);
|
|
@@ -85,8 +82,8 @@ export function useIAP(options) {
|
|
|
85
82
|
setProducts((prevProducts) => mergeWithDuplicateCheck(prevProducts, productsResult, (product) => product.id));
|
|
86
83
|
}
|
|
87
84
|
else {
|
|
88
|
-
const productItems = items.filter((item) => item.type === 'in-app');
|
|
89
|
-
const subscriptionItems = items.filter((item) => item.type === 'subs');
|
|
85
|
+
const productItems = items.filter((item) => canonicalProductType(item.type) === 'in-app');
|
|
86
|
+
const subscriptionItems = items.filter((item) => canonicalProductType(item.type) === 'subs');
|
|
90
87
|
setProducts((prevProducts) => mergeWithDuplicateCheck(prevProducts, productItems, (product) => product.id));
|
|
91
88
|
setSubscriptions((prevSubscriptions) => mergeWithDuplicateCheck(prevSubscriptions, subscriptionItems, (subscription) => subscription.id));
|
|
92
89
|
}
|
|
@@ -94,7 +91,7 @@ export function useIAP(options) {
|
|
|
94
91
|
catch (error) {
|
|
95
92
|
console.error('Error fetching products:', error);
|
|
96
93
|
}
|
|
97
|
-
}, [mergeWithDuplicateCheck, normalizeProductQueryType]);
|
|
94
|
+
}, [canonicalProductType, mergeWithDuplicateCheck, normalizeProductQueryType]);
|
|
98
95
|
const getAvailablePurchasesInternal = useCallback(async () => {
|
|
99
96
|
try {
|
|
100
97
|
const result = await getAvailablePurchases({
|
|
@@ -138,14 +135,14 @@ export function useIAP(options) {
|
|
|
138
135
|
const refreshSubscriptionStatus = useCallback(async (productId) => {
|
|
139
136
|
try {
|
|
140
137
|
if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {
|
|
141
|
-
await
|
|
138
|
+
await fetchProductsInternal({ skus: [productId], type: 'subs' });
|
|
142
139
|
await getAvailablePurchasesInternal();
|
|
143
140
|
}
|
|
144
141
|
}
|
|
145
142
|
catch (error) {
|
|
146
143
|
console.warn('Failed to refresh subscription status:', error);
|
|
147
144
|
}
|
|
148
|
-
}, [
|
|
145
|
+
}, [fetchProductsInternal, getAvailablePurchasesInternal]);
|
|
149
146
|
// Restore completed transactions with cross-platform behavior.
|
|
150
147
|
// iOS: best-effort sync (ignore sync errors) then fetch available purchases.
|
|
151
148
|
// Android: fetch available purchases directly.
|
package/build/useIAP.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIAP.js","sourceRoot":"","sources":["../src/useIAP.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\n// Internal modules\nimport {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n promotedProductListenerIOS,\n getAvailablePurchases,\n finishTransaction as finishTransactionInternal,\n requestPurchase as requestPurchaseInternal,\n fetchProducts,\n validateReceipt as validateReceiptInternal,\n getActiveSubscriptions,\n hasActiveSubscriptions,\n type ActiveSubscription,\n type ProductTypeInput,\n restorePurchases,\n} from './index';\nimport {\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n} from './modules/ios';\n\n// Types\nimport type {\n Product,\n ProductSubscription,\n ProductQueryType,\n ProductRequest,\n Purchase,\n MutationRequestPurchaseArgs,\n PurchaseInput,\n ReceiptValidationProps,\n ReceiptValidationResult,\n} from './types';\nimport {ErrorCode} from './types';\nimport {PurchaseError} from './purchase-error';\nimport {\n getUserFriendlyErrorMessage,\n isUserCancelledError,\n isRecoverableError,\n} from './utils/errorMapping';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: Purchase[];\n promotedProductIdIOS?: string;\n subscriptions: ProductSubscription[];\n availablePurchases: Purchase[];\n promotedProductIOS?: Product;\n activeSubscriptions: ActiveSubscription[];\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<void>;\n getAvailablePurchases: () => Promise<void>;\n fetchProducts: (params: {\n skus: string[];\n type?: ProductTypeInput;\n }) => Promise<void>;\n\n requestPurchase: (\n params: MutationRequestPurchaseArgs,\n ) => ReturnType<typeof requestPurchaseInternal>;\n validateReceipt: (\n props: ReceiptValidationProps,\n ) => Promise<ReceiptValidationResult>;\n restorePurchases: () => Promise<void>;\n getPromotedProductIOS: () => Promise<Product | null>;\n requestPurchaseOnPromotedProductIOS: () => Promise<boolean>;\n getActiveSubscriptions: (subscriptionIds?: string[]) => Promise<void>;\n hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean>;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (purchase: Purchase) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n onPromotedProductIOS?: (product: Product) => void;\n}\n\n/**\n * React Hook for managing In-App Purchases.\n * See documentation at https://hyochan.github.io/expo-iap/docs/hooks/useIAP\n */\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS] = useState<Purchase[]>([]);\n const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);\n\n const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);\n const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();\n const [promotedProductIdIOS] = useState<string>();\n const [activeSubscriptions, setActiveSubscriptions] = useState<\n ActiveSubscription[]\n >([]);\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n const connectedRef = useRef<boolean>(false);\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 useEffect(() => {\n connectedRef.current = connected;\n }, [connected]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIOS?: EventSubscription;\n promotedProductIOS?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<ProductSubscription[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const normalizeProductQueryType = useCallback(\n (type?: ProductTypeInput): ProductQueryType => {\n if (!type || type === 'inapp' || type === 'in-app') {\n return 'in-app';\n }\n return type;\n },\n [],\n );\n\n const toPurchaseInput = useCallback(\n (purchase: Purchase): PurchaseInput => ({\n id: purchase.id,\n ids: purchase.ids ?? undefined,\n isAutoRenewing: purchase.isAutoRenewing,\n platform: purchase.platform,\n productId: purchase.productId,\n purchaseState: purchase.purchaseState,\n purchaseToken: purchase.purchaseToken ?? null,\n quantity: purchase.quantity,\n transactionDate: purchase.transactionDate,\n }),\n [],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await fetchProducts({skus, type: 'subs'});\n const subscriptionsResult = (result ?? []) as ProductSubscription[];\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionsResult,\n (subscription) => subscription.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching subscriptions:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const fetchProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: ProductTypeInput;\n }): Promise<void> => {\n try {\n const queryType = normalizeProductQueryType(params.type);\n const request: ProductRequest = {skus: params.skus, type: queryType};\n const result = await fetchProducts(request);\n const items = (result ?? []) as (Product | ProductSubscription)[];\n\n if (queryType === 'subs') {\n const subscriptionsResult = items as ProductSubscription[];\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionsResult,\n (subscription) => subscription.id,\n ),\n );\n } else if (queryType === 'in-app') {\n const productsResult = items as Product[];\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productsResult,\n (product) => product.id,\n ),\n );\n } else {\n const productItems = items.filter(\n (item) => item.type === 'in-app',\n ) as Product[];\n const subscriptionItems = items.filter(\n (item) => item.type === 'subs',\n ) as ProductSubscription[];\n\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productItems,\n (product) => product.id,\n ),\n );\n\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionItems,\n (subscription) => subscription.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [mergeWithDuplicateCheck, normalizeProductQueryType],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<void> => {\n try {\n const result = await getActiveSubscriptions(subscriptionIds);\n setActiveSubscriptions(result);\n } catch (error) {\n console.error('Error getting active subscriptions:', error);\n // Preserve existing state on error\n }\n },\n [],\n );\n\n const hasActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<boolean> => {\n try {\n return await hasActiveSubscriptions(subscriptionIds);\n } catch (error) {\n console.error('Error checking active subscriptions:', error);\n return false;\n }\n },\n [],\n );\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }): Promise<void> => {\n await finishTransactionInternal({\n purchase: toPurchaseInput(purchase),\n isConsumable,\n });\n },\n [toPurchaseInput],\n );\n\n const requestPurchaseWithReset = useCallback(\n (requestObj: MutationRequestPurchaseArgs) => {\n return requestPurchaseInternal(requestObj);\n },\n [],\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 // Restore completed transactions with cross-platform behavior.\n // iOS: best-effort sync (ignore sync errors) then fetch available purchases.\n // Android: fetch available purchases directly.\n const restorePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n await restorePurchases();\n const purchases = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(purchases);\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, []);\n\n const validateReceipt = useCallback(async (props: ReceiptValidationProps) => {\n return validateReceiptInternal(props);\n }, []);\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n // CRITICAL: Register listeners BEFORE initConnection to avoid race condition\n // Events might fire immediately after initConnection, so listeners must be ready\n // Register purchase update listener BEFORE initConnection to avoid race conditions.\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase) => {\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 // Register purchase error listener EARLY. Ignore init-related errors until connected.\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n if (!connectedRef.current && error.code === ErrorCode.InitConnection) {\n return; // Ignore initialization error before connected\n }\n const friendly = getUserFriendlyErrorMessage(error);\n if (!isUserCancelledError(error) && !isRecoverableError(error)) {\n console.warn('[useIAP] Purchase error:', friendly);\n }\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products listener\n subscriptionsRef.current.promotedProductsIOS = promotedProductListenerIOS(\n (product: Product) => {\n setPromotedProductIOS(product);\n\n if (optionsRef.current?.onPromotedProductIOS) {\n optionsRef.current.onPromotedProductIOS(product);\n }\n },\n );\n }\n\n // NOW call initConnection after listeners are ready\n const result = await initConnection();\n setConnected(result);\n if (!result) {\n // If connection failed, clean up listeners\n console.warn('[useIAP] Connection failed, cleaning up listeners...');\n subscriptionsRef.current.purchaseUpdate?.remove();\n subscriptionsRef.current.promotedProductsIOS?.remove();\n subscriptionsRef.current.purchaseUpdate = undefined;\n subscriptionsRef.current.promotedProductsIOS = undefined;\n // Keep purchaseError listener registered to capture subsequent retries\n return;\n }\n }, [refreshSubscriptionStatus]);\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 currentSubscriptions.promotedProductIOS?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n promotedProductIdIOS,\n subscriptions,\n finishTransaction,\n availablePurchases,\n promotedProductIOS,\n activeSubscriptions,\n getAvailablePurchases: getAvailablePurchasesInternal,\n fetchProducts: fetchProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases: restorePurchasesInternal,\n // internal getters kept for hook state management\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n getActiveSubscriptions: getActiveSubscriptionsInternal,\n hasActiveSubscriptions: hasActiveSubscriptionsInternal,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useIAP.js","sourceRoot":"","sources":["../src/useIAP.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\n// Internal modules\nimport {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n promotedProductListenerIOS,\n getAvailablePurchases,\n finishTransaction as finishTransactionInternal,\n requestPurchase as requestPurchaseInternal,\n fetchProducts,\n validateReceipt as validateReceiptInternal,\n getActiveSubscriptions,\n hasActiveSubscriptions,\n type ActiveSubscription,\n type ProductTypeInput,\n restorePurchases,\n} from './index';\nimport {\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n} from './modules/ios';\n\n// Types\nimport type {\n Product,\n ProductSubscription,\n ProductQueryType,\n ProductRequest,\n Purchase,\n MutationRequestPurchaseArgs,\n PurchaseInput,\n ReceiptValidationProps,\n ReceiptValidationResult,\n} from './types';\nimport {ErrorCode} from './types';\nimport type {PurchaseError} from './utils/errorMapping';\nimport {\n getUserFriendlyErrorMessage,\n isUserCancelledError,\n isRecoverableError,\n} from './utils/errorMapping';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: Purchase[];\n promotedProductIdIOS?: string;\n subscriptions: ProductSubscription[];\n availablePurchases: Purchase[];\n promotedProductIOS?: Product;\n activeSubscriptions: ActiveSubscription[];\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<void>;\n getAvailablePurchases: () => Promise<void>;\n fetchProducts: (params: {\n skus: string[];\n type?: ProductTypeInput;\n }) => Promise<void>;\n\n requestPurchase: (\n params: MutationRequestPurchaseArgs,\n ) => ReturnType<typeof requestPurchaseInternal>;\n validateReceipt: (\n props: ReceiptValidationProps,\n ) => Promise<ReceiptValidationResult>;\n restorePurchases: () => Promise<void>;\n getPromotedProductIOS: () => Promise<Product | null>;\n requestPurchaseOnPromotedProductIOS: () => Promise<boolean>;\n getActiveSubscriptions: (subscriptionIds?: string[]) => Promise<void>;\n hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean>;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (purchase: Purchase) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n onPromotedProductIOS?: (product: Product) => void;\n}\n\n/**\n * React Hook for managing In-App Purchases.\n * See documentation at https://hyochan.github.io/expo-iap/docs/hooks/useIAP\n */\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS] = useState<Purchase[]>([]);\n const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);\n\n const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);\n const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();\n const [promotedProductIdIOS] = useState<string>();\n const [activeSubscriptions, setActiveSubscriptions] = useState<\n ActiveSubscription[]\n >([]);\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n const connectedRef = useRef<boolean>(false);\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 useEffect(() => {\n connectedRef.current = connected;\n }, [connected]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIOS?: EventSubscription;\n promotedProductIOS?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<ProductSubscription[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const normalizeProductQueryType = useCallback(\n (type?: ProductTypeInput): ProductQueryType => {\n if (!type || type === 'inapp' || type === 'in-app') {\n return 'in-app';\n }\n return type;\n },\n [],\n );\n\n const canonicalProductType = useCallback(\n (value?: string): ProductQueryType => {\n if (!value) {\n return 'in-app';\n }\n const normalized = value.trim().toLowerCase().replace(/[_-]/g, '');\n return normalized === 'subs' ? 'subs' : 'in-app';\n },\n [],\n );\n\n const toPurchaseInput = useCallback(\n (purchase: Purchase): PurchaseInput => ({\n id: purchase.id,\n ids: purchase.ids ?? undefined,\n isAutoRenewing: purchase.isAutoRenewing,\n platform: purchase.platform,\n productId: purchase.productId,\n purchaseState: purchase.purchaseState,\n purchaseToken: purchase.purchaseToken ?? null,\n quantity: purchase.quantity,\n transactionDate: purchase.transactionDate,\n }),\n [],\n );\n\n const fetchProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: ProductTypeInput;\n }): Promise<void> => {\n try {\n const queryType = normalizeProductQueryType(params.type);\n const request: ProductRequest = {skus: params.skus, type: queryType};\n const result = await fetchProducts(request);\n const items = (result ?? []) as (Product | ProductSubscription)[];\n\n if (queryType === 'subs') {\n const subscriptionsResult = items as ProductSubscription[];\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionsResult,\n (subscription) => subscription.id,\n ),\n );\n } else if (queryType === 'in-app') {\n const productsResult = items as Product[];\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productsResult,\n (product) => product.id,\n ),\n );\n } else {\n const productItems = items.filter(\n (item) => canonicalProductType(item.type as string) === 'in-app',\n ) as Product[];\n const subscriptionItems = items.filter(\n (item) => canonicalProductType(item.type as string) === 'subs',\n ) as ProductSubscription[];\n\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productItems,\n (product) => product.id,\n ),\n );\n\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionItems,\n (subscription) => subscription.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [canonicalProductType, mergeWithDuplicateCheck, normalizeProductQueryType],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<void> => {\n try {\n const result = await getActiveSubscriptions(subscriptionIds);\n setActiveSubscriptions(result);\n } catch (error) {\n console.error('Error getting active subscriptions:', error);\n // Preserve existing state on error\n }\n },\n [],\n );\n\n const hasActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<boolean> => {\n try {\n return await hasActiveSubscriptions(subscriptionIds);\n } catch (error) {\n console.error('Error checking active subscriptions:', error);\n return false;\n }\n },\n [],\n );\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }): Promise<void> => {\n await finishTransactionInternal({\n purchase: toPurchaseInput(purchase),\n isConsumable,\n });\n },\n [toPurchaseInput],\n );\n\n const requestPurchaseWithReset = useCallback(\n (requestObj: MutationRequestPurchaseArgs) => {\n return requestPurchaseInternal(requestObj);\n },\n [],\n );\n\n const refreshSubscriptionStatus = useCallback(\n async (productId: string) => {\n try {\n if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {\n await fetchProductsInternal({skus: [productId], type: 'subs'});\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n },\n [fetchProductsInternal, getAvailablePurchasesInternal],\n );\n\n // Restore completed transactions with cross-platform behavior.\n // iOS: best-effort sync (ignore sync errors) then fetch available purchases.\n // Android: fetch available purchases directly.\n const restorePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n await restorePurchases();\n const purchases = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(purchases);\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, []);\n\n const validateReceipt = useCallback(async (props: ReceiptValidationProps) => {\n return validateReceiptInternal(props);\n }, []);\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n // CRITICAL: Register listeners BEFORE initConnection to avoid race condition\n // Events might fire immediately after initConnection, so listeners must be ready\n // Register purchase update listener BEFORE initConnection to avoid race conditions.\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase) => {\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 // Register purchase error listener EARLY. Ignore init-related errors until connected.\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n if (!connectedRef.current && error.code === ErrorCode.InitConnection) {\n return; // Ignore initialization error before connected\n }\n const friendly = getUserFriendlyErrorMessage(error);\n if (!isUserCancelledError(error) && !isRecoverableError(error)) {\n console.warn('[useIAP] Purchase error:', friendly);\n }\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products listener\n subscriptionsRef.current.promotedProductsIOS = promotedProductListenerIOS(\n (product: Product) => {\n setPromotedProductIOS(product);\n\n if (optionsRef.current?.onPromotedProductIOS) {\n optionsRef.current.onPromotedProductIOS(product);\n }\n },\n );\n }\n\n // NOW call initConnection after listeners are ready\n const result = await initConnection();\n setConnected(result);\n if (!result) {\n // If connection failed, clean up listeners\n console.warn('[useIAP] Connection failed, cleaning up listeners...');\n subscriptionsRef.current.purchaseUpdate?.remove();\n subscriptionsRef.current.promotedProductsIOS?.remove();\n subscriptionsRef.current.purchaseUpdate = undefined;\n subscriptionsRef.current.promotedProductsIOS = undefined;\n // Keep purchaseError listener registered to capture subsequent retries\n return;\n }\n }, [refreshSubscriptionStatus]);\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 currentSubscriptions.promotedProductIOS?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n promotedProductIdIOS,\n subscriptions,\n finishTransaction,\n availablePurchases,\n promotedProductIOS,\n activeSubscriptions,\n getAvailablePurchases: getAvailablePurchasesInternal,\n fetchProducts: fetchProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases: restorePurchasesInternal,\n // internal getters kept for hook state management\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n getActiveSubscriptions: getActiveSubscriptionsInternal,\n hasActiveSubscriptions: hasActiveSubscriptionsInternal,\n };\n}\n"]}
|
|
@@ -1,35 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Error mapping utilities for expo-iap
|
|
3
|
-
* Provides
|
|
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
|
*/
|
|
5
|
-
import { ErrorCode } from '../types';
|
|
6
|
+
import { ErrorCode, IapPlatform } from '../types';
|
|
7
|
+
export interface PurchaseErrorProps {
|
|
8
|
+
message: string;
|
|
9
|
+
responseCode?: number;
|
|
10
|
+
debugMessage?: string;
|
|
11
|
+
code?: ErrorCode;
|
|
12
|
+
productId?: string;
|
|
13
|
+
platform?: IapPlatform;
|
|
14
|
+
}
|
|
15
|
+
type PlatformErrorData = {
|
|
16
|
+
code?: string | number;
|
|
17
|
+
message?: string;
|
|
18
|
+
responseCode?: number;
|
|
19
|
+
debugMessage?: string;
|
|
20
|
+
productId?: string;
|
|
21
|
+
};
|
|
22
|
+
export type PurchaseError = Error & PurchaseErrorProps;
|
|
23
|
+
export declare const ErrorCodeMapping: {
|
|
24
|
+
readonly ios: Record<ErrorCode, string>;
|
|
25
|
+
readonly android: Record<ErrorCode, string>;
|
|
26
|
+
};
|
|
27
|
+
export declare const createPurchaseError: (props: PurchaseErrorProps) => PurchaseError;
|
|
28
|
+
export declare const createPurchaseErrorFromPlatform: (errorData: PlatformErrorData, platform: IapPlatform) => PurchaseError;
|
|
29
|
+
export declare const ErrorCodeUtils: {
|
|
30
|
+
getNativeErrorCode: (errorCode: ErrorCode) => string;
|
|
31
|
+
fromPlatformCode: (platformCode: string | number, _platform: IapPlatform) => ErrorCode;
|
|
32
|
+
toPlatformCode: (errorCode: ErrorCode, _platform: IapPlatform) => string | number;
|
|
33
|
+
isValidForPlatform: (errorCode: ErrorCode, platform: IapPlatform) => boolean;
|
|
34
|
+
};
|
|
6
35
|
type ErrorLike = string | {
|
|
7
36
|
code?: ErrorCode | string;
|
|
8
37
|
message?: string;
|
|
9
38
|
};
|
|
10
|
-
/**
|
|
11
|
-
* Checks if an error is a user cancellation
|
|
12
|
-
* @param error Error object or error code
|
|
13
|
-
* @returns True if the error represents user cancellation
|
|
14
|
-
*/
|
|
15
39
|
export declare function isUserCancelledError(error: unknown): boolean;
|
|
16
|
-
/**
|
|
17
|
-
* Checks if an error is related to network connectivity
|
|
18
|
-
* @param error Error object or error code
|
|
19
|
-
* @returns True if the error is network-related
|
|
20
|
-
*/
|
|
21
40
|
export declare function isNetworkError(error: unknown): boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Checks if an error is recoverable (user can retry)
|
|
24
|
-
* @param error Error object or error code
|
|
25
|
-
* @returns True if the error is potentially recoverable
|
|
26
|
-
*/
|
|
27
41
|
export declare function isRecoverableError(error: unknown): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Gets a user-friendly error message for display
|
|
30
|
-
* @param error Error object or error code
|
|
31
|
-
* @returns User-friendly error message
|
|
32
|
-
*/
|
|
33
42
|
export declare function getUserFriendlyErrorMessage(error: ErrorLike): string;
|
|
34
43
|
export {};
|
|
35
44
|
//# sourceMappingURL=errorMapping.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorMapping.d.ts","sourceRoot":"","sources":["../../src/utils/errorMapping.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errorMapping.d.ts","sourceRoot":"","sources":["../../src/utils/errorMapping.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,UAAU,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,KAAK,iBAAiB,GAAG;IACvB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,kBAAkB,CAAC;AA6DvD,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAMX,eAAO,MAAM,mBAAmB,GAC9B,OAAO,kBAAkB,KACxB,aASF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,WAAW,iBAAiB,EAC5B,UAAU,WAAW,KACpB,aAcF,CAAC;AAEF,eAAO,MAAM,cAAc;oCACO,SAAS,KAAG,MAAM;qCASlC,MAAM,GAAG,MAAM,aAClB,WAAW,KACrB,SAAS;gCAuCC,SAAS,aACT,WAAW,KACrB,MAAM,GAAG,MAAM;oCAQL,SAAS,YACV,WAAW,KACpB,OAAO;CAUX,CAAC;AAMF,KAAK,SAAS,GAAG,MAAM,GAAG;IAAC,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,CAAC;AAmCxE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWtD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAc1D;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAoDpE"}
|
|
@@ -1,8 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Error mapping utilities for expo-iap
|
|
3
|
-
* Provides
|
|
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"]}
|
package/ios/ExpoIap.podspec
CHANGED
|
@@ -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.
|
|
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', '
|
|
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
|
+
}
|