expo-iap 2.7.3 → 2.7.5-rc.1
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/.copilot-instructions.md +8 -26
- package/CHANGELOG.md +9 -10
- package/CONTRIBUTING.md +2 -25
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js.map +1 -1
- package/build/index.d.ts +25 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +31 -3
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +20 -0
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +24 -0
- package/build/modules/ios.js.map +1 -1
- package/build/types/ExpoIapIos.types.d.ts +2 -2
- package/build/types/ExpoIapIos.types.d.ts.map +1 -1
- package/build/types/ExpoIapIos.types.js.map +1 -1
- package/build/useIap.d.ts +4 -1
- package/build/useIap.d.ts.map +1 -1
- package/build/useIap.js +15 -14
- package/build/useIap.js.map +1 -1
- package/ios/ExpoIap.podspec +1 -1
- package/ios/ExpoIapModule.swift +98 -1
- package/package.json +1 -1
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/ExpoIap.types.ts +8 -17
- package/src/index.ts +59 -23
- package/src/modules/android.ts +4 -2
- package/src/modules/ios.ts +69 -83
- package/src/types/ExpoIapIos.types.ts +2 -2
- package/src/useIap.ts +26 -24
- package/build/utils/smartPurchase.d.ts +0 -2
- package/build/utils/smartPurchase.d.ts.map +0 -1
- package/build/utils/smartPurchase.js +0 -2
- package/build/utils/smartPurchase.js.map +0 -1
- package/src/utils/smartPurchase.ts +0 -0
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 getProducts,\n getAvailablePurchases,\n getPurchaseHistories,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n requestProducts,\n} from './';\nimport {syncIOS, validateReceiptIOS} from './modules/ios';\nimport {validateReceiptAndroid} from './modules/android';\n\n// Types\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n RequestPurchaseProps,\n RequestSubscriptionProps,\n} from './ExpoIap.types';\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 requestProducts: (params: {\n skus: string[];\n type?: 'inapp' | 'subs';\n }) => Promise<void>;\n requestPurchase: (params: {\n request: RequestPurchaseProps | RequestSubscriptionProps;\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 requestProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: 'inapp' | 'subs';\n }): Promise<void> => {\n try {\n const result = await requestProducts(params);\n if (params.type === 'subs') {\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n result as SubscriptionProduct[],\n (subscription) => subscription.id,\n ),\n );\n } else {\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n result as Product[],\n (product) => product.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', 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 getPurchaseHistories());\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 syncIOS().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 requestProducts: requestProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAAA,wBAAwB;AACxB,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAC/D,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AAGtC,mBAAmB;AACnB,OAAO,EACL,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,IAAI,yBAAyB,EAC9C,gBAAgB,EAChB,eAAe,IAAI,uBAAuB,EAC1C,eAAe,GAChB,MAAM,IAAI,CAAC;AACZ,OAAO,EACL,OAAO,EACP,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AAqEzD,MAAM,UAAU,MAAM,CAAC,OAAuB;IAC5C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC3D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAY,EAAE,CAAC,CAAC;IACxD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAwB,EAAE,CAAC,CAAC;IAC9E,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CACxD,EAAE,CACH,CAAC;IACF,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAE1D,EAAE,CAAC,CAAC;IACN,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,EAAmB,CAAC;IAC1E,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,EAAW,CAAC;IACxE,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GACnD,QAAQ,EAAiB,CAAC;IAE5B,MAAM,UAAU,GAAG,MAAM,CAA4B,OAAO,CAAC,CAAC;IAE9D,0DAA0D;IAC1D,MAAM,uBAAuB,GAAG,WAAW,CACzC,CACE,aAAkB,EAClB,QAAa,EACb,MAA2B,EACtB,EAAE;QACP,MAAM,MAAM,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC;QAClC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAC7B,CAAC,YAAY,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,CAC3D,CAAC;YACF,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC,EACD,EAAE,CACH,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC/B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,gBAAgB,GAAG,MAAM,CAI5B,EAAE,CAAC,CAAC;IAEP,MAAM,qBAAqB,GAAG,MAAM,CAAwB,EAAE,CAAC,CAAC;IAEhE,SAAS,CAAC,GAAG,EAAE;QACb,qBAAqB,CAAC,OAAO,GAAG,aAAa,CAAC;IAChD,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;IAEpB,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5C,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,yBAAyB,GAAG,WAAW,CAAC,GAAG,EAAE;QACjD,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,mBAAmB,GAAG,WAAW,CACrC,KAAK,EAAE,IAAc,EAAiB,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;YACvC,WAAW,CAAC,CAAC,YAAY,EAAE,EAAE,CAC3B,uBAAuB,CACrB,YAAY,EACZ,MAAM,EACN,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CACxB,CACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,EACD,CAAC,uBAAuB,CAAC,CAC1B,CAAC;IAEF,MAAM,wBAAwB,GAAG,WAAW,CAC1C,KAAK,EAAE,IAAc,EAAiB,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC5C,gBAAgB,CAAC,CAAC,iBAAiB,EAAE,EAAE,CACrC,uBAAuB,CACrB,iBAAiB,EACjB,MAAM,EACN,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAClC,CACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,EACD,CAAC,uBAAuB,CAAC,CAC1B,CAAC;IAEF,MAAM,uBAAuB,GAAG,WAAW,CACzC,KAAK,EAAE,MAGN,EAAiB,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,gBAAgB,CAAC,CAAC,iBAAiB,EAAE,EAAE,CACrC,uBAAuB,CACrB,iBAAiB,EACjB,MAA+B,EAC/B,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAClC,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,WAAW,CAAC,CAAC,YAAY,EAAE,EAAE,CAC3B,uBAAuB,CACrB,YAAY,EACZ,MAAmB,EACnB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CACxB,CACF,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,EACD,CAAC,uBAAuB,CAAC,CAC1B,CAAC;IAEF,MAAM,6BAA6B,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QAC1E,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,qBAAqB,EAAE,CAAC;YAC7C,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,4BAA4B,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACzE,oBAAoB,CAAC,MAAM,oBAAoB,EAAE,CAAC,CAAC;IACrD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,iBAAiB,GAAG,WAAW,CACnC,KAAK,EAAE,EACL,QAAQ,EACR,YAAY,GAIb,EAAqC,EAAE;QACtC,IAAI,CAAC;YACH,OAAO,MAAM,yBAAyB,CAAC;gBACrC,QAAQ;gBACR,YAAY;aACb,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,QAAQ,CAAC,EAAE,KAAK,eAAe,EAAE,EAAE,EAAE,CAAC;gBACxC,oBAAoB,EAAE,CAAC;YACzB,CAAC;YACD,IAAI,QAAQ,CAAC,EAAE,KAAK,oBAAoB,EAAE,SAAS,EAAE,CAAC;gBACpD,yBAAyB,EAAE,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC,EACD;QACE,eAAe,EAAE,EAAE;QACnB,oBAAoB,EAAE,SAAS;QAC/B,oBAAoB;QACpB,yBAAyB;KAC1B,CACF,CAAC;IAEF,MAAM,wBAAwB,GAAG,WAAW,CAC1C,KAAK,EAAE,UAAmD,EAAE,EAAE;QAC5D,oBAAoB,EAAE,CAAC;QACvB,yBAAyB,EAAE,CAAC;QAE5B,IAAI,CAAC;YACH,OAAO,MAAM,uBAAuB,CAAC,UAAU,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,EACD,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,CAClD,CAAC;IAEF,MAAM,yBAAyB,GAAG,WAAW,CAC3C,KAAK,EAAE,SAAiB,EAAE,EAAE;QAC1B,IAAI,CAAC;YACH,IAAI,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC;gBACtE,MAAM,wBAAwB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;gBAC5C,MAAM,6BAA6B,EAAE,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,EACD,CAAC,6BAA6B,EAAE,wBAAwB,CAAC,CAC1D,CAAC;IAEF,MAAM,gBAAgB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QAC7D,IAAI,CAAC;YACH,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1B,MAAM,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC9B,IAAI,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;wBACpC,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;oBACxC,CAAC;yBAAM,CAAC;wBACN,OAAO,CAAC,IAAI,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;oBACpD,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;YACD,MAAM,6BAA6B,EAAE,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,EAAE,CAAC,6BAA6B,CAAC,CAAC,CAAC;IAEpC,MAAM,eAAe,GAAG,WAAW,CACjC,KAAK,EACH,GAAW,EACX,cAKC,EACD,EAAE;QACF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YACrC,IACE,CAAC,cAAc;gBACf,CAAC,cAAc,CAAC,WAAW;gBAC3B,CAAC,cAAc,CAAC,YAAY;gBAC5B,CAAC,cAAc,CAAC,WAAW,EAC3B,CAAC;gBACD,MAAM,IAAI,KAAK,CACb,wEAAwE,CACzE,CAAC;YACJ,CAAC;YACD,OAAO,MAAM,sBAAsB,CAAC;gBAClC,WAAW,EAAE,cAAc,CAAC,WAAW;gBACvC,SAAS,EAAE,GAAG;gBACd,YAAY,EAAE,cAAc,CAAC,YAAY;gBACzC,WAAW,EAAE,cAAc,CAAC,WAAW;gBACvC,KAAK,EAAE,cAAc,CAAC,KAAK;aAC5B,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,wBAAwB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACrE,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;QACtC,YAAY,CAAC,MAAM,CAAC,CAAC;QAErB,IAAI,MAAM,EAAE,CAAC;YACX,gBAAgB,CAAC,OAAO,CAAC,cAAc,GAAG,uBAAuB,CAC/D,KAAK,EAAE,QAAyC,EAAE,EAAE;gBAClD,uBAAuB,CAAC,SAAS,CAAC,CAAC;gBACnC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBAE7B,IAAI,mBAAmB,IAAI,QAAQ,EAAE,CAAC;oBACpC,MAAM,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBAC/C,CAAC;gBAED,IAAI,UAAU,CAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;oBAC1C,UAAU,CAAC,OAAO,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC,CACF,CAAC;YAEF,gBAAgB,CAAC,OAAO,CAAC,aAAa,GAAG,qBAAqB,CAC5D,CAAC,KAAoB,EAAE,EAAE;gBACvB,kBAAkB,CAAC,SAAS,CAAC,CAAC;gBAC9B,uBAAuB,CAAC,KAAK,CAAC,CAAC;gBAE/B,IAAI,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC;oBACxC,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;gBAC5C,CAAC;YACH,CAAC,CACF,CAAC;YAEF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1B,iCAAiC;gBACjC,gBAAgB,CAAC,OAAO,CAAC,mBAAmB;oBAC1C,0BAA0B,CAAC,CAAC,OAAgB,EAAE,EAAE;wBAC9C,qBAAqB,CAAC,OAAO,CAAC,CAAC;wBAE/B,IAAI,UAAU,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC;4BAC7C,UAAU,CAAC,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;wBACnD,CAAC;oBACH,CAAC,CAAC,CAAC;YACP,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,CAAC;IAEhC,SAAS,CAAC,GAAG,EAAE;QACb,wBAAwB,EAAE,CAAC;QAC3B,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAEtD,OAAO,GAAG,EAAE;YACV,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;YAC9C,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;YAC7C,oBAAoB,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;YACnD,aAAa,EAAE,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAE/B,OAAO;QACL,SAAS;QACT,QAAQ;QACR,aAAa;QACb,iBAAiB;QACjB,iBAAiB;QACjB,kBAAkB;QAClB,eAAe;QACf,oBAAoB;QACpB,kBAAkB;QAClB,oBAAoB;QACpB,yBAAyB;QACzB,qBAAqB,EAAE,6BAA6B;QACpD,oBAAoB,EAAE,4BAA4B;QAClD,WAAW,EAAE,mBAAmB;QAChC,gBAAgB,EAAE,wBAAwB;QAC1C,eAAe,EAAE,uBAAuB;QACxC,eAAe,EAAE,wBAAwB;QACzC,eAAe;QACf,gBAAgB;QAChB,qBAAqB,EACnB,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI;QAClE,qBAAqB,EACnB,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC;KACjE,CAAC;AACJ,CAAC","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 getProducts,\n getAvailablePurchases,\n getPurchaseHistories,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\n requestProducts,\n} from './';\nimport {\n syncIOS,\n validateReceiptIOS,\n getPromotedProductIOS,\n buyPromotedProductIOS,\n} from './modules/ios';\nimport {validateReceiptAndroid} from './modules/android';\n\n// Types\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n RequestPurchaseProps,\n RequestSubscriptionProps,\n} from './ExpoIap.types';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n promotedProductIOS?: Product;\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 requestProducts: (params: {\n skus: string[];\n type?: 'inapp' | 'subs';\n }) => Promise<void>;\n requestPurchase: (params: {\n request: RequestPurchaseProps | RequestSubscriptionProps;\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 getPromotedProductIOS: () => Promise<any | null>;\n buyPromotedProductIOS: () => 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 onPromotedProductIOS?: (product: Product) => void;\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\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 [promotedProductIOS, setPromotedProductIOS] = useState<Product>();\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 requestProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: 'inapp' | 'subs';\n }): Promise<void> => {\n try {\n const result = await requestProducts(params);\n if (params.type === 'subs') {\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n result as SubscriptionProduct[],\n (subscription) => subscription.id,\n ),\n );\n } else {\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n result as Product[],\n (product) => product.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', 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 getPurchaseHistories());\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 syncIOS().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 listener\n subscriptionsRef.current.promotedProductsIos =\n promotedProductListenerIOS((product: Product) => {\n setPromotedProductIOS(product);\n\n if (optionsRef.current?.onPromotedProductIOS) {\n optionsRef.current.onPromotedProductIOS(product);\n }\n });\n }\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 endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n promotedProductIOS,\n clearCurrentPurchase,\n clearCurrentPurchaseError,\n getAvailablePurchases: getAvailablePurchasesInternal,\n getPurchaseHistories: getPurchaseHistoriesInternal,\n getProducts: getProductsInternal,\n getSubscriptions: getSubscriptionsInternal,\n requestProducts: requestProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n getPromotedProductIOS:\n Platform.OS === 'ios' ? getPromotedProductIOS : async () => null,\n buyPromotedProductIOS:\n Platform.OS === 'ios' ? buyPromotedProductIOS : async () => {},\n };\n}\n"]}
|
package/ios/ExpoIap.podspec
CHANGED
|
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package['license']
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
|
-
s.platforms = { :ios => '
|
|
13
|
+
s.platforms = { :ios => '15.0', :tvos => '15.0' }
|
|
14
14
|
s.swift_version = '5.4'
|
|
15
15
|
s.source = { git: 'https://github.com/hyochan/expo-iap' }
|
|
16
16
|
s.static_framework = true
|
package/ios/ExpoIapModule.swift
CHANGED
|
@@ -18,6 +18,7 @@ func logDebug(_ message: String) {
|
|
|
18
18
|
struct IapEvent {
|
|
19
19
|
static let PurchaseUpdated = "purchase-updated"
|
|
20
20
|
static let PurchaseError = "purchase-error"
|
|
21
|
+
static let PromotedProductIOS = "promoted-product-ios"
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
@available(iOS 15.0, *)
|
|
@@ -225,6 +226,9 @@ public class ExpoIapModule: Module {
|
|
|
225
226
|
private var updateListenerTask: Task<Void, Error>?
|
|
226
227
|
private var subscriptionPollingTask: Task<Void, Error>?
|
|
227
228
|
private var pollingSkus: Set<String> = []
|
|
229
|
+
private var paymentObserver: PaymentObserver?
|
|
230
|
+
private var promotedPayment: SKPayment?
|
|
231
|
+
private var promotedProduct: SKProduct?
|
|
228
232
|
|
|
229
233
|
public func definition() -> ModuleDefinition {
|
|
230
234
|
Name("ExpoIap")
|
|
@@ -247,6 +251,13 @@ public class ExpoIapModule: Module {
|
|
|
247
251
|
|
|
248
252
|
Function("initConnection") { () -> Bool in
|
|
249
253
|
self.productStore = ProductStore()
|
|
254
|
+
|
|
255
|
+
// Set up PaymentObserver for promoted products
|
|
256
|
+
if self.paymentObserver == nil {
|
|
257
|
+
self.paymentObserver = PaymentObserver(module: self)
|
|
258
|
+
SKPaymentQueue.default().add(self.paymentObserver!)
|
|
259
|
+
}
|
|
260
|
+
|
|
250
261
|
return AppStore.canMakePayments
|
|
251
262
|
}
|
|
252
263
|
|
|
@@ -269,7 +280,6 @@ public class ExpoIapModule: Module {
|
|
|
269
280
|
}
|
|
270
281
|
|
|
271
282
|
var result: [String: Any?] = [
|
|
272
|
-
"appTransactionID": appTransaction.appTransactionID,
|
|
273
283
|
"bundleID": appTransaction.bundleID,
|
|
274
284
|
"appVersion": appTransaction.appVersion,
|
|
275
285
|
"originalAppVersion": appTransaction.originalAppVersion,
|
|
@@ -284,6 +294,7 @@ public class ExpoIapModule: Module {
|
|
|
284
294
|
]
|
|
285
295
|
|
|
286
296
|
if #available(iOS 18.4, *) {
|
|
297
|
+
result["appTransactionID"] = appTransaction.appTransactionID
|
|
287
298
|
result["originalPlatform"] = appTransaction.originalPlatform.rawValue
|
|
288
299
|
}
|
|
289
300
|
|
|
@@ -303,6 +314,42 @@ public class ExpoIapModule: Module {
|
|
|
303
314
|
)
|
|
304
315
|
}
|
|
305
316
|
}
|
|
317
|
+
|
|
318
|
+
AsyncFunction("getPromotedProduct") { () -> [String: Any?]? in
|
|
319
|
+
guard let product = self.promotedProduct else {
|
|
320
|
+
return nil
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Convert SKProduct to dictionary
|
|
324
|
+
return [
|
|
325
|
+
"productIdentifier": product.productIdentifier,
|
|
326
|
+
"localizedTitle": product.localizedTitle,
|
|
327
|
+
"localizedDescription": product.localizedDescription,
|
|
328
|
+
"price": product.price.doubleValue,
|
|
329
|
+
"priceLocale": [
|
|
330
|
+
"currencyCode": product.priceLocale.currencyCode ?? "",
|
|
331
|
+
"currencySymbol": product.priceLocale.currencySymbol ?? "",
|
|
332
|
+
"countryCode": product.priceLocale.regionCode ?? ""
|
|
333
|
+
]
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
AsyncFunction("buyPromotedProduct") { () -> Void in
|
|
338
|
+
guard let payment = self.promotedPayment else {
|
|
339
|
+
throw Exception(
|
|
340
|
+
name: "ExpoIapModule",
|
|
341
|
+
description: "No promoted product available",
|
|
342
|
+
code: IapErrorCode.itemUnavailable
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Add the deferred payment to the queue
|
|
347
|
+
SKPaymentQueue.default().add(payment)
|
|
348
|
+
|
|
349
|
+
// Clear the promoted product data
|
|
350
|
+
self.promotedPayment = nil
|
|
351
|
+
self.promotedProduct = nil
|
|
352
|
+
}
|
|
306
353
|
|
|
307
354
|
AsyncFunction("getItems") { (skus: [String]) -> [[String: Any?]?] in
|
|
308
355
|
guard let productStore = self.productStore else {
|
|
@@ -335,6 +382,13 @@ public class ExpoIapModule: Module {
|
|
|
335
382
|
self.transactions.removeAll()
|
|
336
383
|
self.productStore = nil
|
|
337
384
|
self.removeTransactionObserver()
|
|
385
|
+
|
|
386
|
+
// Remove PaymentObserver
|
|
387
|
+
if let observer = self.paymentObserver {
|
|
388
|
+
SKPaymentQueue.default().remove(observer)
|
|
389
|
+
self.paymentObserver = nil
|
|
390
|
+
}
|
|
391
|
+
|
|
338
392
|
return true
|
|
339
393
|
}
|
|
340
394
|
|
|
@@ -921,4 +975,47 @@ public class ExpoIapModule: Module {
|
|
|
921
975
|
throw Exception(name: "ExpoIapModule", description: "App Store receipt not found", code: IapErrorCode.receiptFailed)
|
|
922
976
|
}
|
|
923
977
|
}
|
|
978
|
+
|
|
979
|
+
// Called by PaymentObserver when a promoted product is received
|
|
980
|
+
func handlePromotedProduct(payment: SKPayment, product: SKProduct) {
|
|
981
|
+
self.promotedPayment = payment
|
|
982
|
+
self.promotedProduct = product
|
|
983
|
+
|
|
984
|
+
if hasListeners {
|
|
985
|
+
let productData: [String: Any] = [
|
|
986
|
+
"productIdentifier": product.productIdentifier,
|
|
987
|
+
"localizedTitle": product.localizedTitle,
|
|
988
|
+
"localizedDescription": product.localizedDescription,
|
|
989
|
+
"price": product.price.doubleValue,
|
|
990
|
+
"priceLocale": [
|
|
991
|
+
"currencyCode": product.priceLocale.currencyCode ?? "",
|
|
992
|
+
"currencySymbol": product.priceLocale.currencySymbol ?? "",
|
|
993
|
+
"countryCode": product.priceLocale.regionCode ?? ""
|
|
994
|
+
]
|
|
995
|
+
]
|
|
996
|
+
sendEvent(IapEvent.PromotedProductIOS, productData)
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
// PaymentObserver for handling promoted products
|
|
1002
|
+
@available(iOS 15.0, *)
|
|
1003
|
+
class PaymentObserver: NSObject, SKPaymentTransactionObserver {
|
|
1004
|
+
weak var module: ExpoIapModule?
|
|
1005
|
+
|
|
1006
|
+
init(module: ExpoIapModule) {
|
|
1007
|
+
self.module = module
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Required by SKPaymentTransactionObserver protocol but not used
|
|
1011
|
+
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
|
1012
|
+
// We don't handle transactions here as StoreKit 2 handles them in ExpoIapModule
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Handle promoted products from App Store
|
|
1016
|
+
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
|
|
1017
|
+
module?.handlePromotedProduct(payment: payment, product: product)
|
|
1018
|
+
// Return false to defer the payment
|
|
1019
|
+
return false
|
|
1020
|
+
}
|
|
924
1021
|
}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/
|
|
1
|
+
{"root":["./src/withiap.ts"],"version":"5.8.3"}
|
package/src/ExpoIap.types.ts
CHANGED
|
@@ -365,8 +365,7 @@ export interface AndroidRequestPurchaseProps {
|
|
|
365
365
|
/**
|
|
366
366
|
* Android-specific subscription request parameters
|
|
367
367
|
*/
|
|
368
|
-
export interface AndroidRequestSubscriptionProps
|
|
369
|
-
extends AndroidRequestPurchaseProps {
|
|
368
|
+
export interface AndroidRequestSubscriptionProps extends AndroidRequestPurchaseProps {
|
|
370
369
|
readonly purchaseTokenAndroid?: string;
|
|
371
370
|
readonly replacementModeAndroid?: number;
|
|
372
371
|
readonly subscriptionOffers: {
|
|
@@ -409,36 +408,28 @@ export type RequestSubscriptionProps = PlatformRequestSubscriptionProps;
|
|
|
409
408
|
* Includes both unified and old platform-specific formats
|
|
410
409
|
* @deprecated Use RequestPurchaseProps with platform-specific structure instead
|
|
411
410
|
*/
|
|
412
|
-
export type LegacyRequestPurchasePropsAll =
|
|
413
|
-
| UnifiedRequestPurchaseProps
|
|
414
|
-
| LegacyRequestPurchaseProps;
|
|
411
|
+
export type LegacyRequestPurchasePropsAll = UnifiedRequestPurchaseProps | LegacyRequestPurchaseProps;
|
|
415
412
|
|
|
416
413
|
/**
|
|
417
414
|
* Legacy request subscription parameters (deprecated)
|
|
418
415
|
* Includes both unified and old platform-specific formats
|
|
419
416
|
* @deprecated Use RequestSubscriptionProps with platform-specific structure instead
|
|
420
417
|
*/
|
|
421
|
-
export type LegacyRequestSubscriptionPropsAll =
|
|
422
|
-
| UnifiedRequestSubscriptionProps
|
|
423
|
-
| LegacyRequestSubscriptionProps;
|
|
418
|
+
export type LegacyRequestSubscriptionPropsAll = UnifiedRequestSubscriptionProps | LegacyRequestSubscriptionProps;
|
|
424
419
|
|
|
425
420
|
/**
|
|
426
421
|
* All supported request purchase parameters
|
|
427
422
|
* Used internally for backward compatibility
|
|
428
423
|
* @internal
|
|
429
424
|
*/
|
|
430
|
-
export type RequestPurchasePropsWithLegacy =
|
|
431
|
-
| RequestPurchaseProps
|
|
432
|
-
| LegacyRequestPurchasePropsAll;
|
|
425
|
+
export type RequestPurchasePropsWithLegacy = RequestPurchaseProps | LegacyRequestPurchasePropsAll;
|
|
433
426
|
|
|
434
427
|
/**
|
|
435
428
|
* All supported request subscription parameters
|
|
436
429
|
* Used internally for backward compatibility
|
|
437
430
|
* @internal
|
|
438
431
|
*/
|
|
439
|
-
export type RequestSubscriptionPropsWithLegacy =
|
|
440
|
-
| RequestSubscriptionProps
|
|
441
|
-
| LegacyRequestSubscriptionPropsAll;
|
|
432
|
+
export type RequestSubscriptionPropsWithLegacy = RequestSubscriptionProps | LegacyRequestSubscriptionPropsAll;
|
|
442
433
|
|
|
443
434
|
// ============================================================================
|
|
444
435
|
// Type Guards and Utility Functions
|
|
@@ -446,19 +437,19 @@ export type RequestSubscriptionPropsWithLegacy =
|
|
|
446
437
|
|
|
447
438
|
// Type guards to check which API style is being used
|
|
448
439
|
export function isPlatformRequestProps(
|
|
449
|
-
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
|
|
440
|
+
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
|
|
450
441
|
): props is PlatformRequestPurchaseProps | PlatformRequestSubscriptionProps {
|
|
451
442
|
return 'ios' in props || 'android' in props;
|
|
452
443
|
}
|
|
453
444
|
|
|
454
445
|
export function isUnifiedRequestProps(
|
|
455
|
-
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
|
|
446
|
+
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
|
|
456
447
|
): props is UnifiedRequestPurchaseProps | UnifiedRequestSubscriptionProps {
|
|
457
448
|
return 'sku' in props || 'skus' in props;
|
|
458
449
|
}
|
|
459
450
|
|
|
460
451
|
export function isLegacyRequestProps(
|
|
461
|
-
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
|
|
452
|
+
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
|
|
462
453
|
): props is LegacyRequestPurchaseProps | LegacyRequestSubscriptionProps {
|
|
463
454
|
return 'productId' in props || 'productIds' in props;
|
|
464
455
|
}
|
package/src/index.ts
CHANGED
|
@@ -21,8 +21,12 @@ import {
|
|
|
21
21
|
isPlatformRequestProps,
|
|
22
22
|
isUnifiedRequestProps,
|
|
23
23
|
} from './ExpoIap.types';
|
|
24
|
-
import {
|
|
25
|
-
|
|
24
|
+
import {
|
|
25
|
+
ProductPurchaseAndroid,
|
|
26
|
+
} from './types/ExpoIapAndroid.types';
|
|
27
|
+
import {
|
|
28
|
+
PaymentDiscount,
|
|
29
|
+
} from './types/ExpoIapIos.types';
|
|
26
30
|
|
|
27
31
|
// Export all types
|
|
28
32
|
export * from './ExpoIap.types';
|
|
@@ -38,6 +42,7 @@ export enum IapEvent {
|
|
|
38
42
|
PurchaseError = 'purchase-error',
|
|
39
43
|
/** @deprecated Use PurchaseUpdated instead. This will be removed in a future version. */
|
|
40
44
|
TransactionIapUpdated = 'iap-transaction-updated',
|
|
45
|
+
PromotedProductIOS = 'promoted-product-ios',
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
export function setValueAsync(value: string) {
|
|
@@ -72,13 +77,43 @@ export const purchaseErrorListener = (
|
|
|
72
77
|
return emitter.addListener(IapEvent.PurchaseError, listener);
|
|
73
78
|
};
|
|
74
79
|
|
|
80
|
+
/**
|
|
81
|
+
* iOS-only listener for App Store promoted product events.
|
|
82
|
+
* This fires when a user taps on a promoted product in the App Store.
|
|
83
|
+
*
|
|
84
|
+
* @param listener - Callback function that receives the promoted product details
|
|
85
|
+
* @returns EventSubscription that can be used to unsubscribe
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const subscription = promotedProductListenerIOS((product) => {
|
|
90
|
+
* console.log('Promoted product:', product);
|
|
91
|
+
* // Handle the promoted product
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* // Later, clean up
|
|
95
|
+
* subscription.remove();
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @platform iOS
|
|
99
|
+
*/
|
|
100
|
+
export const promotedProductListenerIOS = (
|
|
101
|
+
listener: (product: Product) => void,
|
|
102
|
+
) => {
|
|
103
|
+
if (Platform.OS !== 'ios') {
|
|
104
|
+
console.warn('promotedProductListenerIOS: This listener is only available on iOS');
|
|
105
|
+
return { remove: () => {} };
|
|
106
|
+
}
|
|
107
|
+
return emitter.addListener(IapEvent.PromotedProductIOS, listener);
|
|
108
|
+
};
|
|
109
|
+
|
|
75
110
|
export function initConnection() {
|
|
76
111
|
return ExpoIapModule.initConnection();
|
|
77
112
|
}
|
|
78
113
|
|
|
79
114
|
export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
80
115
|
console.warn(
|
|
81
|
-
|
|
116
|
+
'`getProducts` is deprecated. Use `requestProducts({ skus, type: \'inapp\' })` instead. This function will be removed in version 3.0.0.',
|
|
82
117
|
);
|
|
83
118
|
if (!skus?.length) {
|
|
84
119
|
return Promise.reject(new Error('"skus" is required'));
|
|
@@ -112,7 +147,7 @@ export const getSubscriptions = async (
|
|
|
112
147
|
skus: string[],
|
|
113
148
|
): Promise<SubscriptionProduct[]> => {
|
|
114
149
|
console.warn(
|
|
115
|
-
|
|
150
|
+
'`getSubscriptions` is deprecated. Use `requestProducts({ skus, type: \'subs\' })` instead. This function will be removed in version 3.0.0.',
|
|
116
151
|
);
|
|
117
152
|
if (!skus?.length) {
|
|
118
153
|
return Promise.reject(new Error('"skus" is required'));
|
|
@@ -236,7 +271,7 @@ export const getPurchaseHistory = ({
|
|
|
236
271
|
onlyIncludeActiveItems?: boolean;
|
|
237
272
|
} = {}): Promise<ProductPurchase[]> => {
|
|
238
273
|
console.warn(
|
|
239
|
-
|
|
274
|
+
"`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.",
|
|
240
275
|
);
|
|
241
276
|
return getPurchaseHistories({
|
|
242
277
|
alsoPublishToEventListener,
|
|
@@ -286,8 +321,9 @@ export const getAvailablePurchases = ({
|
|
|
286
321
|
),
|
|
287
322
|
android: async () => {
|
|
288
323
|
const products = await ExpoIapModule.getAvailableItemsByType('inapp');
|
|
289
|
-
const subscriptions =
|
|
290
|
-
|
|
324
|
+
const subscriptions = await ExpoIapModule.getAvailableItemsByType(
|
|
325
|
+
'subs',
|
|
326
|
+
);
|
|
291
327
|
return products.concat(subscriptions);
|
|
292
328
|
},
|
|
293
329
|
}) || (() => Promise.resolve([]))
|
|
@@ -306,6 +342,7 @@ const offerToRecordIos = (
|
|
|
306
342
|
};
|
|
307
343
|
};
|
|
308
344
|
|
|
345
|
+
|
|
309
346
|
// Define discriminated union with explicit type parameter
|
|
310
347
|
// Using legacy types internally for backward compatibility
|
|
311
348
|
type PurchaseRequest =
|
|
@@ -335,8 +372,7 @@ const normalizeRequestProps = (
|
|
|
335
372
|
if (platform === 'ios') {
|
|
336
373
|
return {
|
|
337
374
|
sku: request.sku || (request.skus?.[0] ?? ''),
|
|
338
|
-
andDangerouslyFinishTransactionAutomaticallyIOS:
|
|
339
|
-
request.andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
375
|
+
andDangerouslyFinishTransactionAutomaticallyIOS: request.andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
340
376
|
appAccountToken: request.appAccountToken,
|
|
341
377
|
quantity: request.quantity,
|
|
342
378
|
withOffer: request.withOffer,
|
|
@@ -348,7 +384,7 @@ const normalizeRequestProps = (
|
|
|
348
384
|
obfuscatedProfileIdAndroid: request.obfuscatedProfileIdAndroid,
|
|
349
385
|
isOfferPersonalized: request.isOfferPersonalized,
|
|
350
386
|
};
|
|
351
|
-
|
|
387
|
+
|
|
352
388
|
// Add subscription-specific fields if present
|
|
353
389
|
if ('subscriptionOffers' in request && request.subscriptionOffers) {
|
|
354
390
|
androidRequest.subscriptionOffers = request.subscriptionOffers;
|
|
@@ -359,7 +395,7 @@ const normalizeRequestProps = (
|
|
|
359
395
|
if ('replacementModeAndroid' in request) {
|
|
360
396
|
androidRequest.replacementModeAndroid = request.replacementModeAndroid;
|
|
361
397
|
}
|
|
362
|
-
|
|
398
|
+
|
|
363
399
|
return androidRequest;
|
|
364
400
|
}
|
|
365
401
|
}
|
|
@@ -370,11 +406,11 @@ const normalizeRequestProps = (
|
|
|
370
406
|
|
|
371
407
|
/**
|
|
372
408
|
* Request a purchase for products or subscriptions.
|
|
373
|
-
*
|
|
409
|
+
*
|
|
374
410
|
* @param requestObj - Purchase request configuration
|
|
375
411
|
* @param requestObj.request - Platform-specific purchase parameters
|
|
376
412
|
* @param requestObj.type - Type of purchase: 'inapp' for products (default) or 'subs' for subscriptions
|
|
377
|
-
*
|
|
413
|
+
*
|
|
378
414
|
* @example
|
|
379
415
|
* ```typescript
|
|
380
416
|
* // Product purchase
|
|
@@ -385,12 +421,12 @@ const normalizeRequestProps = (
|
|
|
385
421
|
* },
|
|
386
422
|
* type: 'inapp'
|
|
387
423
|
* });
|
|
388
|
-
*
|
|
424
|
+
*
|
|
389
425
|
* // Subscription purchase
|
|
390
426
|
* await requestPurchase({
|
|
391
427
|
* request: {
|
|
392
428
|
* ios: { sku: subscriptionId },
|
|
393
|
-
* android: {
|
|
429
|
+
* android: {
|
|
394
430
|
* skus: [subscriptionId],
|
|
395
431
|
* subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
|
|
396
432
|
* }
|
|
@@ -412,7 +448,7 @@ export const requestPurchase = (
|
|
|
412
448
|
|
|
413
449
|
if (Platform.OS === 'ios') {
|
|
414
450
|
const normalizedRequest = normalizeRequestProps(request, 'ios');
|
|
415
|
-
|
|
451
|
+
|
|
416
452
|
if (!normalizedRequest?.sku) {
|
|
417
453
|
throw new Error(
|
|
418
454
|
'Invalid request for iOS. The `sku` property is required and must be a string.',
|
|
@@ -445,7 +481,7 @@ export const requestPurchase = (
|
|
|
445
481
|
|
|
446
482
|
if (Platform.OS === 'android') {
|
|
447
483
|
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
448
|
-
|
|
484
|
+
|
|
449
485
|
if (!normalizedRequest?.skus?.length) {
|
|
450
486
|
throw new Error(
|
|
451
487
|
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
@@ -509,7 +545,7 @@ export const requestPurchase = (
|
|
|
509
545
|
|
|
510
546
|
/**
|
|
511
547
|
* @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0.
|
|
512
|
-
*
|
|
548
|
+
*
|
|
513
549
|
* @example
|
|
514
550
|
* ```typescript
|
|
515
551
|
* // Old way (deprecated)
|
|
@@ -518,12 +554,12 @@ export const requestPurchase = (
|
|
|
518
554
|
* // or for Android
|
|
519
555
|
* skus: [subscriptionId],
|
|
520
556
|
* });
|
|
521
|
-
*
|
|
557
|
+
*
|
|
522
558
|
* // New way (recommended)
|
|
523
559
|
* await requestPurchase({
|
|
524
560
|
* request: {
|
|
525
561
|
* ios: { sku: subscriptionId },
|
|
526
|
-
* android: {
|
|
562
|
+
* android: {
|
|
527
563
|
* skus: [subscriptionId],
|
|
528
564
|
* subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
|
|
529
565
|
* }
|
|
@@ -588,16 +624,16 @@ export const finishTransaction = ({
|
|
|
588
624
|
|
|
589
625
|
/**
|
|
590
626
|
* Retrieves the current storefront information from iOS App Store
|
|
591
|
-
*
|
|
627
|
+
*
|
|
592
628
|
* @returns Promise resolving to the storefront country code
|
|
593
629
|
* @throws Error if called on non-iOS platform
|
|
594
|
-
*
|
|
630
|
+
*
|
|
595
631
|
* @example
|
|
596
632
|
* ```typescript
|
|
597
633
|
* const storefront = await getStorefrontIOS();
|
|
598
634
|
* console.log(storefront); // 'US'
|
|
599
635
|
* ```
|
|
600
|
-
*
|
|
636
|
+
*
|
|
601
637
|
* @platform iOS
|
|
602
638
|
*/
|
|
603
639
|
export const getStorefrontIOS = (): Promise<string> => {
|
package/src/modules/android.ts
CHANGED
|
@@ -102,9 +102,11 @@ export const acknowledgePurchaseAndroid = ({
|
|
|
102
102
|
* Open the Google Play Store to redeem offer codes (Android only).
|
|
103
103
|
* Note: Google Play does not provide a direct API to redeem codes within the app.
|
|
104
104
|
* This function opens the Play Store where users can manually enter their codes.
|
|
105
|
-
*
|
|
105
|
+
*
|
|
106
106
|
* @returns {Promise<void>}
|
|
107
107
|
*/
|
|
108
108
|
export const openRedeemOfferCodeAndroid = async (): Promise<void> => {
|
|
109
|
-
return Linking.openURL(
|
|
109
|
+
return Linking.openURL(
|
|
110
|
+
`https://play.google.com/redeem?code=`
|
|
111
|
+
);
|
|
110
112
|
};
|