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.
Files changed (48) hide show
  1. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +0 -11
  2. package/build/ExpoIapModule.d.ts +0 -1
  3. package/build/ExpoIapModule.d.ts.map +1 -1
  4. package/build/ExpoIapModule.js +4 -29
  5. package/build/ExpoIapModule.js.map +1 -1
  6. package/build/useIAP.d.ts +0 -2
  7. package/build/useIAP.d.ts.map +1 -1
  8. package/build/useIAP.js +3 -8
  9. package/build/useIAP.js.map +1 -1
  10. package/expo-module.config.json +3 -10
  11. package/package.json +3 -4
  12. package/plugin/build/withIAP.d.ts +9 -22
  13. package/plugin/build/withIAP.js +9 -157
  14. package/plugin/jest.config.js +3 -13
  15. package/plugin/src/expoConfig.augmentation.d.ts +1 -30
  16. package/plugin/src/withIAP.ts +18 -258
  17. package/plugin/tsconfig.json +1 -2
  18. package/plugin/tsconfig.tsbuildinfo +1 -1
  19. package/src/ExpoIapModule.ts +4 -45
  20. package/src/useIAP.ts +3 -11
  21. package/coverage/clover.xml +0 -497
  22. package/coverage/coverage-final.json +0 -6
  23. package/coverage/lcov-report/base.css +0 -224
  24. package/coverage/lcov-report/block-navigation.js +0 -87
  25. package/coverage/lcov-report/favicon.png +0 -0
  26. package/coverage/lcov-report/index.html +0 -161
  27. package/coverage/lcov-report/prettify.css +0 -1
  28. package/coverage/lcov-report/prettify.js +0 -2
  29. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  30. package/coverage/lcov-report/sorter.js +0 -196
  31. package/coverage/lcov-report/src/ExpoIap.types.ts.html +0 -1243
  32. package/coverage/lcov-report/src/PurchaseError.ts.html +0 -787
  33. package/coverage/lcov-report/src/helpers/index.html +0 -116
  34. package/coverage/lcov-report/src/helpers/subscription.ts.html +0 -496
  35. package/coverage/lcov-report/src/index.html +0 -116
  36. package/coverage/lcov-report/src/index.ts.html +0 -1993
  37. package/coverage/lcov-report/src/modules/android.ts.html +0 -550
  38. package/coverage/lcov-report/src/modules/index.html +0 -131
  39. package/coverage/lcov-report/src/modules/ios.ts.html +0 -1222
  40. package/coverage/lcov-report/src/purchase-error.ts.html +0 -880
  41. package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +0 -493
  42. package/coverage/lcov-report/src/types/index.html +0 -116
  43. package/coverage/lcov-report/src/useIap.ts.html +0 -1483
  44. package/coverage/lcov-report/src/utils/errorMapping.ts.html +0 -1069
  45. package/coverage/lcov-report/src/utils/index.html +0 -116
  46. package/coverage/lcov-report/src/utils/purchase.ts.html +0 -241
  47. package/coverage/lcov.info +0 -929
  48. 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() },
@@ -1,5 +1,4 @@
1
1
  declare const ExpoIapModule: any;
2
- export declare const USING_ONSIDE_SDK: boolean;
3
2
  export declare const NATIVE_ERROR_CODES: any;
4
3
  export default ExpoIapModule;
5
4
  //# sourceMappingURL=ExpoIapModule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"AAIA,QAAA,MAAe,aAAa,KACL,CAAC;AAExB,eAAO,MAAM,gBAAgB,SAA+C,CAAC;AAG7E,eAAO,MAAM,kBAAkB,KAAkC,CAAC;AAElE,eAAe,aAAa,CAAC"}
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"}
@@ -1,33 +1,8 @@
1
- import { requireNativeModule, UnavailabilityError } from 'expo-modules-core';
2
- const { module: ExpoIapModule, name: resolvedNativeModuleName } = resolveNativeModule();
3
- export const USING_ONSIDE_SDK = resolvedNativeModuleName === 'ExpoIapOnside';
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,EAAE,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAI3E,MAAM,EAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,wBAAwB,EAAC,GAC3D,mBAAmB,EAAE,CAAC;AAExB,MAAM,CAAC,MAAM,gBAAgB,GAAG,wBAAwB,KAAK,eAAe,CAAC;AAE7E,oDAAoD;AACpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC,WAAW,IAAI,EAAE,CAAC;AAElE,eAAe,aAAa,CAAC;AAE7B,SAAS,mBAAmB;IAI1B,MAAM,UAAU,GAA0B,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAEvE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACzC,OAAO,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,IAAI,KAAK,eAAe,IAAI,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;gBAClE,mEAAmE;gBACnE,SAAS;YACX,CAAC;YAED,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,IAAI,mBAAmB,CAC3B,UAAU,EACV,sCAAsC,CACvC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAc,EAAE,UAAkB;IAC9D,IAAI,KAAK,YAAY,mBAAmB,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,UAAU,GAAG,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import {requireNativeModule, UnavailabilityError} from 'expo-modules-core';\n\ntype NativeIapModuleName = 'ExpoIapOnside' | 'ExpoIap';\n\nconst {module: ExpoIapModule, name: resolvedNativeModuleName} =\n resolveNativeModule();\n\nexport const USING_ONSIDE_SDK = resolvedNativeModuleName === 'ExpoIapOnside';\n\n// Platform-specific error codes from native modules\nexport const NATIVE_ERROR_CODES = ExpoIapModule.ERROR_CODES || {};\n\nexport default ExpoIapModule;\n\nfunction resolveNativeModule(): {\n module: any;\n name: NativeIapModuleName;\n} {\n const candidates: NativeIapModuleName[] = ['ExpoIapOnside', 'ExpoIap'];\n\n for (const name of candidates) {\n try {\n const module = requireNativeModule(name);\n return {module, name};\n } catch (error) {\n if (name === 'ExpoIapOnside' && isMissingModuleError(error, name)) {\n // Onside module is optional. If unavailable, fall back to ExpoIap.\n continue;\n }\n\n throw error;\n }\n }\n\n throw new UnavailabilityError(\n 'expo-iap',\n 'ExpoIap native module is unavailable',\n );\n}\n\nfunction isMissingModuleError(error: unknown, moduleName: string): boolean {\n if (error instanceof UnavailabilityError) {\n return true;\n }\n\n if (error instanceof Error) {\n return error.message.includes(`Cannot find native module '${moduleName}'`);\n }\n\n return false;\n}\n"]}
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;
@@ -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,mBAAmB,EAAE,QAAQ,EAAE,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,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,CAwVtD"}
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.promotedProductsIOS = promotedProductListenerIOS((product) => {
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.promotedProductsIOS?.remove();
204
+ subscriptionsRef.current.promotedProductIOS?.remove();
207
205
  subscriptionsRef.current.purchaseUpdate = undefined;
208
- subscriptionsRef.current.promotedProductsIOS = undefined;
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,
@@ -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"]}
@@ -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-rc.2",
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 && bun run test:plugin",
17
- "test:plugin": "cd plugin && npx jest",
16
+ "test": "jest",
18
17
  "test:coverage": "jest --coverage",
19
- "prepare": "bun clean:plugin && bun clean && expo-module prepare && sh -c 'command -v husky >/dev/null 2>&1 && husky install || { echo \"husky not installed; skipping\"; exit 0; }'",
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
- import type { ExpoConfig } from '@expo/config-types';
3
- import type { ExpoIapPluginCommonOptions } from './expoConfig.augmentation';
4
- export declare const modifyAppBuildGradle: (gradle: string, language: "groovy" | "kotlin") => string;
5
- export type AutolinkState = {
6
- expoIap: boolean;
7
- onside: boolean;
8
- };
9
- type AutolinkEntry = {
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
- export declare function resolveModuleSelection(config: ExpoConfig, options?: ExpoIapPluginCommonOptions | void): ModuleSelectionResult;
24
- declare const _default: ConfigPlugin<void | ExpoIapPluginCommonOptions>;
11
+ declare const _default: ConfigPlugin<void | ExpoIapPluginOptions>;
25
12
  export default _default;
@@ -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 = (0, exports.modifyAppBuildGradle)(config.modResults.contents, language);
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
- const ensureOnsidePod = (content) => {
126
- const podLine = " pod 'OnsideKit', :git => 'https://github.com/onside-io/OnsideKit-iOS.git'";
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 MODULE_RULES = {
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
- const shouldConfigureAndroid = includeExpoIap;
294
- const shouldAddAndroidDeps = includeExpoIap && !isLocalDev;
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, { enableOnside: includeOnside });
322
- if (includeExpoIap) {
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)(withIAP, pkg.name, pkg.version);
187
+ exports.default = (0, config_plugins_1.createRunOncePlugin)(withIap, pkg.name, pkg.version);
@@ -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: 'ts-jest',
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
- import type {IOS} from '@expo/config-types';
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
  }