expo-iap 3.1.4 → 3.1.6

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 (39) hide show
  1. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +5 -5
  2. package/build/index.d.ts +1 -1
  3. package/build/index.d.ts.map +1 -1
  4. package/build/index.js +3 -7
  5. package/build/index.js.map +1 -1
  6. package/build/modules/android.d.ts.map +1 -1
  7. package/build/modules/android.js +3 -2
  8. package/build/modules/android.js.map +1 -1
  9. package/build/modules/ios.d.ts +0 -15
  10. package/build/modules/ios.d.ts.map +1 -1
  11. package/build/modules/ios.js +1 -22
  12. package/build/modules/ios.js.map +1 -1
  13. package/build/types.d.ts +7 -1
  14. package/build/types.d.ts.map +1 -1
  15. package/build/types.js.map +1 -1
  16. package/build/useIAP.d.ts.map +1 -1
  17. package/build/useIAP.js +25 -2
  18. package/build/useIAP.js.map +1 -1
  19. package/coverage/clover.xml +236 -242
  20. package/coverage/coverage-final.json +3 -3
  21. package/coverage/lcov-report/index.html +22 -22
  22. package/coverage/lcov-report/src/helpers/index.html +1 -1
  23. package/coverage/lcov-report/src/helpers/subscription.ts.html +1 -1
  24. package/coverage/lcov-report/src/index.html +15 -15
  25. package/coverage/lcov-report/src/index.ts.html +13 -28
  26. package/coverage/lcov-report/src/modules/android.ts.html +36 -6
  27. package/coverage/lcov-report/src/modules/index.html +12 -12
  28. package/coverage/lcov-report/src/modules/ios.ts.html +7 -73
  29. package/coverage/lcov-report/src/utils/errorMapping.ts.html +1 -1
  30. package/coverage/lcov-report/src/utils/index.html +1 -1
  31. package/coverage/lcov.info +414 -422
  32. package/ios/ExpoIapModule.swift +3 -3
  33. package/openiap-versions.json +3 -3
  34. package/package.json +1 -1
  35. package/src/index.ts +3 -8
  36. package/src/modules/android.ts +12 -2
  37. package/src/modules/ios.ts +1 -23
  38. package/src/types.ts +7 -1
  39. package/src/useIAP.ts +39 -6
@@ -246,11 +246,11 @@ public final class ExpoIapModule: Module {
246
246
  return nil
247
247
  }
248
248
 
249
- AsyncFunction("getStorefrontIOS") { () async throws -> String in
250
- ExpoIapLog.payload("getStorefrontIOS", payload: nil)
249
+ AsyncFunction("getStorefront") { () async throws -> String in
250
+ ExpoIapLog.payload("getStorefront", payload: nil)
251
251
  try await ExpoIapHelper.ensureConnection(isInitialized: self.isInitialized)
252
252
  let storefront = try await OpenIapModule.shared.getStorefrontIOS()
253
- ExpoIapLog.result("getStorefrontIOS", value: storefront)
253
+ ExpoIapLog.result("getStorefront", value: storefront)
254
254
  return storefront
255
255
  }
256
256
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "apple": "1.2.3",
3
- "google": "1.2.7",
4
- "gql": "1.0.8"
2
+ "apple": "1.2.4",
3
+ "google": "1.2.9",
4
+ "gql": "1.0.9"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "3.1.4",
3
+ "version": "3.1.6",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
package/src/index.ts CHANGED
@@ -9,7 +9,6 @@ import {
9
9
  validateReceiptIOS,
10
10
  deepLinkToSubscriptionsIOS,
11
11
  syncIOS,
12
- getStorefrontIOS,
13
12
  } from './modules/ios';
14
13
  import {
15
14
  isProductAndroid,
@@ -292,15 +291,11 @@ export const getAvailablePurchases: QueryField<
292
291
  return normalizePurchaseArray(purchases as Purchase[]);
293
292
  };
294
293
 
295
- export const getStorefront: QueryField<'getStorefrontIOS'> = async () => {
296
- // Cross-platform storefront
297
- if (Platform.OS === 'android') {
298
- if (typeof ExpoIapModule.getStorefrontAndroid === 'function') {
299
- return ExpoIapModule.getStorefrontAndroid();
300
- }
294
+ export const getStorefront: QueryField<'getStorefront'> = async () => {
295
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
301
296
  return '';
302
297
  }
303
- return getStorefrontIOS();
298
+ return ExpoIapModule.getStorefront();
304
299
  };
305
300
 
306
301
  /**
@@ -11,6 +11,16 @@ import type {
11
11
  ReceiptValidationResultAndroid,
12
12
  } from '../types';
13
13
 
14
+ type NativeAndroidModule = {
15
+ deepLinkToSubscriptionsAndroid?: (params: {
16
+ skuAndroid?: string;
17
+ packageNameAndroid?: string;
18
+ }) => Promise<void> | void;
19
+ getStorefront?: () => Promise<string> | string;
20
+ };
21
+
22
+ const nativeAndroidModule = ExpoIapModule as NativeAndroidModule;
23
+
14
24
  // Type guards
15
25
  export function isProductAndroid<T extends {platform?: string}>(
16
26
  item: unknown,
@@ -46,8 +56,8 @@ export const deepLinkToSubscriptionsAndroid = async (
46
56
  const packageName = options?.packageNameAndroid ?? undefined;
47
57
 
48
58
  // Prefer native deep link implementation via OpenIAP module
49
- if (ExpoIapModule?.deepLinkToSubscriptionsAndroid) {
50
- return (ExpoIapModule as any).deepLinkToSubscriptionsAndroid({
59
+ if (nativeAndroidModule?.deepLinkToSubscriptionsAndroid) {
60
+ return nativeAndroidModule.deepLinkToSubscriptionsAndroid({
51
61
  skuAndroid: sku,
52
62
  packageNameAndroid: packageName,
53
63
  });
@@ -16,7 +16,7 @@ import type {
16
16
  SubscriptionStatusIOS,
17
17
  } from '../types';
18
18
  import type {PurchaseError} from '../utils/errorMapping';
19
- import {Linking, Platform} from 'react-native';
19
+ import {Linking} from 'react-native';
20
20
 
21
21
  export type TransactionEvent = {
22
22
  transaction?: Purchase;
@@ -178,28 +178,6 @@ export const getReceiptDataIOS: QueryField<'getReceiptDataIOS'> = async () => {
178
178
 
179
179
  export const getReceiptIOS = getReceiptDataIOS;
180
180
 
181
- /**
182
- * Retrieves the current storefront information from the iOS App Store.
183
- *
184
- * @returns Promise resolving to the storefront country code
185
- * @throws Error if called on non-iOS platform
186
- *
187
- * @example
188
- * ```typescript
189
- * const storefront = await getStorefrontIOS();
190
- * console.log(storefront); // 'US'
191
- * ```
192
- *
193
- * @platform iOS
194
- */
195
- export const getStorefrontIOS: QueryField<'getStorefrontIOS'> = async () => {
196
- if (Platform.OS !== 'ios') {
197
- console.warn('getStorefrontIOS: This method is only available on iOS');
198
- return '';
199
- }
200
- return ExpoIapModule.getStorefrontIOS();
201
- };
202
-
203
181
  /**
204
182
  * Check if a transaction is verified through StoreKit 2.
205
183
  * StoreKit 2 performs local verification of transaction JWS signatures.
package/src/types.ts CHANGED
@@ -449,7 +449,12 @@ export interface Query {
449
449
  getPromotedProductIOS?: Promise<(ProductIOS | null)>;
450
450
  /** Get base64-encoded receipt data for validation */
451
451
  getReceiptDataIOS?: Promise<(string | null)>;
452
- /** Get the current App Store storefront country code */
452
+ /** Get the current storefront country code */
453
+ getStorefront: Promise<string>;
454
+ /**
455
+ * Get the current App Store storefront country code
456
+ * @deprecated Use getStorefront
457
+ */
453
458
  getStorefrontIOS: Promise<string>;
454
459
  /** Get the transaction JWS (StoreKit 2) */
455
460
  getTransactionJwsIOS?: Promise<(string | null)>;
@@ -680,6 +685,7 @@ export type QueryArgsMap = {
680
685
  getPendingTransactionsIOS: never;
681
686
  getPromotedProductIOS: never;
682
687
  getReceiptDataIOS: never;
688
+ getStorefront: never;
683
689
  getStorefrontIOS: never;
684
690
  getTransactionJwsIOS: QueryGetTransactionJwsIosArgs;
685
691
  hasActiveSubscriptions: QueryHasActiveSubscriptionsArgs;
package/src/useIAP.ts CHANGED
@@ -37,6 +37,8 @@ import type {
37
37
  PurchaseInput,
38
38
  ReceiptValidationProps,
39
39
  ReceiptValidationResult,
40
+ ProductAndroid,
41
+ ProductSubscriptionIOS,
40
42
  } from './types';
41
43
  import {ErrorCode} from './types';
42
44
  import type {PurchaseError} from './utils/errorMapping';
@@ -159,6 +161,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
159
161
  if (!value) {
160
162
  return 'in-app';
161
163
  }
164
+
162
165
  const normalized = value.trim().toLowerCase().replace(/[_-]/g, '');
163
166
  return normalized === 'subs' ? 'subs' : 'in-app';
164
167
  },
@@ -191,6 +194,8 @@ export function useIAP(options?: UseIAPOptions): UseIap {
191
194
  const result = await fetchProducts(request);
192
195
  const items = (result ?? []) as (Product | ProductSubscription)[];
193
196
 
197
+ console.log('Fetched products:', items);
198
+
194
199
  if (queryType === 'subs') {
195
200
  const subscriptionsResult = items as ProductSubscription[];
196
201
  setSubscriptions((prevSubscriptions) =>
@@ -210,12 +215,40 @@ export function useIAP(options?: UseIAPOptions): UseIap {
210
215
  ),
211
216
  );
212
217
  } else {
213
- const productItems = items.filter(
214
- (item) => canonicalProductType(item.type as string) === 'in-app',
215
- ) as Product[];
216
- const subscriptionItems = items.filter(
217
- (item) => canonicalProductType(item.type as string) === 'subs',
218
- ) as ProductSubscription[];
218
+ // For 'all' type, need to properly distinguish between products and subscriptions
219
+ // On Android, check subscriptionOfferDetailsAndroid to determine if it's a real subscription
220
+ const productItems = items.filter((item) => {
221
+ // iOS: check type
222
+ if (Platform.OS === 'ios') {
223
+ return canonicalProductType(item.type as string) === 'in-app';
224
+ }
225
+ // Android: check if it has actual subscription details
226
+ const androidItem = item as ProductAndroid;
227
+ return (
228
+ !androidItem.subscriptionOfferDetailsAndroid ||
229
+ (Array.isArray(androidItem.subscriptionOfferDetailsAndroid) &&
230
+ androidItem.subscriptionOfferDetailsAndroid.length === 0)
231
+ );
232
+ }) as Product[];
233
+
234
+ const subscriptionItems = items.filter((item) => {
235
+ // iOS: check type
236
+ if (Platform.OS === 'ios') {
237
+ return (
238
+ canonicalProductType(
239
+ item.type as ProductSubscriptionIOS['type'],
240
+ ) === 'subs'
241
+ );
242
+ }
243
+ // Android: check if it has actual subscription details
244
+ const androidItem = item as ProductAndroid;
245
+
246
+ return (
247
+ androidItem.subscriptionOfferDetailsAndroid &&
248
+ Array.isArray(androidItem.subscriptionOfferDetailsAndroid) &&
249
+ androidItem.subscriptionOfferDetailsAndroid.length > 0
250
+ );
251
+ }) as ProductSubscription[];
219
252
 
220
253
  setProducts((prevProducts) =>
221
254
  mergeWithDuplicateCheck(