expo-iap 2.6.3 → 2.7.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/android/build.gradle +9 -1
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +45 -44
- package/build/ExpoIap.types.d.ts +84 -2
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js +11 -2
- package/build/ExpoIap.types.js.map +1 -1
- package/build/index.d.ts +58 -7
- package/build/index.d.ts.map +1 -1
- package/build/index.js +114 -12
- package/build/index.js.map +1 -1
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +0 -43
- package/build/modules/ios.js.map +1 -1
- package/build/useIap.d.ts +2 -2
- package/build/useIap.d.ts.map +1 -1
- package/build/useIap.js.map +1 -1
- package/package.json +1 -1
- package/src/ExpoIap.types.ts +125 -4
- package/src/index.ts +137 -31
- package/src/modules/ios.ts +0 -65
- package/src/useIap.ts +3 -1
package/build/useIap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,IAAI,yBAAyB,EAC9C,gBAAgB,EAChB,eAAe,IAAI,uBAAuB,GAC3C,MAAM,IAAI,CAAC;AACZ,OAAO,EAAC,OAAO,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAC,sBAAsB,EAAC,MAAM,mBAAmB,CAAC;AA4DzD,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,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAE5D,EAAE,CAAC,CAAC;IACN,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,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,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,qEAAqE;gBACrE,gBAAgB,CAAC,OAAO,CAAC,mBAAmB,GAAG,uBAAuB,CACpE,KAAK,EAAE,QAAyC,EAAE,EAAE;oBAClD,6EAA6E;oBAC7E,sBAAsB,CAAC,CAAC,YAAY,EAAE,EAAE,CACtC,uBAAuB,CACrB,YAAY,EACZ,CAAC,QAA2B,CAAC,EAC7B,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,EAAE,CACjD,CACF,CAAC;oBAEF,8DAA8D;oBAC9D,IAAI,mBAAmB,IAAI,QAAQ,EAAE,CAAC;wBACpC,MAAM,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;oBAC/C,CAAC;gBACH,CAAC,CACF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,yBAAyB,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAEzD,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,mBAAmB;QACnB,aAAa;QACb,iBAAiB;QACjB,iBAAiB;QACjB,kBAAkB;QAClB,eAAe;QACf,oBAAoB;QACpB,oBAAoB;QACpB,yBAAyB;QACzB,qBAAqB,EAAE,6BAA6B;QACpD,oBAAoB,EAAE,4BAA4B;QAClD,WAAW,EAAE,mBAAmB;QAChC,gBAAgB,EAAE,wBAAwB;QAC1C,eAAe,EAAE,wBAAwB;QACzC,eAAe;QACf,gBAAgB;KACjB,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 getProducts,\n getAvailablePurchases,\n getPurchaseHistories,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n requestPurchase as requestPurchaseInternal,\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} 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 requestPurchase: (params: {\n request: any;\n type?: 'inapp' | 'subs';\n }) => Promise<any>;\n validateReceipt: (\n sku: string,\n androidOptions?: {\n packageName: string;\n productToken: string;\n accessToken: string;\n isSub?: boolean;\n },\n ) => Promise<any>;\n restorePurchases: () => Promise<void>; // 구매 복원 함수 추가\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (\n purchase: ProductPurchase | SubscriptionPurchase,\n ) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n}\n\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n\n // Helper function to merge arrays with duplicate checking\n const mergeWithDuplicateCheck = useCallback(\n <T>(\n existingItems: T[],\n newItems: T[],\n getKey: (item: T) => string,\n ): T[] => {\n const merged = [...existingItems];\n newItems.forEach((newItem) => {\n const isDuplicate = merged.some(\n (existingItem) => getKey(existingItem) === getKey(newItem),\n );\n if (!isDuplicate) {\n merged.push(newItem);\n }\n });\n return merged;\n },\n [],\n );\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIos?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<SubscriptionProduct[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const clearCurrentPurchase = useCallback(() => {\n setCurrentPurchase(undefined);\n }, []);\n\n const clearCurrentPurchaseError = useCallback(() => {\n setCurrentPurchaseError(undefined);\n }, []);\n\n const getProductsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await getProducts(skus);\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n result,\n (product) => product.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const getSubscriptionsInternal = useCallback(\n async (skus: string[]): Promise<void> => {\n try {\n const result = await getSubscriptions(skus);\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n result,\n (subscription) => subscription.id,\n ),\n );\n } catch (error) {\n console.error('Error fetching subscriptions:', error);\n }\n },\n [mergeWithDuplicateCheck],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases();\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await 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 requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["// 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} 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 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 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 requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases,\n };\n}\n"]}
|
package/package.json
CHANGED
package/src/ExpoIap.types.ts
CHANGED
|
@@ -51,12 +51,12 @@ export type SubscriptionProduct =
|
|
|
51
51
|
| (SubscriptionProductAndroid & AndroidPlatform)
|
|
52
52
|
| (SubscriptionProductIos & IosPlatform);
|
|
53
53
|
|
|
54
|
-
//
|
|
55
|
-
export type
|
|
54
|
+
// Legacy internal platform-specific types (kept for backward compatibility)
|
|
55
|
+
export type LegacyRequestPurchaseProps =
|
|
56
56
|
| RequestPurchaseIosProps
|
|
57
57
|
| RequestPurchaseAndroidProps;
|
|
58
58
|
|
|
59
|
-
export type
|
|
59
|
+
export type LegacyRequestSubscriptionProps =
|
|
60
60
|
| RequestSubscriptionAndroidProps
|
|
61
61
|
| RequestSubscriptionIosProps;
|
|
62
62
|
|
|
@@ -338,8 +338,129 @@ export interface UnifiedRequestSubscriptionProps
|
|
|
338
338
|
}
|
|
339
339
|
|
|
340
340
|
// ============================================================================
|
|
341
|
+
// New Platform-Specific Request Types (v2.7.0+)
|
|
342
|
+
// ============================================================================
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* iOS-specific purchase request parameters
|
|
346
|
+
*/
|
|
347
|
+
export interface IosRequestPurchaseProps {
|
|
348
|
+
readonly sku: string;
|
|
349
|
+
readonly andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
|
|
350
|
+
readonly appAccountToken?: string;
|
|
351
|
+
readonly quantity?: number;
|
|
352
|
+
readonly withOffer?: import('./types/ExpoIapIos.types').PaymentDiscount;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Android-specific purchase request parameters
|
|
357
|
+
*/
|
|
358
|
+
export interface AndroidRequestPurchaseProps {
|
|
359
|
+
readonly skus: string[];
|
|
360
|
+
readonly obfuscatedAccountIdAndroid?: string;
|
|
361
|
+
readonly obfuscatedProfileIdAndroid?: string;
|
|
362
|
+
readonly isOfferPersonalized?: boolean;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Android-specific subscription request parameters
|
|
367
|
+
*/
|
|
368
|
+
export interface AndroidRequestSubscriptionProps
|
|
369
|
+
extends AndroidRequestPurchaseProps {
|
|
370
|
+
readonly purchaseTokenAndroid?: string;
|
|
371
|
+
readonly replacementModeAndroid?: number;
|
|
372
|
+
readonly subscriptionOffers: {
|
|
373
|
+
sku: string;
|
|
374
|
+
offerToken: string;
|
|
375
|
+
}[];
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Modern platform-specific request structure (v2.7.0+)
|
|
380
|
+
* Allows clear separation of iOS and Android parameters
|
|
381
|
+
*/
|
|
382
|
+
export interface PlatformRequestPurchaseProps {
|
|
383
|
+
readonly ios?: IosRequestPurchaseProps;
|
|
384
|
+
readonly android?: AndroidRequestPurchaseProps;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Modern platform-specific subscription request structure (v2.7.0+)
|
|
389
|
+
*/
|
|
390
|
+
export interface PlatformRequestSubscriptionProps {
|
|
391
|
+
readonly ios?: IosRequestPurchaseProps;
|
|
392
|
+
readonly android?: AndroidRequestSubscriptionProps;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Modern request purchase parameters (v2.7.0+)
|
|
397
|
+
* This is the recommended API moving forward
|
|
398
|
+
*/
|
|
399
|
+
export type RequestPurchaseProps = PlatformRequestPurchaseProps;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Modern request subscription parameters (v2.7.0+)
|
|
403
|
+
* This is the recommended API moving forward
|
|
404
|
+
*/
|
|
405
|
+
export type RequestSubscriptionProps = PlatformRequestSubscriptionProps;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Legacy request purchase parameters (deprecated)
|
|
409
|
+
* Includes both unified and old platform-specific formats
|
|
410
|
+
* @deprecated Use RequestPurchaseProps with platform-specific structure instead
|
|
411
|
+
*/
|
|
412
|
+
export type LegacyRequestPurchasePropsAll =
|
|
413
|
+
| UnifiedRequestPurchaseProps
|
|
414
|
+
| LegacyRequestPurchaseProps;
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Legacy request subscription parameters (deprecated)
|
|
418
|
+
* Includes both unified and old platform-specific formats
|
|
419
|
+
* @deprecated Use RequestSubscriptionProps with platform-specific structure instead
|
|
420
|
+
*/
|
|
421
|
+
export type LegacyRequestSubscriptionPropsAll =
|
|
422
|
+
| UnifiedRequestSubscriptionProps
|
|
423
|
+
| LegacyRequestSubscriptionProps;
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* All supported request purchase parameters
|
|
427
|
+
* Used internally for backward compatibility
|
|
428
|
+
* @internal
|
|
429
|
+
*/
|
|
430
|
+
export type RequestPurchasePropsWithLegacy =
|
|
431
|
+
| RequestPurchaseProps
|
|
432
|
+
| LegacyRequestPurchasePropsAll;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* All supported request subscription parameters
|
|
436
|
+
* Used internally for backward compatibility
|
|
437
|
+
* @internal
|
|
438
|
+
*/
|
|
439
|
+
export type RequestSubscriptionPropsWithLegacy =
|
|
440
|
+
| RequestSubscriptionProps
|
|
441
|
+
| LegacyRequestSubscriptionPropsAll;
|
|
442
|
+
|
|
341
443
|
// ============================================================================
|
|
342
444
|
// Type Guards and Utility Functions
|
|
343
445
|
// ============================================================================
|
|
344
446
|
|
|
345
|
-
//
|
|
447
|
+
// Type guards to check which API style is being used
|
|
448
|
+
export function isPlatformRequestProps(
|
|
449
|
+
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
|
|
450
|
+
): props is PlatformRequestPurchaseProps | PlatformRequestSubscriptionProps {
|
|
451
|
+
return 'ios' in props || 'android' in props;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function isUnifiedRequestProps(
|
|
455
|
+
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
|
|
456
|
+
): props is UnifiedRequestPurchaseProps | UnifiedRequestSubscriptionProps {
|
|
457
|
+
return 'sku' in props || 'skus' in props;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export function isLegacyRequestProps(
|
|
461
|
+
props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
|
|
462
|
+
): props is LegacyRequestPurchaseProps | LegacyRequestSubscriptionProps {
|
|
463
|
+
return 'productId' in props || 'productIds' in props;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Note: Other type guard functions are exported from index.ts to avoid conflicts
|
package/src/index.ts
CHANGED
|
@@ -14,21 +14,17 @@ import {
|
|
|
14
14
|
Purchase,
|
|
15
15
|
PurchaseError,
|
|
16
16
|
PurchaseResult,
|
|
17
|
-
|
|
17
|
+
RequestSubscriptionPropsWithLegacy,
|
|
18
|
+
RequestPurchasePropsWithLegacy,
|
|
18
19
|
SubscriptionProduct,
|
|
19
20
|
SubscriptionPurchase,
|
|
21
|
+
isPlatformRequestProps,
|
|
22
|
+
isUnifiedRequestProps,
|
|
20
23
|
} from './ExpoIap.types';
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
RequestPurchaseAndroidProps,
|
|
24
|
-
RequestSubscriptionAndroidProps,
|
|
25
|
-
} from './types/ExpoIapAndroid.types';
|
|
26
|
-
import {
|
|
27
|
-
PaymentDiscount,
|
|
28
|
-
RequestPurchaseIosProps,
|
|
29
|
-
RequestSubscriptionIosProps,
|
|
30
|
-
} from './types/ExpoIapIos.types';
|
|
24
|
+
import {ProductPurchaseAndroid} from './types/ExpoIapAndroid.types';
|
|
25
|
+
import {PaymentDiscount} from './types/ExpoIapIos.types';
|
|
31
26
|
|
|
27
|
+
// Export all types
|
|
32
28
|
export * from './ExpoIap.types';
|
|
33
29
|
export * from './modules/android';
|
|
34
30
|
export * from './modules/ios';
|
|
@@ -186,10 +182,12 @@ export const getPurchaseHistories = ({
|
|
|
186
182
|
);
|
|
187
183
|
},
|
|
188
184
|
android: async () => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
185
|
+
// getPurchaseHistoryByType was removed in Google Play Billing Library v8
|
|
186
|
+
// Android doesn't provide purchase history anymore, only active purchases
|
|
187
|
+
console.warn(
|
|
188
|
+
'getPurchaseHistories is not supported on Android with Google Play Billing Library v8. Use getAvailablePurchases instead to get active purchases.',
|
|
189
|
+
);
|
|
190
|
+
return [];
|
|
193
191
|
},
|
|
194
192
|
}) || (() => Promise.resolve([]))
|
|
195
193
|
)();
|
|
@@ -231,22 +229,98 @@ const offerToRecordIos = (
|
|
|
231
229
|
};
|
|
232
230
|
|
|
233
231
|
// Define discriminated union with explicit type parameter
|
|
232
|
+
// Using legacy types internally for backward compatibility
|
|
234
233
|
type PurchaseRequest =
|
|
235
234
|
| {
|
|
236
|
-
request:
|
|
235
|
+
request: RequestPurchasePropsWithLegacy;
|
|
237
236
|
type?: 'inapp';
|
|
238
237
|
}
|
|
239
238
|
| {
|
|
240
|
-
request:
|
|
239
|
+
request: RequestSubscriptionPropsWithLegacy;
|
|
241
240
|
type: 'subs';
|
|
242
241
|
};
|
|
243
242
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Helper to normalize request props to platform-specific format
|
|
245
|
+
*/
|
|
246
|
+
const normalizeRequestProps = (
|
|
247
|
+
request: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
|
|
248
|
+
platform: 'ios' | 'android',
|
|
249
|
+
): any => {
|
|
250
|
+
// If it's already platform-specific format
|
|
251
|
+
if (isPlatformRequestProps(request)) {
|
|
252
|
+
return platform === 'ios' ? request.ios : request.android;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// If it's unified format, convert to platform-specific
|
|
256
|
+
if (isUnifiedRequestProps(request)) {
|
|
257
|
+
if (platform === 'ios') {
|
|
258
|
+
return {
|
|
259
|
+
sku: request.sku || (request.skus?.[0] ?? ''),
|
|
260
|
+
andDangerouslyFinishTransactionAutomaticallyIOS:
|
|
261
|
+
request.andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
262
|
+
appAccountToken: request.appAccountToken,
|
|
263
|
+
quantity: request.quantity,
|
|
264
|
+
withOffer: request.withOffer,
|
|
265
|
+
};
|
|
266
|
+
} else {
|
|
267
|
+
const androidRequest: any = {
|
|
268
|
+
skus: request.skus || (request.sku ? [request.sku] : []),
|
|
269
|
+
obfuscatedAccountIdAndroid: request.obfuscatedAccountIdAndroid,
|
|
270
|
+
obfuscatedProfileIdAndroid: request.obfuscatedProfileIdAndroid,
|
|
271
|
+
isOfferPersonalized: request.isOfferPersonalized,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Add subscription-specific fields if present
|
|
275
|
+
if ('subscriptionOffers' in request && request.subscriptionOffers) {
|
|
276
|
+
androidRequest.subscriptionOffers = request.subscriptionOffers;
|
|
277
|
+
}
|
|
278
|
+
if ('purchaseTokenAndroid' in request) {
|
|
279
|
+
androidRequest.purchaseTokenAndroid = request.purchaseTokenAndroid;
|
|
280
|
+
}
|
|
281
|
+
if ('replacementModeAndroid' in request) {
|
|
282
|
+
androidRequest.replacementModeAndroid = request.replacementModeAndroid;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return androidRequest;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Legacy format handling
|
|
290
|
+
return request;
|
|
291
|
+
};
|
|
249
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Request a purchase for products or subscriptions.
|
|
295
|
+
*
|
|
296
|
+
* @param requestObj - Purchase request configuration
|
|
297
|
+
* @param requestObj.request - Platform-specific purchase parameters
|
|
298
|
+
* @param requestObj.type - Type of purchase: 'inapp' for products (default) or 'subs' for subscriptions
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```typescript
|
|
302
|
+
* // Product purchase
|
|
303
|
+
* await requestPurchase({
|
|
304
|
+
* request: {
|
|
305
|
+
* ios: { sku: productId },
|
|
306
|
+
* android: { skus: [productId] }
|
|
307
|
+
* },
|
|
308
|
+
* type: 'inapp'
|
|
309
|
+
* });
|
|
310
|
+
*
|
|
311
|
+
* // Subscription purchase
|
|
312
|
+
* await requestPurchase({
|
|
313
|
+
* request: {
|
|
314
|
+
* ios: { sku: subscriptionId },
|
|
315
|
+
* android: {
|
|
316
|
+
* skus: [subscriptionId],
|
|
317
|
+
* subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
|
|
318
|
+
* }
|
|
319
|
+
* },
|
|
320
|
+
* type: 'subs'
|
|
321
|
+
* });
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
250
324
|
export const requestPurchase = (
|
|
251
325
|
requestObj: PurchaseRequest,
|
|
252
326
|
): Promise<
|
|
@@ -259,7 +333,9 @@ export const requestPurchase = (
|
|
|
259
333
|
const {request, type = 'inapp'} = requestObj;
|
|
260
334
|
|
|
261
335
|
if (Platform.OS === 'ios') {
|
|
262
|
-
|
|
336
|
+
const normalizedRequest = normalizeRequestProps(request, 'ios');
|
|
337
|
+
|
|
338
|
+
if (!normalizedRequest?.sku) {
|
|
263
339
|
throw new Error(
|
|
264
340
|
'Invalid request for iOS. The `sku` property is required and must be a string.',
|
|
265
341
|
);
|
|
@@ -271,7 +347,7 @@ export const requestPurchase = (
|
|
|
271
347
|
appAccountToken,
|
|
272
348
|
quantity,
|
|
273
349
|
withOffer,
|
|
274
|
-
} =
|
|
350
|
+
} = normalizedRequest;
|
|
275
351
|
|
|
276
352
|
return (async () => {
|
|
277
353
|
const offer = offerToRecordIos(withOffer);
|
|
@@ -290,13 +366,21 @@ export const requestPurchase = (
|
|
|
290
366
|
}
|
|
291
367
|
|
|
292
368
|
if (Platform.OS === 'android') {
|
|
369
|
+
const normalizedRequest = normalizeRequestProps(request, 'android');
|
|
370
|
+
|
|
371
|
+
if (!normalizedRequest?.skus?.length) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
293
377
|
if (type === 'inapp') {
|
|
294
378
|
const {
|
|
295
379
|
skus,
|
|
296
380
|
obfuscatedAccountIdAndroid,
|
|
297
381
|
obfuscatedProfileIdAndroid,
|
|
298
382
|
isOfferPersonalized,
|
|
299
|
-
} =
|
|
383
|
+
} = normalizedRequest;
|
|
300
384
|
|
|
301
385
|
return (async () => {
|
|
302
386
|
return ExpoIapModule.buyItemByType({
|
|
@@ -321,7 +405,7 @@ export const requestPurchase = (
|
|
|
321
405
|
subscriptionOffers = [],
|
|
322
406
|
replacementModeAndroid = -1,
|
|
323
407
|
purchaseTokenAndroid,
|
|
324
|
-
} =
|
|
408
|
+
} = normalizedRequest;
|
|
325
409
|
|
|
326
410
|
return (async () => {
|
|
327
411
|
return ExpoIapModule.buyItemByType({
|
|
@@ -331,14 +415,14 @@ export const requestPurchase = (
|
|
|
331
415
|
replacementMode: replacementModeAndroid,
|
|
332
416
|
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
333
417
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
334
|
-
offerTokenArr: subscriptionOffers.map((so) => so.offerToken),
|
|
418
|
+
offerTokenArr: subscriptionOffers.map((so: any) => so.offerToken),
|
|
335
419
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
336
420
|
}) as Promise<SubscriptionPurchase[]>;
|
|
337
421
|
})();
|
|
338
422
|
}
|
|
339
423
|
|
|
340
424
|
throw new Error(
|
|
341
|
-
"Invalid request for Android: Expected a
|
|
425
|
+
"Invalid request for Android: Expected a valid request object with 'skus' array.",
|
|
342
426
|
);
|
|
343
427
|
}
|
|
344
428
|
|
|
@@ -346,13 +430,35 @@ export const requestPurchase = (
|
|
|
346
430
|
};
|
|
347
431
|
|
|
348
432
|
/**
|
|
349
|
-
* @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0
|
|
433
|
+
* @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0.
|
|
434
|
+
*
|
|
435
|
+
* @example
|
|
436
|
+
* ```typescript
|
|
437
|
+
* // Old way (deprecated)
|
|
438
|
+
* await requestSubscription({
|
|
439
|
+
* sku: subscriptionId,
|
|
440
|
+
* // or for Android
|
|
441
|
+
* skus: [subscriptionId],
|
|
442
|
+
* });
|
|
443
|
+
*
|
|
444
|
+
* // New way (recommended)
|
|
445
|
+
* await requestPurchase({
|
|
446
|
+
* request: {
|
|
447
|
+
* ios: { sku: subscriptionId },
|
|
448
|
+
* android: {
|
|
449
|
+
* skus: [subscriptionId],
|
|
450
|
+
* subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
|
|
451
|
+
* }
|
|
452
|
+
* },
|
|
453
|
+
* type: 'subs'
|
|
454
|
+
* });
|
|
455
|
+
* ```
|
|
350
456
|
*/
|
|
351
457
|
export const requestSubscription = async (
|
|
352
|
-
request:
|
|
458
|
+
request: RequestSubscriptionPropsWithLegacy,
|
|
353
459
|
): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> => {
|
|
354
460
|
console.warn(
|
|
355
|
-
"`requestSubscription` is deprecated. Use `requestPurchase({ request, type: 'subs' })` instead.
|
|
461
|
+
"`requestSubscription` is deprecated and will be removed in version 3.0.0. Use `requestPurchase({ request, type: 'subs' })` instead.",
|
|
356
462
|
);
|
|
357
463
|
return (await requestPurchase({request, type: 'subs'})) as
|
|
358
464
|
| SubscriptionPurchase
|
package/src/modules/ios.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// External dependencies
|
|
2
|
-
import {Platform} from 'react-native';
|
|
3
2
|
|
|
4
3
|
// Internal modules
|
|
5
4
|
import {purchaseUpdatedListener} from '..';
|
|
@@ -39,10 +38,6 @@ export type TransactionEvent = {
|
|
|
39
38
|
export const transactionUpdatedIos = (
|
|
40
39
|
listener: (event: TransactionEvent) => void,
|
|
41
40
|
) => {
|
|
42
|
-
if (Platform.OS !== 'ios') {
|
|
43
|
-
throw new Error('This method is only available on iOS');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
41
|
const isProductPurchase = (item: unknown): item is ProductPurchase => {
|
|
47
42
|
return (
|
|
48
43
|
item != null &&
|
|
@@ -100,9 +95,6 @@ export function isProductIos<T extends {platform?: string}>(
|
|
|
100
95
|
* @platform iOS
|
|
101
96
|
*/
|
|
102
97
|
export const syncIOS = (): Promise<null> => {
|
|
103
|
-
if (Platform.OS !== 'ios') {
|
|
104
|
-
throw new Error('syncIOS: This method is only available on iOS');
|
|
105
|
-
}
|
|
106
98
|
return ExpoIapModule.sync();
|
|
107
99
|
};
|
|
108
100
|
|
|
@@ -118,11 +110,6 @@ export const syncIOS = (): Promise<null> => {
|
|
|
118
110
|
export const isEligibleForIntroOfferIOS = (
|
|
119
111
|
groupID: string,
|
|
120
112
|
): Promise<boolean> => {
|
|
121
|
-
if (Platform.OS !== 'ios') {
|
|
122
|
-
throw new Error(
|
|
123
|
-
'isEligibleForIntroOfferIOS: This method is only available on iOS',
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
113
|
return ExpoIapModule.isEligibleForIntroOffer(groupID);
|
|
127
114
|
};
|
|
128
115
|
|
|
@@ -138,11 +125,6 @@ export const isEligibleForIntroOfferIOS = (
|
|
|
138
125
|
export const subscriptionStatusIOS = (
|
|
139
126
|
sku: string,
|
|
140
127
|
): Promise<ProductStatusIos[]> => {
|
|
141
|
-
if (Platform.OS !== 'ios') {
|
|
142
|
-
throw new Error(
|
|
143
|
-
'subscriptionStatusIOS: This method is only available on iOS',
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
128
|
return ExpoIapModule.subscriptionStatus(sku);
|
|
147
129
|
};
|
|
148
130
|
|
|
@@ -158,11 +140,6 @@ export const subscriptionStatusIOS = (
|
|
|
158
140
|
export const currentEntitlementIOS = (
|
|
159
141
|
sku: string,
|
|
160
142
|
): Promise<ProductPurchase> => {
|
|
161
|
-
if (Platform.OS !== 'ios') {
|
|
162
|
-
throw new Error(
|
|
163
|
-
'currentEntitlementIOS: This method is only available on iOS',
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
143
|
return ExpoIapModule.currentEntitlement(sku);
|
|
167
144
|
};
|
|
168
145
|
|
|
@@ -176,11 +153,6 @@ export const currentEntitlementIOS = (
|
|
|
176
153
|
* @platform iOS
|
|
177
154
|
*/
|
|
178
155
|
export const latestTransactionIOS = (sku: string): Promise<ProductPurchase> => {
|
|
179
|
-
if (Platform.OS !== 'ios') {
|
|
180
|
-
throw new Error(
|
|
181
|
-
'latestTransactionIOS: This method is only available on iOS',
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
156
|
return ExpoIapModule.latestTransaction(sku);
|
|
185
157
|
};
|
|
186
158
|
|
|
@@ -197,11 +169,6 @@ type RefundRequestStatus = 'success' | 'userCancelled';
|
|
|
197
169
|
export const beginRefundRequestIOS = (
|
|
198
170
|
sku: string,
|
|
199
171
|
): Promise<RefundRequestStatus> => {
|
|
200
|
-
if (Platform.OS !== 'ios') {
|
|
201
|
-
throw new Error(
|
|
202
|
-
'beginRefundRequestIOS: This method is only available on iOS',
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
172
|
return ExpoIapModule.beginRefundRequest(sku);
|
|
206
173
|
};
|
|
207
174
|
|
|
@@ -216,11 +183,6 @@ export const beginRefundRequestIOS = (
|
|
|
216
183
|
* @platform iOS
|
|
217
184
|
*/
|
|
218
185
|
export const showManageSubscriptionsIOS = (): Promise<null> => {
|
|
219
|
-
if (Platform.OS !== 'ios') {
|
|
220
|
-
throw new Error(
|
|
221
|
-
'showManageSubscriptionsIOS: This method is only available on iOS',
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
186
|
return ExpoIapModule.showManageSubscriptions();
|
|
225
187
|
};
|
|
226
188
|
|
|
@@ -235,9 +197,6 @@ export const showManageSubscriptionsIOS = (): Promise<null> => {
|
|
|
235
197
|
* @returns {Promise<string>} Base64 encoded receipt data
|
|
236
198
|
*/
|
|
237
199
|
export const getReceiptIOS = (): Promise<string> => {
|
|
238
|
-
if (Platform.OS !== 'ios') {
|
|
239
|
-
throw new Error('This method is only available on iOS');
|
|
240
|
-
}
|
|
241
200
|
return ExpoIapModule.getReceiptData();
|
|
242
201
|
};
|
|
243
202
|
|
|
@@ -252,11 +211,6 @@ export const getReceiptIOS = (): Promise<string> => {
|
|
|
252
211
|
* @platform iOS
|
|
253
212
|
*/
|
|
254
213
|
export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
|
|
255
|
-
if (Platform.OS !== 'ios') {
|
|
256
|
-
throw new Error(
|
|
257
|
-
'isTransactionVerifiedIOS: This method is only available on iOS',
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
214
|
return ExpoIapModule.isTransactionVerified(sku);
|
|
261
215
|
};
|
|
262
216
|
|
|
@@ -271,11 +225,6 @@ export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
|
|
|
271
225
|
* @platform iOS
|
|
272
226
|
*/
|
|
273
227
|
export const getTransactionJwsIOS = (sku: string): Promise<string> => {
|
|
274
|
-
if (Platform.OS !== 'ios') {
|
|
275
|
-
throw new Error(
|
|
276
|
-
'getTransactionJwsIOS: This method is only available on iOS',
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
228
|
return ExpoIapModule.getTransactionJws(sku);
|
|
280
229
|
};
|
|
281
230
|
|
|
@@ -302,10 +251,6 @@ export const validateReceiptIOS = async (
|
|
|
302
251
|
jwsRepresentation: string;
|
|
303
252
|
latestTransaction?: ProductPurchase;
|
|
304
253
|
}> => {
|
|
305
|
-
if (Platform.OS !== 'ios') {
|
|
306
|
-
throw new Error('This method is only available on iOS');
|
|
307
|
-
}
|
|
308
|
-
|
|
309
254
|
const result = await ExpoIapModule.validateReceiptIOS(sku);
|
|
310
255
|
return result;
|
|
311
256
|
};
|
|
@@ -322,11 +267,6 @@ export const validateReceiptIOS = async (
|
|
|
322
267
|
* @platform iOS
|
|
323
268
|
*/
|
|
324
269
|
export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
|
|
325
|
-
if (Platform.OS !== 'ios') {
|
|
326
|
-
throw new Error(
|
|
327
|
-
'presentCodeRedemptionSheetIOS: This method is only available on iOS',
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
270
|
return ExpoIapModule.presentCodeRedemptionSheet();
|
|
331
271
|
};
|
|
332
272
|
|
|
@@ -340,11 +280,6 @@ export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
|
|
|
340
280
|
* @platform iOS
|
|
341
281
|
*/
|
|
342
282
|
export const getAppTransactionIOS = (): Promise<AppTransactionIOS | null> => {
|
|
343
|
-
if (Platform.OS !== 'ios') {
|
|
344
|
-
throw new Error(
|
|
345
|
-
'getAppTransactionIOS: This method is only available on iOS',
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
283
|
return ExpoIapModule.getAppTransaction();
|
|
349
284
|
};
|
|
350
285
|
|