expo-iap 3.1.1-rc.2 → 3.1.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/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +0 -11
- package/build/ExpoIapModule.d.ts +0 -1
- package/build/ExpoIapModule.d.ts.map +1 -1
- package/build/ExpoIapModule.js +4 -29
- package/build/ExpoIapModule.js.map +1 -1
- package/build/useIAP.d.ts +0 -2
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +3 -8
- package/build/useIAP.js.map +1 -1
- package/expo-module.config.json +3 -10
- package/package.json +3 -4
- package/plugin/build/withIAP.d.ts +9 -22
- package/plugin/build/withIAP.js +9 -157
- package/plugin/jest.config.js +3 -13
- package/plugin/src/expoConfig.augmentation.d.ts +1 -30
- package/plugin/src/withIAP.ts +18 -258
- package/plugin/tsconfig.json +1 -2
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/ExpoIapModule.ts +4 -45
- package/src/useIAP.ts +3 -11
- package/coverage/clover.xml +0 -497
- package/coverage/coverage-final.json +0 -6
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -161
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/ExpoIap.types.ts.html +0 -1243
- package/coverage/lcov-report/src/PurchaseError.ts.html +0 -787
- package/coverage/lcov-report/src/helpers/index.html +0 -116
- package/coverage/lcov-report/src/helpers/subscription.ts.html +0 -496
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov-report/src/index.ts.html +0 -1993
- package/coverage/lcov-report/src/modules/android.ts.html +0 -550
- package/coverage/lcov-report/src/modules/index.html +0 -131
- package/coverage/lcov-report/src/modules/ios.ts.html +0 -1222
- package/coverage/lcov-report/src/purchase-error.ts.html +0 -880
- package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +0 -493
- package/coverage/lcov-report/src/types/index.html +0 -116
- package/coverage/lcov-report/src/useIap.ts.html +0 -1483
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +0 -1069
- package/coverage/lcov-report/src/utils/index.html +0 -116
- package/coverage/lcov-report/src/utils/purchase.ts.html +0 -241
- package/coverage/lcov.info +0 -929
- package/ios/onside/OnsideIapModule.swift +0 -489
|
@@ -360,17 +360,6 @@ class ExpoIapModule : Module() {
|
|
|
360
360
|
"requestPurchaseAndroid",
|
|
361
361
|
purchases.map { it.toJson() },
|
|
362
362
|
)
|
|
363
|
-
purchases.forEach { purchase ->
|
|
364
|
-
runCatching {
|
|
365
|
-
emitOrQueue(EVENT_PURCHASE_UPDATED, purchase.toJson())
|
|
366
|
-
}.onFailure { ex ->
|
|
367
|
-
Log.e(
|
|
368
|
-
TAG,
|
|
369
|
-
"Failed to send PURCHASE_UPDATED event (requestPurchase)",
|
|
370
|
-
ex,
|
|
371
|
-
)
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
363
|
PromiseUtils.resolvePromisesForKey(
|
|
375
364
|
PromiseUtils.PROMISE_BUY_ITEM,
|
|
376
365
|
purchases.map { it.toJson() },
|
package/build/ExpoIapModule.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAIA,QAAA,
|
|
1
|
+
{"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,aAAa,KAAiC,CAAC;AAGrD,eAAO,MAAM,kBAAkB,KAAkC,CAAC;AAElE,eAAe,aAAa,CAAC"}
|
package/build/ExpoIapModule.js
CHANGED
|
@@ -1,33 +1,8 @@
|
|
|
1
|
-
import { requireNativeModule
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { requireNativeModule } from 'expo-modules-core';
|
|
2
|
+
// It loads the native module object from the JSI or falls back to
|
|
3
|
+
// the bridge module (from NativeModulesProxy) if the remote debugger is on.
|
|
4
|
+
const ExpoIapModule = requireNativeModule('ExpoIap');
|
|
4
5
|
// Platform-specific error codes from native modules
|
|
5
6
|
export const NATIVE_ERROR_CODES = ExpoIapModule.ERROR_CODES || {};
|
|
6
7
|
export default ExpoIapModule;
|
|
7
|
-
function resolveNativeModule() {
|
|
8
|
-
const candidates = ['ExpoIapOnside', 'ExpoIap'];
|
|
9
|
-
for (const name of candidates) {
|
|
10
|
-
try {
|
|
11
|
-
const module = requireNativeModule(name);
|
|
12
|
-
return { module, name };
|
|
13
|
-
}
|
|
14
|
-
catch (error) {
|
|
15
|
-
if (name === 'ExpoIapOnside' && isMissingModuleError(error, name)) {
|
|
16
|
-
// Onside module is optional. If unavailable, fall back to ExpoIap.
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
throw error;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
throw new UnavailabilityError('expo-iap', 'ExpoIap native module is unavailable');
|
|
23
|
-
}
|
|
24
|
-
function isMissingModuleError(error, moduleName) {
|
|
25
|
-
if (error instanceof UnavailabilityError) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (error instanceof Error) {
|
|
29
|
-
return error.message.includes(`Cannot find native module '${moduleName}'`);
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
8
|
//# sourceMappingURL=ExpoIapModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAEtD,kEAAkE;AAClE,4EAA4E;AAC5E,MAAM,aAAa,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;AAErD,oDAAoD;AACpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC,WAAW,IAAI,EAAE,CAAC;AAElE,eAAe,aAAa,CAAC","sourcesContent":["import {requireNativeModule} from 'expo-modules-core';\n\n// It loads the native module object from the JSI or falls back to\n// the bridge module (from NativeModulesProxy) if the remote debugger is on.\nconst ExpoIapModule = requireNativeModule('ExpoIap');\n\n// Platform-specific error codes from native modules\nexport const NATIVE_ERROR_CODES = ExpoIapModule.ERROR_CODES || {};\n\nexport default ExpoIapModule;\n"]}
|
package/build/useIAP.d.ts
CHANGED
|
@@ -4,8 +4,6 @@ import type { PurchaseError } from './utils/errorMapping';
|
|
|
4
4
|
type UseIap = {
|
|
5
5
|
connected: boolean;
|
|
6
6
|
products: Product[];
|
|
7
|
-
promotedProductsIOS: Purchase[];
|
|
8
|
-
promotedProductIdIOS?: string;
|
|
9
7
|
subscriptions: ProductSubscription[];
|
|
10
8
|
availablePurchases: Purchase[];
|
|
11
9
|
promotedProductIOS?: Product;
|
package/build/useIAP.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIAP.d.ts","sourceRoot":"","sources":["../src/useIAP.ts"],"names":[],"mappings":"AAMA,OAAO,EAQL,eAAe,IAAI,uBAAuB,EAK1C,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAEtB,MAAM,SAAS,CAAC;AAOjB,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAGnB,QAAQ,EACR,2BAA2B,EAE3B,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAOxD,KAAK,MAAM,GAAG;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,
|
|
1
|
+
{"version":3,"file":"useIAP.d.ts","sourceRoot":"","sources":["../src/useIAP.ts"],"names":[],"mappings":"AAMA,OAAO,EAQL,eAAe,IAAI,uBAAuB,EAK1C,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAEtB,MAAM,SAAS,CAAC;AAOjB,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAGnB,QAAQ,EACR,2BAA2B,EAE3B,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAOxD,KAAK,MAAM,GAAG;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,kBAAkB,EAAE,QAAQ,EAAE,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,YAAY,GACb,EAAE;QACD,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,aAAa,EAAE,CAAC,MAAM,EAAE;QACtB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;KACzB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB,eAAe,EAAE,CACf,MAAM,EAAE,2BAA2B,KAChC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;IAChD,eAAe,EAAE,CACf,KAAK,EAAE,sBAAsB,KAC1B,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACtC,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,qBAAqB,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACrD,mCAAmC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,sBAAsB,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,sBAAsB,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1E,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjD,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACrC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACnD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAkVtD"}
|
package/build/useIAP.js
CHANGED
|
@@ -13,11 +13,9 @@ import { getUserFriendlyErrorMessage, isUserCancelledError, isRecoverableError,
|
|
|
13
13
|
export function useIAP(options) {
|
|
14
14
|
const [connected, setConnected] = useState(false);
|
|
15
15
|
const [products, setProducts] = useState([]);
|
|
16
|
-
const [promotedProductsIOS] = useState([]);
|
|
17
16
|
const [subscriptions, setSubscriptions] = useState([]);
|
|
18
17
|
const [availablePurchases, setAvailablePurchases] = useState([]);
|
|
19
18
|
const [promotedProductIOS, setPromotedProductIOS] = useState();
|
|
20
|
-
const [promotedProductIdIOS] = useState();
|
|
21
19
|
const [activeSubscriptions, setActiveSubscriptions] = useState([]);
|
|
22
20
|
const optionsRef = useRef(options);
|
|
23
21
|
const connectedRef = useRef(false);
|
|
@@ -189,7 +187,7 @@ export function useIAP(options) {
|
|
|
189
187
|
});
|
|
190
188
|
if (Platform.OS === 'ios') {
|
|
191
189
|
// iOS promoted products listener
|
|
192
|
-
subscriptionsRef.current.
|
|
190
|
+
subscriptionsRef.current.promotedProductIOS = promotedProductListenerIOS((product) => {
|
|
193
191
|
setPromotedProductIOS(product);
|
|
194
192
|
if (optionsRef.current?.onPromotedProductIOS) {
|
|
195
193
|
optionsRef.current.onPromotedProductIOS(product);
|
|
@@ -203,9 +201,9 @@ export function useIAP(options) {
|
|
|
203
201
|
// If connection failed, clean up listeners
|
|
204
202
|
console.warn('[useIAP] Connection failed, cleaning up listeners...');
|
|
205
203
|
subscriptionsRef.current.purchaseUpdate?.remove();
|
|
206
|
-
subscriptionsRef.current.
|
|
204
|
+
subscriptionsRef.current.promotedProductIOS?.remove();
|
|
207
205
|
subscriptionsRef.current.purchaseUpdate = undefined;
|
|
208
|
-
subscriptionsRef.current.
|
|
206
|
+
subscriptionsRef.current.promotedProductIOS = undefined;
|
|
209
207
|
// Keep purchaseError listener registered to capture subsequent retries
|
|
210
208
|
return;
|
|
211
209
|
}
|
|
@@ -216,7 +214,6 @@ export function useIAP(options) {
|
|
|
216
214
|
return () => {
|
|
217
215
|
currentSubscriptions.purchaseUpdate?.remove();
|
|
218
216
|
currentSubscriptions.purchaseError?.remove();
|
|
219
|
-
currentSubscriptions.promotedProductsIOS?.remove();
|
|
220
217
|
currentSubscriptions.promotedProductIOS?.remove();
|
|
221
218
|
endConnection();
|
|
222
219
|
setConnected(false);
|
|
@@ -225,8 +222,6 @@ export function useIAP(options) {
|
|
|
225
222
|
return {
|
|
226
223
|
connected,
|
|
227
224
|
products,
|
|
228
|
-
promotedProductsIOS,
|
|
229
|
-
promotedProductIdIOS,
|
|
230
225
|
subscriptions,
|
|
231
226
|
finishTransaction,
|
|
232
227
|
availablePurchases,
|
package/build/useIAP.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIAP.js","sourceRoot":"","sources":["../src/useIAP.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\n// Internal modules\nimport {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n promotedProductListenerIOS,\n getAvailablePurchases,\n finishTransaction as finishTransactionInternal,\n requestPurchase as requestPurchaseInternal,\n fetchProducts,\n validateReceipt as validateReceiptInternal,\n getActiveSubscriptions,\n hasActiveSubscriptions,\n type ActiveSubscription,\n type ProductTypeInput,\n restorePurchases,\n} from './index';\nimport {\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n} from './modules/ios';\n\n// Types\nimport type {\n Product,\n ProductSubscription,\n ProductQueryType,\n ProductRequest,\n Purchase,\n MutationRequestPurchaseArgs,\n PurchaseInput,\n ReceiptValidationProps,\n ReceiptValidationResult,\n} from './types';\nimport {ErrorCode} from './types';\nimport type {PurchaseError} from './utils/errorMapping';\nimport {\n getUserFriendlyErrorMessage,\n isUserCancelledError,\n isRecoverableError,\n} from './utils/errorMapping';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: Purchase[];\n promotedProductIdIOS?: string;\n subscriptions: ProductSubscription[];\n availablePurchases: Purchase[];\n promotedProductIOS?: Product;\n activeSubscriptions: ActiveSubscription[];\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<void>;\n getAvailablePurchases: () => Promise<void>;\n fetchProducts: (params: {\n skus: string[];\n type?: ProductTypeInput;\n }) => Promise<void>;\n\n requestPurchase: (\n params: MutationRequestPurchaseArgs,\n ) => ReturnType<typeof requestPurchaseInternal>;\n validateReceipt: (\n props: ReceiptValidationProps,\n ) => Promise<ReceiptValidationResult>;\n restorePurchases: () => Promise<void>;\n getPromotedProductIOS: () => Promise<Product | null>;\n requestPurchaseOnPromotedProductIOS: () => Promise<boolean>;\n getActiveSubscriptions: (subscriptionIds?: string[]) => Promise<void>;\n hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean>;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (purchase: Purchase) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n onPromotedProductIOS?: (product: Product) => void;\n}\n\n/**\n * React Hook for managing In-App Purchases.\n * See documentation at https://hyochan.github.io/expo-iap/docs/hooks/useIAP\n */\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS] = useState<Purchase[]>([]);\n const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);\n\n const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);\n const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();\n const [promotedProductIdIOS] = useState<string>();\n const [activeSubscriptions, setActiveSubscriptions] = useState<\n ActiveSubscription[]\n >([]);\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n const connectedRef = useRef<boolean>(false);\n\n // Helper function to merge arrays with duplicate checking\n const mergeWithDuplicateCheck = useCallback(\n <T>(\n existingItems: T[],\n newItems: T[],\n getKey: (item: T) => string,\n ): T[] => {\n const merged = [...existingItems];\n newItems.forEach((newItem) => {\n const isDuplicate = merged.some(\n (existingItem) => getKey(existingItem) === getKey(newItem),\n );\n if (!isDuplicate) {\n merged.push(newItem);\n }\n });\n return merged;\n },\n [],\n );\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n connectedRef.current = connected;\n }, [connected]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductsIOS?: EventSubscription;\n promotedProductIOS?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<ProductSubscription[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const normalizeProductQueryType = useCallback(\n (type?: ProductTypeInput): ProductQueryType => {\n if (!type || type === 'inapp' || type === 'in-app') {\n return 'in-app';\n }\n return type;\n },\n [],\n );\n\n const canonicalProductType = useCallback(\n (value?: string): ProductQueryType => {\n if (!value) {\n return 'in-app';\n }\n const normalized = value.trim().toLowerCase().replace(/[_-]/g, '');\n return normalized === 'subs' ? 'subs' : 'in-app';\n },\n [],\n );\n\n const toPurchaseInput = useCallback(\n (purchase: Purchase): PurchaseInput => ({\n id: purchase.id,\n ids: purchase.ids ?? undefined,\n isAutoRenewing: purchase.isAutoRenewing,\n platform: purchase.platform,\n productId: purchase.productId,\n purchaseState: purchase.purchaseState,\n purchaseToken: purchase.purchaseToken ?? null,\n quantity: purchase.quantity,\n transactionDate: purchase.transactionDate,\n }),\n [],\n );\n\n const fetchProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: ProductTypeInput;\n }): Promise<void> => {\n try {\n const queryType = normalizeProductQueryType(params.type);\n const request: ProductRequest = {skus: params.skus, type: queryType};\n const result = await fetchProducts(request);\n const items = (result ?? []) as (Product | ProductSubscription)[];\n\n if (queryType === 'subs') {\n const subscriptionsResult = items as ProductSubscription[];\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionsResult,\n (subscription) => subscription.id,\n ),\n );\n } else if (queryType === 'in-app') {\n const productsResult = items as Product[];\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productsResult,\n (product) => product.id,\n ),\n );\n } else {\n const productItems = items.filter(\n (item) => canonicalProductType(item.type as string) === 'in-app',\n ) as Product[];\n const subscriptionItems = items.filter(\n (item) => canonicalProductType(item.type as string) === 'subs',\n ) as ProductSubscription[];\n\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productItems,\n (product) => product.id,\n ),\n );\n\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionItems,\n (subscription) => subscription.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [canonicalProductType, mergeWithDuplicateCheck, normalizeProductQueryType],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<void> => {\n try {\n const result = await getActiveSubscriptions(subscriptionIds);\n setActiveSubscriptions(result);\n } catch (error) {\n console.error('Error getting active subscriptions:', error);\n // Preserve existing state on error\n }\n },\n [],\n );\n\n const hasActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<boolean> => {\n try {\n return await hasActiveSubscriptions(subscriptionIds);\n } catch (error) {\n console.error('Error checking active subscriptions:', error);\n return false;\n }\n },\n [],\n );\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }): Promise<void> => {\n await finishTransactionInternal({\n purchase: toPurchaseInput(purchase),\n isConsumable,\n });\n },\n [toPurchaseInput],\n );\n\n const requestPurchaseWithReset = useCallback(\n (requestObj: MutationRequestPurchaseArgs) => {\n return requestPurchaseInternal(requestObj);\n },\n [],\n );\n\n const refreshSubscriptionStatus = useCallback(\n async (productId: string) => {\n try {\n if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {\n await fetchProductsInternal({skus: [productId], type: 'subs'});\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n },\n [fetchProductsInternal, getAvailablePurchasesInternal],\n );\n\n // Restore completed transactions with cross-platform behavior.\n // iOS: best-effort sync (ignore sync errors) then fetch available purchases.\n // Android: fetch available purchases directly.\n const restorePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n await restorePurchases();\n const purchases = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(purchases);\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, []);\n\n const validateReceipt = useCallback(async (props: ReceiptValidationProps) => {\n return validateReceiptInternal(props);\n }, []);\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n // CRITICAL: Register listeners BEFORE initConnection to avoid race condition\n // Events might fire immediately after initConnection, so listeners must be ready\n // Register purchase update listener BEFORE initConnection to avoid race conditions.\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase) => {\n if ('expirationDateIOS' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n // Register purchase error listener EARLY. Ignore init-related errors until connected.\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n if (!connectedRef.current && error.code === ErrorCode.InitConnection) {\n return; // Ignore initialization error before connected\n }\n const friendly = getUserFriendlyErrorMessage(error);\n if (!isUserCancelledError(error) && !isRecoverableError(error)) {\n console.warn('[useIAP] Purchase error:', friendly);\n }\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products listener\n subscriptionsRef.current.promotedProductsIOS = promotedProductListenerIOS(\n (product: Product) => {\n setPromotedProductIOS(product);\n\n if (optionsRef.current?.onPromotedProductIOS) {\n optionsRef.current.onPromotedProductIOS(product);\n }\n },\n );\n }\n\n // NOW call initConnection after listeners are ready\n const result = await initConnection();\n setConnected(result);\n if (!result) {\n // If connection failed, clean up listeners\n console.warn('[useIAP] Connection failed, cleaning up listeners...');\n subscriptionsRef.current.purchaseUpdate?.remove();\n subscriptionsRef.current.promotedProductsIOS?.remove();\n subscriptionsRef.current.purchaseUpdate = undefined;\n subscriptionsRef.current.promotedProductsIOS = undefined;\n // Keep purchaseError listener registered to capture subsequent retries\n return;\n }\n }, [refreshSubscriptionStatus]);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIOS?.remove();\n currentSubscriptions.promotedProductIOS?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n promotedProductIdIOS,\n subscriptions,\n finishTransaction,\n availablePurchases,\n promotedProductIOS,\n activeSubscriptions,\n getAvailablePurchases: getAvailablePurchasesInternal,\n fetchProducts: fetchProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases: restorePurchasesInternal,\n // internal getters kept for hook state management\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n getActiveSubscriptions: getActiveSubscriptionsInternal,\n hasActiveSubscriptions: hasActiveSubscriptionsInternal,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useIAP.js","sourceRoot":"","sources":["../src/useIAP.ts"],"names":[],"mappings":"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","sourcesContent":["// External dependencies\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {Platform} from 'react-native';\nimport {EventSubscription} from 'expo-modules-core';\n\n// Internal modules\nimport {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n promotedProductListenerIOS,\n getAvailablePurchases,\n finishTransaction as finishTransactionInternal,\n requestPurchase as requestPurchaseInternal,\n fetchProducts,\n validateReceipt as validateReceiptInternal,\n getActiveSubscriptions,\n hasActiveSubscriptions,\n type ActiveSubscription,\n type ProductTypeInput,\n restorePurchases,\n} from './index';\nimport {\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n} from './modules/ios';\n\n// Types\nimport type {\n Product,\n ProductSubscription,\n ProductQueryType,\n ProductRequest,\n Purchase,\n MutationRequestPurchaseArgs,\n PurchaseInput,\n ReceiptValidationProps,\n ReceiptValidationResult,\n} from './types';\nimport {ErrorCode} from './types';\nimport type {PurchaseError} from './utils/errorMapping';\nimport {\n getUserFriendlyErrorMessage,\n isUserCancelledError,\n isRecoverableError,\n} from './utils/errorMapping';\n\ntype UseIap = {\n connected: boolean;\n products: Product[];\n subscriptions: ProductSubscription[];\n availablePurchases: Purchase[];\n promotedProductIOS?: Product;\n activeSubscriptions: ActiveSubscription[];\n finishTransaction: ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }) => Promise<void>;\n getAvailablePurchases: () => Promise<void>;\n fetchProducts: (params: {\n skus: string[];\n type?: ProductTypeInput;\n }) => Promise<void>;\n\n requestPurchase: (\n params: MutationRequestPurchaseArgs,\n ) => ReturnType<typeof requestPurchaseInternal>;\n validateReceipt: (\n props: ReceiptValidationProps,\n ) => Promise<ReceiptValidationResult>;\n restorePurchases: () => Promise<void>;\n getPromotedProductIOS: () => Promise<Product | null>;\n requestPurchaseOnPromotedProductIOS: () => Promise<boolean>;\n getActiveSubscriptions: (subscriptionIds?: string[]) => Promise<void>;\n hasActiveSubscriptions: (subscriptionIds?: string[]) => Promise<boolean>;\n};\n\nexport interface UseIAPOptions {\n onPurchaseSuccess?: (purchase: Purchase) => void;\n onPurchaseError?: (error: PurchaseError) => void;\n onSyncError?: (error: Error) => void;\n shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing\n onPromotedProductIOS?: (product: Product) => void;\n}\n\n/**\n * React Hook for managing In-App Purchases.\n * See documentation at https://hyochan.github.io/expo-iap/docs/hooks/useIAP\n */\nexport function useIAP(options?: UseIAPOptions): UseIap {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);\n\n const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);\n const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();\n const [activeSubscriptions, setActiveSubscriptions] = useState<\n ActiveSubscription[]\n >([]);\n\n const optionsRef = useRef<UseIAPOptions | undefined>(options);\n const connectedRef = useRef<boolean>(false);\n\n // Helper function to merge arrays with duplicate checking\n const mergeWithDuplicateCheck = useCallback(\n <T>(\n existingItems: T[],\n newItems: T[],\n getKey: (item: T) => string,\n ): T[] => {\n const merged = [...existingItems];\n newItems.forEach((newItem) => {\n const isDuplicate = merged.some(\n (existingItem) => getKey(existingItem) === getKey(newItem),\n );\n if (!isDuplicate) {\n merged.push(newItem);\n }\n });\n return merged;\n },\n [],\n );\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n connectedRef.current = connected;\n }, [connected]);\n\n const subscriptionsRef = useRef<{\n purchaseUpdate?: EventSubscription;\n purchaseError?: EventSubscription;\n promotedProductIOS?: EventSubscription;\n }>({});\n\n const subscriptionsRefState = useRef<ProductSubscription[]>([]);\n\n useEffect(() => {\n subscriptionsRefState.current = subscriptions;\n }, [subscriptions]);\n\n const normalizeProductQueryType = useCallback(\n (type?: ProductTypeInput): ProductQueryType => {\n if (!type || type === 'inapp' || type === 'in-app') {\n return 'in-app';\n }\n return type;\n },\n [],\n );\n\n const canonicalProductType = useCallback(\n (value?: string): ProductQueryType => {\n if (!value) {\n return 'in-app';\n }\n const normalized = value.trim().toLowerCase().replace(/[_-]/g, '');\n return normalized === 'subs' ? 'subs' : 'in-app';\n },\n [],\n );\n\n const toPurchaseInput = useCallback(\n (purchase: Purchase): PurchaseInput => ({\n id: purchase.id,\n ids: purchase.ids ?? undefined,\n isAutoRenewing: purchase.isAutoRenewing,\n platform: purchase.platform,\n productId: purchase.productId,\n purchaseState: purchase.purchaseState,\n purchaseToken: purchase.purchaseToken ?? null,\n quantity: purchase.quantity,\n transactionDate: purchase.transactionDate,\n }),\n [],\n );\n\n const fetchProductsInternal = useCallback(\n async (params: {\n skus: string[];\n type?: ProductTypeInput;\n }): Promise<void> => {\n try {\n const queryType = normalizeProductQueryType(params.type);\n const request: ProductRequest = {skus: params.skus, type: queryType};\n const result = await fetchProducts(request);\n const items = (result ?? []) as (Product | ProductSubscription)[];\n\n if (queryType === 'subs') {\n const subscriptionsResult = items as ProductSubscription[];\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionsResult,\n (subscription) => subscription.id,\n ),\n );\n } else if (queryType === 'in-app') {\n const productsResult = items as Product[];\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productsResult,\n (product) => product.id,\n ),\n );\n } else {\n const productItems = items.filter(\n (item) => canonicalProductType(item.type as string) === 'in-app',\n ) as Product[];\n const subscriptionItems = items.filter(\n (item) => canonicalProductType(item.type as string) === 'subs',\n ) as ProductSubscription[];\n\n setProducts((prevProducts) =>\n mergeWithDuplicateCheck(\n prevProducts,\n productItems,\n (product) => product.id,\n ),\n );\n\n setSubscriptions((prevSubscriptions) =>\n mergeWithDuplicateCheck(\n prevSubscriptions,\n subscriptionItems,\n (subscription) => subscription.id,\n ),\n );\n }\n } catch (error) {\n console.error('Error fetching products:', error);\n }\n },\n [canonicalProductType, mergeWithDuplicateCheck, normalizeProductQueryType],\n );\n\n const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n const result = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(result);\n } catch (error) {\n console.error('Error fetching available purchases:', error);\n }\n }, []);\n\n const getActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<void> => {\n try {\n const result = await getActiveSubscriptions(subscriptionIds);\n setActiveSubscriptions(result);\n } catch (error) {\n console.error('Error getting active subscriptions:', error);\n // Preserve existing state on error\n }\n },\n [],\n );\n\n const hasActiveSubscriptionsInternal = useCallback(\n async (subscriptionIds?: string[]): Promise<boolean> => {\n try {\n return await hasActiveSubscriptions(subscriptionIds);\n } catch (error) {\n console.error('Error checking active subscriptions:', error);\n return false;\n }\n },\n [],\n );\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n }): Promise<void> => {\n await finishTransactionInternal({\n purchase: toPurchaseInput(purchase),\n isConsumable,\n });\n },\n [toPurchaseInput],\n );\n\n const requestPurchaseWithReset = useCallback(\n (requestObj: MutationRequestPurchaseArgs) => {\n return requestPurchaseInternal(requestObj);\n },\n [],\n );\n\n const refreshSubscriptionStatus = useCallback(\n async (productId: string) => {\n try {\n if (subscriptionsRefState.current.some((sub) => sub.id === productId)) {\n await fetchProductsInternal({skus: [productId], type: 'subs'});\n await getAvailablePurchasesInternal();\n }\n } catch (error) {\n console.warn('Failed to refresh subscription status:', error);\n }\n },\n [fetchProductsInternal, getAvailablePurchasesInternal],\n );\n\n // Restore completed transactions with cross-platform behavior.\n // iOS: best-effort sync (ignore sync errors) then fetch available purchases.\n // Android: fetch available purchases directly.\n const restorePurchasesInternal = useCallback(async (): Promise<void> => {\n try {\n await restorePurchases();\n const purchases = await getAvailablePurchases({\n alsoPublishToEventListenerIOS: false,\n onlyIncludeActiveItemsIOS: true,\n });\n setAvailablePurchases(purchases);\n } catch (error) {\n console.warn('Failed to restore purchases:', error);\n }\n }, []);\n\n const validateReceipt = useCallback(async (props: ReceiptValidationProps) => {\n return validateReceiptInternal(props);\n }, []);\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n // CRITICAL: Register listeners BEFORE initConnection to avoid race condition\n // Events might fire immediately after initConnection, so listeners must be ready\n // Register purchase update listener BEFORE initConnection to avoid race conditions.\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase) => {\n if ('expirationDateIOS' in purchase) {\n await refreshSubscriptionStatus(purchase.id);\n }\n\n if (optionsRef.current?.onPurchaseSuccess) {\n optionsRef.current.onPurchaseSuccess(purchase);\n }\n },\n );\n\n // Register purchase error listener EARLY. Ignore init-related errors until connected.\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n if (!connectedRef.current && error.code === ErrorCode.InitConnection) {\n return; // Ignore initialization error before connected\n }\n const friendly = getUserFriendlyErrorMessage(error);\n if (!isUserCancelledError(error) && !isRecoverableError(error)) {\n console.warn('[useIAP] Purchase error:', friendly);\n }\n\n if (optionsRef.current?.onPurchaseError) {\n optionsRef.current.onPurchaseError(error);\n }\n },\n );\n\n if (Platform.OS === 'ios') {\n // iOS promoted products listener\n subscriptionsRef.current.promotedProductIOS = promotedProductListenerIOS(\n (product: Product) => {\n setPromotedProductIOS(product);\n\n if (optionsRef.current?.onPromotedProductIOS) {\n optionsRef.current.onPromotedProductIOS(product);\n }\n },\n );\n }\n\n // NOW call initConnection after listeners are ready\n const result = await initConnection();\n setConnected(result);\n if (!result) {\n // If connection failed, clean up listeners\n console.warn('[useIAP] Connection failed, cleaning up listeners...');\n subscriptionsRef.current.purchaseUpdate?.remove();\n subscriptionsRef.current.promotedProductIOS?.remove();\n subscriptionsRef.current.purchaseUpdate = undefined;\n subscriptionsRef.current.promotedProductIOS = undefined;\n // Keep purchaseError listener registered to capture subsequent retries\n return;\n }\n }, [refreshSubscriptionStatus]);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductIOS?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n subscriptions,\n finishTransaction,\n availablePurchases,\n promotedProductIOS,\n activeSubscriptions,\n getAvailablePurchases: getAvailablePurchasesInternal,\n fetchProducts: fetchProductsInternal,\n requestPurchase: requestPurchaseWithReset,\n validateReceipt,\n restorePurchases: restorePurchasesInternal,\n // internal getters kept for hook state management\n getPromotedProductIOS,\n requestPurchaseOnPromotedProductIOS,\n getActiveSubscriptions: getActiveSubscriptionsInternal,\n hasActiveSubscriptions: hasActiveSubscriptionsInternal,\n };\n}\n"]}
|
package/expo-module.config.json
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"platforms": [
|
|
3
|
-
"ios",
|
|
4
|
-
"android"
|
|
5
|
-
],
|
|
2
|
+
"platforms": ["ios", "android"],
|
|
6
3
|
"ios": {
|
|
7
|
-
"modules": [
|
|
8
|
-
"OnsideIapModule"
|
|
9
|
-
]
|
|
4
|
+
"modules": ["ExpoIapModule"]
|
|
10
5
|
},
|
|
11
6
|
"android": {
|
|
12
|
-
"modules": [
|
|
13
|
-
"expo.modules.iap.ExpoIapModule"
|
|
14
|
-
]
|
|
7
|
+
"modules": ["expo.modules.iap.ExpoIapModule"]
|
|
15
8
|
}
|
|
16
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-iap",
|
|
3
|
-
"version": "3.1.1
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "In App Purchase module in Expo",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -13,10 +13,9 @@
|
|
|
13
13
|
"lint:tsc": "tsc -p tsconfig.json --noEmit --skipLibCheck",
|
|
14
14
|
"lint:kt": "sh -c 'command -v ktlint >/dev/null 2>&1 && ktlint --format ./android || { echo \"ktlint not installed; skipping\"; exit 0; }'",
|
|
15
15
|
"lint:ci": "bun run lint:tsc && bun run lint:eslint && bun run lint:prettier && bun run lint:kt",
|
|
16
|
-
"test": "jest
|
|
17
|
-
"test:plugin": "cd plugin && npx jest",
|
|
16
|
+
"test": "jest",
|
|
18
17
|
"test:coverage": "jest --coverage",
|
|
19
|
-
"prepare": "
|
|
18
|
+
"prepare": "expo-module prepare && sh -c 'command -v husky >/dev/null 2>&1 && husky install || { echo \"husky not installed; skipping\"; exit 0; }'",
|
|
20
19
|
"expo-module": "expo-module",
|
|
21
20
|
"open:ios": "xed example/ios",
|
|
22
21
|
"open:android": "open -a \"Android Studio\" example/android",
|
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
import { ConfigPlugin } from 'expo/config-plugins';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
name: string;
|
|
11
|
-
enable: boolean;
|
|
12
|
-
};
|
|
13
|
-
export declare function computeAutolinkModules(existing: string[], desired: AutolinkEntry[]): {
|
|
14
|
-
modules: string[];
|
|
15
|
-
added: string[];
|
|
16
|
-
removed: string[];
|
|
17
|
-
};
|
|
18
|
-
export interface ModuleSelectionResult {
|
|
19
|
-
selection: 'auto' | 'expo-iap' | 'onside';
|
|
20
|
-
includeExpoIap: boolean;
|
|
21
|
-
includeOnside: boolean;
|
|
2
|
+
export interface ExpoIapPluginOptions {
|
|
3
|
+
/** Local development path for OpenIAP library */
|
|
4
|
+
localPath?: string | {
|
|
5
|
+
ios?: string;
|
|
6
|
+
android?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Enable local development mode */
|
|
9
|
+
enableLocalDev?: boolean;
|
|
22
10
|
}
|
|
23
|
-
|
|
24
|
-
declare const _default: ConfigPlugin<void | ExpoIapPluginCommonOptions>;
|
|
11
|
+
declare const _default: ConfigPlugin<void | ExpoIapPluginOptions>;
|
|
25
12
|
export default _default;
|
package/plugin/build/withIAP.js
CHANGED
|
@@ -36,9 +36,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.modifyAppBuildGradle = void 0;
|
|
40
|
-
exports.computeAutolinkModules = computeAutolinkModules;
|
|
41
|
-
exports.resolveModuleSelection = resolveModuleSelection;
|
|
42
39
|
const config_plugins_1 = require("expo/config-plugins");
|
|
43
40
|
const fs = __importStar(require("fs"));
|
|
44
41
|
const path = __importStar(require("path"));
|
|
@@ -46,7 +43,6 @@ const withLocalOpenIAP_1 = __importDefault(require("./withLocalOpenIAP"));
|
|
|
46
43
|
const pkg = require('../../package.json');
|
|
47
44
|
const openiapVersions = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../openiap-versions.json'), 'utf8'));
|
|
48
45
|
const OPENIAP_ANDROID_VERSION = openiapVersions.google;
|
|
49
|
-
const AUTOLINKING_CONFIG_PATH = path.resolve(__dirname, '../../expo-module.config.json');
|
|
50
46
|
// Log a message only once per Node process
|
|
51
47
|
const logOnce = (() => {
|
|
52
48
|
const printed = new Set();
|
|
@@ -92,14 +88,13 @@ const modifyAppBuildGradle = (gradle, language) => {
|
|
|
92
88
|
}
|
|
93
89
|
return modified;
|
|
94
90
|
};
|
|
95
|
-
exports.modifyAppBuildGradle = modifyAppBuildGradle;
|
|
96
91
|
const withIapAndroid = (config, props) => {
|
|
97
92
|
const addDeps = props?.addDeps ?? true;
|
|
98
93
|
if (addDeps) {
|
|
99
94
|
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
100
95
|
// language provided by config-plugins: 'groovy' | 'kotlin'
|
|
101
96
|
const language = config.modResults.language || 'groovy';
|
|
102
|
-
config.modResults.contents =
|
|
97
|
+
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents, language);
|
|
103
98
|
return config;
|
|
104
99
|
});
|
|
105
100
|
}
|
|
@@ -122,88 +117,8 @@ const withIapAndroid = (config, props) => {
|
|
|
122
117
|
});
|
|
123
118
|
return config;
|
|
124
119
|
};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const podRegex = /^\s*pod\s+'OnsideKit'\b.*$/m;
|
|
128
|
-
if (podRegex.test(content)) {
|
|
129
|
-
return content;
|
|
130
|
-
}
|
|
131
|
-
const targetMatch = content.match(/target\s+'[^']+'\s+do\s*\n/);
|
|
132
|
-
if (!targetMatch) {
|
|
133
|
-
config_plugins_1.WarningAggregator.addWarningIOS('expo-iap', 'Could not find a target block in Podfile when adding OnsideKit; skipping installation.');
|
|
134
|
-
return content;
|
|
135
|
-
}
|
|
136
|
-
const insertIndex = targetMatch.index + targetMatch[0].length;
|
|
137
|
-
const before = content.slice(0, insertIndex);
|
|
138
|
-
const after = content.slice(insertIndex);
|
|
139
|
-
logOnce('📦 expo-iap: Added OnsideKit pod to Podfile');
|
|
140
|
-
return `${before}${podLine}\n${after}`;
|
|
141
|
-
};
|
|
142
|
-
function computeAutolinkModules(existing, desired) {
|
|
143
|
-
let modules = [...existing];
|
|
144
|
-
const added = [];
|
|
145
|
-
const removed = [];
|
|
146
|
-
for (const entry of desired) {
|
|
147
|
-
const hasModule = modules.includes(entry.name);
|
|
148
|
-
if (entry.enable && !hasModule) {
|
|
149
|
-
modules = [...modules, entry.name];
|
|
150
|
-
added.push(entry.name);
|
|
151
|
-
}
|
|
152
|
-
else if (!entry.enable && hasModule) {
|
|
153
|
-
modules = modules.filter((module) => module !== entry.name);
|
|
154
|
-
removed.push(entry.name);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return { modules, added, removed };
|
|
158
|
-
}
|
|
159
|
-
const syncAutolinking = (state) => {
|
|
160
|
-
if (!fs.existsSync(AUTOLINKING_CONFIG_PATH)) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
try {
|
|
164
|
-
const raw = fs.readFileSync(AUTOLINKING_CONFIG_PATH, 'utf8');
|
|
165
|
-
const config = JSON.parse(raw);
|
|
166
|
-
const iosConfig = config.ios ?? (config.ios = {});
|
|
167
|
-
const existing = Array.isArray(iosConfig.modules)
|
|
168
|
-
? iosConfig.modules.filter((module) => module !== 'OneSideModule')
|
|
169
|
-
: [];
|
|
170
|
-
const desiredEntries = [
|
|
171
|
-
{
|
|
172
|
-
name: 'ExpoIapModule',
|
|
173
|
-
enable: state.expoIap,
|
|
174
|
-
addLog: '🔗 expo-iap: Enabled ExpoIapModule autolinking',
|
|
175
|
-
removeLog: '🧹 expo-iap: Disabled ExpoIapModule autolinking',
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
name: 'OnsideIapModule',
|
|
179
|
-
enable: state.onside,
|
|
180
|
-
addLog: '🔗 expo-iap: Enabled OnsideIapModule autolinking',
|
|
181
|
-
removeLog: '🧹 expo-iap: Disabled OnsideIapModule autolinking',
|
|
182
|
-
},
|
|
183
|
-
];
|
|
184
|
-
const { modules: nextModules, added, removed, } = computeAutolinkModules(existing, desiredEntries.map(({ name, enable }) => ({ name, enable })));
|
|
185
|
-
for (const name of added) {
|
|
186
|
-
const entry = desiredEntries.find((candidate) => candidate.name === name);
|
|
187
|
-
if (entry) {
|
|
188
|
-
logOnce(entry.addLog);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
for (const name of removed) {
|
|
192
|
-
const entry = desiredEntries.find((candidate) => candidate.name === name);
|
|
193
|
-
if (entry) {
|
|
194
|
-
logOnce(entry.removeLog);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (added.length > 0 || removed.length > 0) {
|
|
198
|
-
iosConfig.modules = nextModules;
|
|
199
|
-
fs.writeFileSync(AUTOLINKING_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
config_plugins_1.WarningAggregator.addWarningIOS('expo-iap', `Failed to sync Expo IAP autolinking modules: ${String(error)}`);
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const withIapIOS = (config, props) => {
|
|
120
|
+
/** Ensure Podfile uses CocoaPods CDN and no stale local OpenIAP entry remains. */
|
|
121
|
+
const withIapIOS = (config) => {
|
|
207
122
|
return (0, config_plugins_1.withDangerousMod)(config, [
|
|
208
123
|
'ios',
|
|
209
124
|
async (config) => {
|
|
@@ -225,77 +140,17 @@ const withIapIOS = (config, props) => {
|
|
|
225
140
|
content = content.replace(localPodRegex, '').replace(/\n{3,}/g, '\n\n');
|
|
226
141
|
logOnce('🧹 expo-iap: Removed local OpenIAP pod from Podfile');
|
|
227
142
|
}
|
|
228
|
-
// 3) Optionally install OnsideKit when enabled in config
|
|
229
|
-
if (props?.enableOnside) {
|
|
230
|
-
content = ensureOnsidePod(content);
|
|
231
|
-
}
|
|
232
143
|
fs.writeFileSync(podfilePath, content);
|
|
233
144
|
return config;
|
|
234
145
|
},
|
|
235
146
|
]);
|
|
236
147
|
};
|
|
237
|
-
const
|
|
238
|
-
expoIap: {
|
|
239
|
-
when: {
|
|
240
|
-
'expo-iap': true,
|
|
241
|
-
onside: false,
|
|
242
|
-
},
|
|
243
|
-
default: ({ options }) => options?.modules?.expoIap ?? true,
|
|
244
|
-
},
|
|
245
|
-
onside: {
|
|
246
|
-
when: {
|
|
247
|
-
'expo-iap': false,
|
|
248
|
-
onside: true,
|
|
249
|
-
},
|
|
250
|
-
default: ({ config, options }) => options?.modules?.onside ?? config.ios?.onside?.enabled ?? true,
|
|
251
|
-
},
|
|
252
|
-
};
|
|
253
|
-
function resolveModuleSelection(config, options) {
|
|
254
|
-
const normalizedOptions = (options ?? undefined);
|
|
255
|
-
const selection = normalizedOptions?.module ?? 'auto';
|
|
256
|
-
const includeExpoIap = pickModuleState('expoIap', selection, config, normalizedOptions);
|
|
257
|
-
const includeOnside = pickModuleState('onside', selection, config, normalizedOptions);
|
|
258
|
-
return { selection, includeExpoIap, includeOnside };
|
|
259
|
-
}
|
|
260
|
-
function pickModuleState(key, selection, config, options) {
|
|
261
|
-
const rules = MODULE_RULES[key];
|
|
262
|
-
const explicit = rules.when[selection];
|
|
263
|
-
if (explicit !== undefined) {
|
|
264
|
-
return explicit;
|
|
265
|
-
}
|
|
266
|
-
const override = options?.modules?.[key];
|
|
267
|
-
if (override !== undefined) {
|
|
268
|
-
return override;
|
|
269
|
-
}
|
|
270
|
-
return rules.default({ config, options });
|
|
271
|
-
}
|
|
272
|
-
const withIAP = (config, options) => {
|
|
148
|
+
const withIap = (config, options) => {
|
|
273
149
|
try {
|
|
274
|
-
const { includeExpoIap, includeOnside } = resolveModuleSelection(config, options);
|
|
275
|
-
const autolinkState = {
|
|
276
|
-
expoIap: includeExpoIap,
|
|
277
|
-
onside: includeOnside,
|
|
278
|
-
};
|
|
279
|
-
if (includeOnside) {
|
|
280
|
-
config.ios = {
|
|
281
|
-
...config.ios,
|
|
282
|
-
onside: {
|
|
283
|
-
...(config.ios?.onside ?? {}),
|
|
284
|
-
enabled: true,
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
else if (config.ios?.onside?.enabled) {
|
|
289
|
-
config.ios.onside.enabled = false;
|
|
290
|
-
}
|
|
291
150
|
// Respect explicit flag; fall back to presence of localPath only when flag is unset
|
|
292
151
|
const isLocalDev = options?.enableLocalDev ?? !!options?.localPath;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
// Apply Android modifications (skip when Expo IAP disabled)
|
|
296
|
-
let result = shouldConfigureAndroid
|
|
297
|
-
? withIapAndroid(config, { addDeps: shouldAddAndroidDeps })
|
|
298
|
-
: config;
|
|
152
|
+
// Apply Android modifications (skip adding deps when linking local module)
|
|
153
|
+
let result = withIapAndroid(config, { addDeps: !isLocalDev });
|
|
299
154
|
// iOS: choose one path to avoid overlap
|
|
300
155
|
if (isLocalDev) {
|
|
301
156
|
if (!options?.localPath) {
|
|
@@ -318,12 +173,9 @@ const withIAP = (config, options) => {
|
|
|
318
173
|
}
|
|
319
174
|
else {
|
|
320
175
|
// Ensure iOS Podfile is set up to resolve public CocoaPods specs
|
|
321
|
-
result = withIapIOS(result
|
|
322
|
-
|
|
323
|
-
logOnce('📦 [expo-iap] Using OpenIAP from CocoaPods');
|
|
324
|
-
}
|
|
176
|
+
result = withIapIOS(result);
|
|
177
|
+
logOnce('📦 [expo-iap] Using OpenIAP from CocoaPods');
|
|
325
178
|
}
|
|
326
|
-
syncAutolinking(autolinkState);
|
|
327
179
|
return result;
|
|
328
180
|
}
|
|
329
181
|
catch (error) {
|
|
@@ -332,4 +184,4 @@ const withIAP = (config, options) => {
|
|
|
332
184
|
return config;
|
|
333
185
|
}
|
|
334
186
|
};
|
|
335
|
-
exports.default = (0, config_plugins_1.createRunOncePlugin)(
|
|
187
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withIap, pkg.name, pkg.version);
|
package/plugin/jest.config.js
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
+
// In documentation there is `preset: expo-module-scripts`, but it runs tests for every platform (ios, android, web, node)
|
|
2
|
+
// We need only node tests right now
|
|
1
3
|
module.exports = {
|
|
2
|
-
preset: '
|
|
3
|
-
testEnvironment: 'node',
|
|
4
|
-
transform: {
|
|
5
|
-
'^.+\\.(ts|tsx)$': [
|
|
6
|
-
'ts-jest',
|
|
7
|
-
{
|
|
8
|
-
tsconfig: '<rootDir>/tsconfig.json',
|
|
9
|
-
},
|
|
10
|
-
],
|
|
11
|
-
},
|
|
12
|
-
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
|
13
|
-
roots: ['<rootDir>/__tests__'],
|
|
14
|
-
testMatch: ['**/?(*.)+(spec|test).ts?(x)'],
|
|
4
|
+
preset: 'jest-expo/node',
|
|
15
5
|
};
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type ExpoIapModuleOverrides = {
|
|
4
|
-
expoIap?: boolean;
|
|
5
|
-
onside?: boolean;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
type BaseExpoIapOptions = {
|
|
1
|
+
export interface ExpoIapPluginCommonOptions {
|
|
9
2
|
enableLocalDev?: boolean;
|
|
10
3
|
localPath?:
|
|
11
4
|
| string
|
|
@@ -13,26 +6,4 @@ type BaseExpoIapOptions = {
|
|
|
13
6
|
ios?: string;
|
|
14
7
|
android?: string;
|
|
15
8
|
};
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type AutoModuleOptions = BaseExpoIapOptions & {
|
|
19
|
-
module?: 'auto';
|
|
20
|
-
modules?: ExpoIapModuleOverrides;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
type ExplicitModuleOptions = BaseExpoIapOptions & {
|
|
24
|
-
module: 'expo-iap' | 'onside';
|
|
25
|
-
modules?: never;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type ExpoIapPluginCommonOptions =
|
|
29
|
-
| AutoModuleOptions
|
|
30
|
-
| ExplicitModuleOptions;
|
|
31
|
-
|
|
32
|
-
declare module '@expo/config-types' {
|
|
33
|
-
interface IOS {
|
|
34
|
-
onside?: {
|
|
35
|
-
enabled?: boolean;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
9
|
}
|