expo-iap 2.7.9 → 2.7.11

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.
@@ -1,15 +1,11 @@
1
1
  import {
2
2
  ProductAndroid,
3
3
  ProductPurchaseAndroid,
4
- RequestPurchaseAndroidProps,
5
- RequestSubscriptionAndroidProps,
6
4
  SubscriptionProductAndroid,
7
5
  } from './types/ExpoIapAndroid.types';
8
6
  import {
9
7
  ProductIos,
10
8
  ProductPurchaseIos,
11
- RequestPurchaseIosProps,
12
- RequestSubscriptionIosProps,
13
9
  SubscriptionProductIos,
14
10
  } from './types/ExpoIapIos.types';
15
11
  import {NATIVE_ERROR_CODES} from './ExpoIapModule';
@@ -51,15 +47,6 @@ export type SubscriptionProduct =
51
47
  | (SubscriptionProductAndroid & AndroidPlatform)
52
48
  | (SubscriptionProductIos & IosPlatform);
53
49
 
54
- // Legacy internal platform-specific types (kept for backward compatibility)
55
- export type LegacyRequestPurchaseProps =
56
- | RequestPurchaseIosProps
57
- | RequestPurchaseAndroidProps;
58
-
59
- export type LegacyRequestSubscriptionProps =
60
- | RequestSubscriptionAndroidProps
61
- | RequestSubscriptionIosProps;
62
-
63
50
  // ============================================================================
64
51
  // Legacy Types (For backward compatibility with useIap hook)
65
52
  // ============================================================================
@@ -323,20 +310,6 @@ export interface UnifiedRequestPurchaseProps {
323
310
  readonly isOfferPersonalized?: boolean;
324
311
  }
325
312
 
326
- /**
327
- * Unified subscription request props
328
- */
329
- export interface UnifiedRequestSubscriptionProps
330
- extends UnifiedRequestPurchaseProps {
331
- // Android subscription-specific properties
332
- readonly purchaseTokenAndroid?: string;
333
- readonly replacementModeAndroid?: number;
334
- readonly subscriptionOffers?: {
335
- sku: string;
336
- offerToken: string;
337
- }[];
338
- }
339
-
340
313
  // ============================================================================
341
314
  // New Platform-Specific Request Types (v2.7.0+)
342
315
  // ============================================================================
@@ -344,7 +317,7 @@ export interface UnifiedRequestSubscriptionProps
344
317
  /**
345
318
  * iOS-specific purchase request parameters
346
319
  */
347
- export interface IosRequestPurchaseProps {
320
+ export interface RequestPurchaseIosProps {
348
321
  readonly sku: string;
349
322
  readonly andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
350
323
  readonly appAccountToken?: string;
@@ -355,7 +328,7 @@ export interface IosRequestPurchaseProps {
355
328
  /**
356
329
  * Android-specific purchase request parameters
357
330
  */
358
- export interface AndroidRequestPurchaseProps {
331
+ export interface RequestPurchaseAndroidProps {
359
332
  readonly skus: string[];
360
333
  readonly obfuscatedAccountIdAndroid?: string;
361
334
  readonly obfuscatedProfileIdAndroid?: string;
@@ -365,8 +338,8 @@ export interface AndroidRequestPurchaseProps {
365
338
  /**
366
339
  * Android-specific subscription request parameters
367
340
  */
368
- export interface AndroidRequestSubscriptionProps
369
- extends AndroidRequestPurchaseProps {
341
+ export interface RequestSubscriptionAndroidProps
342
+ extends RequestPurchaseAndroidProps {
370
343
  readonly purchaseTokenAndroid?: string;
371
344
  readonly replacementModeAndroid?: number;
372
345
  readonly subscriptionOffers: {
@@ -379,88 +352,29 @@ export interface AndroidRequestSubscriptionProps
379
352
  * Modern platform-specific request structure (v2.7.0+)
380
353
  * Allows clear separation of iOS and Android parameters
381
354
  */
382
- export interface PlatformRequestPurchaseProps {
383
- readonly ios?: IosRequestPurchaseProps;
384
- readonly android?: AndroidRequestPurchaseProps;
355
+ export interface RequestPurchasePropsByPlatforms {
356
+ readonly ios?: RequestPurchaseIosProps;
357
+ readonly android?: RequestPurchaseAndroidProps;
385
358
  }
386
359
 
387
360
  /**
388
361
  * Modern platform-specific subscription request structure (v2.7.0+)
389
362
  */
390
- export interface PlatformRequestSubscriptionProps {
391
- readonly ios?: IosRequestPurchaseProps;
392
- readonly android?: AndroidRequestSubscriptionProps;
363
+ export interface RequestSubscriptionPropsByPlatforms {
364
+ readonly ios?: RequestPurchaseIosProps;
365
+ readonly android?: RequestSubscriptionAndroidProps;
393
366
  }
394
367
 
395
368
  /**
396
369
  * Modern request purchase parameters (v2.7.0+)
397
370
  * This is the recommended API moving forward
398
371
  */
399
- export type RequestPurchaseProps = PlatformRequestPurchaseProps;
372
+ export type RequestPurchaseProps = RequestPurchasePropsByPlatforms;
400
373
 
401
374
  /**
402
375
  * Modern request subscription parameters (v2.7.0+)
403
376
  * This is the recommended API moving forward
404
377
  */
405
- export type RequestSubscriptionProps = PlatformRequestSubscriptionProps;
406
-
407
- /**
408
- * Legacy request purchase parameters (deprecated)
409
- * Includes both unified and old platform-specific formats
410
- * @deprecated Use RequestPurchaseProps with platform-specific structure instead
411
- */
412
- export type LegacyRequestPurchasePropsAll =
413
- | UnifiedRequestPurchaseProps
414
- | LegacyRequestPurchaseProps;
415
-
416
- /**
417
- * Legacy request subscription parameters (deprecated)
418
- * Includes both unified and old platform-specific formats
419
- * @deprecated Use RequestSubscriptionProps with platform-specific structure instead
420
- */
421
- export type LegacyRequestSubscriptionPropsAll =
422
- | UnifiedRequestSubscriptionProps
423
- | LegacyRequestSubscriptionProps;
424
-
425
- /**
426
- * All supported request purchase parameters
427
- * Used internally for backward compatibility
428
- * @internal
429
- */
430
- export type RequestPurchasePropsWithLegacy =
431
- | RequestPurchaseProps
432
- | LegacyRequestPurchasePropsAll;
433
-
434
- /**
435
- * All supported request subscription parameters
436
- * Used internally for backward compatibility
437
- * @internal
438
- */
439
- export type RequestSubscriptionPropsWithLegacy =
440
- | RequestSubscriptionProps
441
- | LegacyRequestSubscriptionPropsAll;
442
-
443
- // ============================================================================
444
- // Type Guards and Utility Functions
445
- // ============================================================================
446
-
447
- // Type guards to check which API style is being used
448
- export function isPlatformRequestProps(
449
- props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
450
- ): props is PlatformRequestPurchaseProps | PlatformRequestSubscriptionProps {
451
- return 'ios' in props || 'android' in props;
452
- }
453
-
454
- export function isUnifiedRequestProps(
455
- props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
456
- ): props is UnifiedRequestPurchaseProps | UnifiedRequestSubscriptionProps {
457
- return 'sku' in props || 'skus' in props;
458
- }
459
-
460
- export function isLegacyRequestProps(
461
- props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
462
- ): props is LegacyRequestPurchaseProps | LegacyRequestSubscriptionProps {
463
- return 'productId' in props || 'productIds' in props;
464
- }
378
+ export type RequestSubscriptionProps = RequestSubscriptionPropsByPlatforms;
465
379
 
466
- // Note: Other type guard functions are exported from index.ts to avoid conflicts
380
+ // Note: Type guard functions are exported from index.ts to avoid conflicts
package/src/index.ts CHANGED
@@ -4,8 +4,16 @@ import {Platform} from 'react-native';
4
4
 
5
5
  // Internal modules
6
6
  import ExpoIapModule from './ExpoIapModule';
7
- import {isProductIos, validateReceiptIOS} from './modules/ios';
8
- import {isProductAndroid, validateReceiptAndroid} from './modules/android';
7
+ import {
8
+ isProductIos,
9
+ validateReceiptIOS,
10
+ deepLinkToSubscriptionsIos,
11
+ } from './modules/ios';
12
+ import {
13
+ isProductAndroid,
14
+ validateReceiptAndroid,
15
+ deepLinkToSubscriptionsAndroid,
16
+ } from './modules/android';
9
17
 
10
18
  // Types
11
19
  import {
@@ -14,12 +22,10 @@ import {
14
22
  Purchase,
15
23
  PurchaseError,
16
24
  PurchaseResult,
17
- RequestSubscriptionPropsWithLegacy,
18
- RequestPurchasePropsWithLegacy,
25
+ RequestSubscriptionProps,
26
+ RequestPurchaseProps,
19
27
  SubscriptionProduct,
20
28
  SubscriptionPurchase,
21
- isPlatformRequestProps,
22
- isUnifiedRequestProps,
23
29
  } from './ExpoIap.types';
24
30
  import {ProductPurchaseAndroid} from './types/ExpoIapAndroid.types';
25
31
  import {PaymentDiscount} from './types/ExpoIapIos.types';
@@ -341,14 +347,13 @@ const offerToRecordIos = (
341
347
  };
342
348
 
343
349
  // Define discriminated union with explicit type parameter
344
- // Using legacy types internally for backward compatibility
345
350
  type PurchaseRequest =
346
351
  | {
347
- request: RequestPurchasePropsWithLegacy;
352
+ request: RequestPurchaseProps;
348
353
  type?: 'inapp';
349
354
  }
350
355
  | {
351
- request: RequestSubscriptionPropsWithLegacy;
356
+ request: RequestSubscriptionProps;
352
357
  type: 'subs';
353
358
  };
354
359
 
@@ -356,50 +361,11 @@ type PurchaseRequest =
356
361
  * Helper to normalize request props to platform-specific format
357
362
  */
358
363
  const normalizeRequestProps = (
359
- request: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
364
+ request: RequestPurchaseProps | RequestSubscriptionProps,
360
365
  platform: 'ios' | 'android',
361
366
  ): any => {
362
- // If it's already platform-specific format
363
- if (isPlatformRequestProps(request)) {
364
- return platform === 'ios' ? request.ios : request.android;
365
- }
366
-
367
- // If it's unified format, convert to platform-specific
368
- if (isUnifiedRequestProps(request)) {
369
- if (platform === 'ios') {
370
- return {
371
- sku: request.sku || (request.skus?.[0] ?? ''),
372
- andDangerouslyFinishTransactionAutomaticallyIOS:
373
- request.andDangerouslyFinishTransactionAutomaticallyIOS,
374
- appAccountToken: request.appAccountToken,
375
- quantity: request.quantity,
376
- withOffer: request.withOffer,
377
- };
378
- } else {
379
- const androidRequest: any = {
380
- skus: request.skus || (request.sku ? [request.sku] : []),
381
- obfuscatedAccountIdAndroid: request.obfuscatedAccountIdAndroid,
382
- obfuscatedProfileIdAndroid: request.obfuscatedProfileIdAndroid,
383
- isOfferPersonalized: request.isOfferPersonalized,
384
- };
385
-
386
- // Add subscription-specific fields if present
387
- if ('subscriptionOffers' in request && request.subscriptionOffers) {
388
- androidRequest.subscriptionOffers = request.subscriptionOffers;
389
- }
390
- if ('purchaseTokenAndroid' in request) {
391
- androidRequest.purchaseTokenAndroid = request.purchaseTokenAndroid;
392
- }
393
- if ('replacementModeAndroid' in request) {
394
- androidRequest.replacementModeAndroid = request.replacementModeAndroid;
395
- }
396
-
397
- return androidRequest;
398
- }
399
- }
400
-
401
- // Legacy format handling
402
- return request;
367
+ // Platform-specific format - directly return the appropriate platform data
368
+ return platform === 'ios' ? request.ios : request.android;
403
369
  };
404
370
 
405
371
  /**
@@ -567,7 +533,7 @@ export const requestPurchase = (
567
533
  * ```
568
534
  */
569
535
  export const requestSubscription = async (
570
- request: RequestSubscriptionPropsWithLegacy,
536
+ request: RequestSubscriptionProps,
571
537
  ): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> => {
572
538
  console.warn(
573
539
  "`requestSubscription` is deprecated and will be removed in version 3.0.0. Use `requestPurchase({ request, type: 'subs' })` instead.",
@@ -694,5 +660,55 @@ export const validateReceipt = async (
694
660
  }
695
661
  };
696
662
 
663
+ /**
664
+ * Deeplinks to native interface that allows users to manage their subscriptions
665
+ * @param options.skuAndroid - Required for Android to locate specific subscription (ignored on iOS)
666
+ * @param options.packageNameAndroid - Required for Android to identify your app (ignored on iOS)
667
+ *
668
+ * @returns Promise that resolves when the deep link is successfully opened
669
+ *
670
+ * @throws {Error} When called on unsupported platform or when required Android parameters are missing
671
+ *
672
+ * @example
673
+ * import { deepLinkToSubscriptions } from 'expo-iap';
674
+ *
675
+ * // Works on both iOS and Android
676
+ * await deepLinkToSubscriptions({
677
+ * skuAndroid: 'your_subscription_sku',
678
+ * packageNameAndroid: 'com.example.app'
679
+ * });
680
+ */
681
+ export const deepLinkToSubscriptions = (options: {
682
+ skuAndroid?: string;
683
+ packageNameAndroid?: string;
684
+ }): Promise<void> => {
685
+ if (Platform.OS === 'ios') {
686
+ return deepLinkToSubscriptionsIos();
687
+ }
688
+
689
+ if (Platform.OS === 'android') {
690
+ if (!options.skuAndroid) {
691
+ return Promise.reject(
692
+ new Error(
693
+ 'skuAndroid is required to locate subscription in Android Store',
694
+ ),
695
+ );
696
+ }
697
+ if (!options.packageNameAndroid) {
698
+ return Promise.reject(
699
+ new Error(
700
+ 'packageNameAndroid is required to identify your app in Android Store',
701
+ ),
702
+ );
703
+ }
704
+ return deepLinkToSubscriptionsAndroid({
705
+ sku: options.skuAndroid,
706
+ packageName: options.packageNameAndroid,
707
+ });
708
+ }
709
+
710
+ return Promise.reject(new Error(`Unsupported platform: ${Platform.OS}`));
711
+ };
712
+
697
713
  export * from './useIap';
698
714
  export * from './utils/errorMapping';
@@ -22,16 +22,34 @@ export function isProductAndroid<T extends {platform?: string}>(
22
22
 
23
23
  /**
24
24
  * Deep link to subscriptions screen on Android.
25
- * @param {string} sku The product's SKU (on Android)
25
+ * @param {Object} params - The parameters object
26
+ * @param {string} params.sku - The product's SKU (on Android)
27
+ * @param {string} params.packageName - The package name of your Android app (e.g., 'com.example.app')
26
28
  * @returns {Promise<void>}
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * await deepLinkToSubscriptionsAndroid({
33
+ * sku: 'subscription_id',
34
+ * packageName: 'com.example.app'
35
+ * });
36
+ * ```
27
37
  */
28
38
  export const deepLinkToSubscriptionsAndroid = async ({
29
39
  sku,
40
+ packageName,
30
41
  }: {
31
42
  sku: string;
43
+ packageName: string;
32
44
  }): Promise<void> => {
45
+ if (!packageName) {
46
+ throw new Error(
47
+ 'packageName is required for deepLinkToSubscriptionsAndroid',
48
+ );
49
+ }
50
+
33
51
  return Linking.openURL(
34
- `https://play.google.com/store/account/subscriptions?package=${await ExpoIapModule.getPackageName()}&sku=${sku}`,
52
+ `https://play.google.com/store/account/subscriptions?package=${packageName}&sku=${sku}`,
35
53
  );
36
54
  };
37
55
 
@@ -39,11 +57,12 @@ export const deepLinkToSubscriptionsAndroid = async ({
39
57
  * Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including
40
58
  * your access token in the binary you ship to users is potentially dangerous.
41
59
  * Use server side validation instead for your production builds
42
- * @param {string} packageName package name of your app.
43
- * @param {string} productId product id for your in app product.
44
- * @param {string} productToken token for your purchase (called 'token' in the API documentation).
45
- * @param {string} accessToken OAuth access token with androidpublisher scope. Required for authentication.
46
- * @param {boolean} isSub whether this is subscription or inapp. `true` for subscription.
60
+ * @param {Object} params - The parameters object
61
+ * @param {string} params.packageName - package name of your app.
62
+ * @param {string} params.productId - product id for your in app product.
63
+ * @param {string} params.productToken - token for your purchase (called 'token' in the API documentation).
64
+ * @param {string} params.accessToken - OAuth access token with androidpublisher scope. Required for authentication.
65
+ * @param {boolean} params.isSub - whether this is subscription or inapp. `true` for subscription.
47
66
  * @returns {Promise<ReceiptAndroid>}
48
67
  */
49
68
  export const validateReceiptAndroid = async ({
@@ -85,7 +104,8 @@ export const validateReceiptAndroid = async ({
85
104
 
86
105
  /**
87
106
  * Acknowledge a product (on Android.) No-op on iOS.
88
- * @param {string} token The product's token (on Android)
107
+ * @param {Object} params - The parameters object
108
+ * @param {string} params.token - The product's token (on Android)
89
109
  * @returns {Promise<PurchaseResult | void>}
90
110
  */
91
111
  export const acknowledgePurchaseAndroid = ({
@@ -15,6 +15,7 @@ import type {
15
15
  ProductStatusIos,
16
16
  AppTransactionIOS,
17
17
  } from '../types/ExpoIapIos.types';
18
+ import {Linking} from 'react-native';
18
19
 
19
20
  export type TransactionEvent = {
20
21
  transaction?: ProductPurchase;
@@ -314,6 +315,15 @@ export const buyPromotedProductIOS = (): Promise<void> => {
314
315
  return ExpoIapModule.buyPromotedProduct();
315
316
  };
316
317
 
318
+ /**
319
+ * Deep link to subscriptions screen on iOS.
320
+ * @returns {Promise<void>}
321
+ *
322
+ * @platform iOS
323
+ */
324
+ export const deepLinkToSubscriptionsIos = (): Promise<void> =>
325
+ Linking.openURL('https://apps.apple.com/account/subscriptions');
326
+
317
327
  // ============= DEPRECATED FUNCTIONS =============
318
328
  // These will be removed in version 3.0.0
319
329