react-native-iap 15.5.4 → 15.6.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 (77) hide show
  1. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +163 -15
  2. package/android/src/main/java/com/margelo/nitro/iap/PendingEventBuffer.kt +86 -0
  3. package/android/src/main/java/com/margelo/nitro/iap/RnIapLog.kt +13 -0
  4. package/ios/HybridRnIap.swift +31 -4
  5. package/ios/RnIapHelper.swift +3 -2
  6. package/ios/RnIapLog.swift +14 -0
  7. package/lib/module/hooks/useIAP.js +8 -2
  8. package/lib/module/hooks/useIAP.js.map +1 -1
  9. package/lib/module/index.js +105 -30
  10. package/lib/module/index.js.map +1 -1
  11. package/lib/module/index.kepler.js +72 -5
  12. package/lib/module/index.kepler.js.map +1 -1
  13. package/lib/module/kit-api.js +78 -3
  14. package/lib/module/kit-api.js.map +1 -1
  15. package/lib/module/types.js +29 -25
  16. package/lib/module/types.js.map +1 -1
  17. package/lib/module/utils/deprecation.js +15 -0
  18. package/lib/module/utils/deprecation.js.map +1 -0
  19. package/lib/module/utils/platform-request.js +28 -0
  20. package/lib/module/utils/platform-request.js.map +1 -0
  21. package/lib/module/utils/type-bridge.js +9 -4
  22. package/lib/module/utils/type-bridge.js.map +1 -1
  23. package/lib/module/vega-adapter.js +34 -22
  24. package/lib/module/vega-adapter.js.map +1 -1
  25. package/lib/typescript/src/hooks/useIAP.d.ts +16 -2
  26. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  27. package/lib/typescript/src/index.d.ts +56 -12
  28. package/lib/typescript/src/index.d.ts.map +1 -1
  29. package/lib/typescript/src/index.kepler.d.ts +50 -4
  30. package/lib/typescript/src/index.kepler.d.ts.map +1 -1
  31. package/lib/typescript/src/kit-api.d.ts +18 -8
  32. package/lib/typescript/src/kit-api.d.ts.map +1 -1
  33. package/lib/typescript/src/specs/RnIap.nitro.d.ts +32 -6
  34. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  35. package/lib/typescript/src/types.d.ts +119 -175
  36. package/lib/typescript/src/types.d.ts.map +1 -1
  37. package/lib/typescript/src/utils/deprecation.d.ts +3 -0
  38. package/lib/typescript/src/utils/deprecation.d.ts.map +1 -0
  39. package/lib/typescript/src/utils/platform-request.d.ts +12 -0
  40. package/lib/typescript/src/utils/platform-request.d.ts.map +1 -0
  41. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
  42. package/lib/typescript/src/vega-adapter.d.ts +1 -0
  43. package/lib/typescript/src/vega-adapter.d.ts.map +1 -1
  44. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +16 -0
  45. package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +1 -0
  46. package/nitrogen/generated/android/c++/JNitroPurchase.hpp +5 -1
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +4 -0
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +7 -2
  49. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +8 -0
  50. package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +1 -0
  51. package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +19 -0
  52. package/nitrogen/generated/ios/swift/NitroPurchase.swift +39 -2
  53. package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +1 -0
  54. package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +1 -0
  55. package/nitrogen/generated/shared/c++/NitroPurchase.hpp +5 -1
  56. package/openiap-versions.json +3 -3
  57. package/package.json +1 -1
  58. package/src/hooks/useIAP.ts +22 -2
  59. package/src/index.kepler.ts +86 -8
  60. package/src/index.ts +139 -47
  61. package/src/kit-api.ts +136 -13
  62. package/src/specs/RnIap.nitro.ts +33 -6
  63. package/src/types.ts +130 -203
  64. package/src/utils/deprecation.ts +16 -0
  65. package/src/utils/platform-request.ts +37 -0
  66. package/src/utils/type-bridge.ts +20 -4
  67. package/src/vega-adapter.ts +50 -24
  68. package/lib/module/hooks/useWebhookEvents.js +0 -113
  69. package/lib/module/hooks/useWebhookEvents.js.map +0 -1
  70. package/lib/module/webhook-client.js +0 -164
  71. package/lib/module/webhook-client.js.map +0 -1
  72. package/lib/typescript/src/hooks/useWebhookEvents.d.ts +0 -55
  73. package/lib/typescript/src/hooks/useWebhookEvents.d.ts.map +0 -1
  74. package/lib/typescript/src/webhook-client.d.ts +0 -82
  75. package/lib/typescript/src/webhook-client.d.ts.map +0 -1
  76. package/src/hooks/useWebhookEvents.ts +0 -180
  77. package/src/webhook-client.ts +0 -312
@@ -30,6 +30,7 @@ import {
30
30
  launchExternalLinkAndroid,
31
31
  showBillingProgramInformationDialogAndroid,
32
32
  showInAppMessagesAndroid,
33
+ openRedeemOfferCodeAndroid,
33
34
  userChoiceBillingListenerAndroid,
34
35
  developerProvidedBillingListenerAndroid,
35
36
  subscriptionBillingIssueListener,
@@ -194,7 +195,8 @@ type UseIap = {
194
195
  */
195
196
  requestPurchase: (params: RequestPurchaseProps) => Promise<void>;
196
197
  /**
197
- * @deprecated Use `verifyPurchase` instead. This function will be removed in a future version.
198
+ * @deprecated Use `verifyPurchase` instead. This function will be removed in
199
+ * react-native-iap 16.0.0.
198
200
  *
199
201
  * @see {@link https://openiap.dev/docs/apis/validate-receipt}
200
202
  */
@@ -233,6 +235,9 @@ type UseIap = {
233
235
  * Buy the currently promoted product.
234
236
  *
235
237
  * @see {@link https://openiap.dev/docs/apis/ios/request-purchase-on-promoted-product-ios}
238
+ * @deprecated Use the `onPromotedProductIOS` hook callback followed by
239
+ * `requestPurchase` instead. Scheduled for removal in react-native-iap
240
+ * 16.0.0.
236
241
  */
237
242
  requestPurchaseOnPromotedProductIOS: () => Promise<boolean>;
238
243
  /**
@@ -260,18 +265,25 @@ type UseIap = {
260
265
  * Check whether alternative billing is available for the user.
261
266
  *
262
267
  * @see {@link https://openiap.dev/docs/apis/android/check-alternative-billing-availability-android}
268
+ * @deprecated Use `isBillingProgramAvailableAndroid('external-offer')`
269
+ * instead. Scheduled for removal in react-native-iap 16.0.0.
263
270
  */
264
271
  checkAlternativeBillingAvailabilityAndroid?: () => Promise<boolean>;
265
272
  /**
266
273
  * Display Google's alternative billing information dialog.
267
274
  *
268
275
  * @see {@link https://openiap.dev/docs/apis/android/show-alternative-billing-dialog-android}
276
+ * @deprecated Use `launchExternalLinkAndroid` instead. Scheduled for removal in
277
+ * react-native-iap 16.0.0.
269
278
  */
270
279
  showAlternativeBillingDialogAndroid?: () => Promise<boolean>;
271
280
  /**
272
281
  * Create a reporting token for an alternative billing flow.
273
282
  *
274
283
  * @see {@link https://openiap.dev/docs/apis/android/create-alternative-billing-token-android}
284
+ * @deprecated Use
285
+ * `createBillingProgramReportingDetailsAndroid('external-offer')` instead.
286
+ * Scheduled for removal in react-native-iap 16.0.0.
275
287
  */
276
288
  createAlternativeBillingTokenAndroid?: (
277
289
  sku?: string,
@@ -288,6 +300,8 @@ type UseIap = {
288
300
  showBillingProgramInformationDialogAndroid?: MutationField<'showBillingProgramInformationDialogAndroid'>;
289
301
  /** Show Play billing in-app messages. */
290
302
  showInAppMessagesAndroid?: MutationField<'showInAppMessagesAndroid'>;
303
+ /** Open the Play Store offer code redemption page; purchases arrive via the standard purchase listeners. */
304
+ openRedeemOfferCodeAndroid?: MutationField<'openRedeemOfferCodeAndroid'>;
291
305
  };
292
306
 
293
307
  export interface UseIapOptions {
@@ -321,7 +335,8 @@ export interface UseIapOptions {
321
335
  */
322
336
  onSubscriptionBillingIssue?: (purchase: Purchase) => void;
323
337
  /**
324
- * @deprecated Use enableBillingProgramAndroid instead.
338
+ * @deprecated Use enableBillingProgramAndroid instead. This option will be
339
+ * removed in react-native-iap 16.0.0.
325
340
  * - 'user-choice' → 'user-choice-billing'
326
341
  * - 'alternative-only' → 'external-offer'
327
342
  */
@@ -743,11 +758,15 @@ export function useIAP(options?: UseIapOptions): UseIap {
743
758
  }
744
759
 
745
760
  if (!result) {
761
+ cleanupListeners();
746
762
  setConnected(false);
747
763
  RnIapConsole.warn('[useIAP] initConnection returned false');
748
764
  return;
749
765
  }
750
766
 
767
+ // Android retains events emitted during connection setup in bounded
768
+ // native queues; registration flushes that backlog after Nitro is ready.
769
+ // Other platforms preserve the existing post-init listener ordering.
751
770
  registerListeners();
752
771
  setConnected(true);
753
772
  } catch (error) {
@@ -833,6 +852,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
833
852
  launchExternalLinkAndroid,
834
853
  showBillingProgramInformationDialogAndroid,
835
854
  showInAppMessagesAndroid,
855
+ openRedeemOfferCodeAndroid,
836
856
  }
837
857
  : {}),
838
858
  };
@@ -3,6 +3,7 @@ import type {
3
3
  MutationField,
4
4
  Product,
5
5
  ProductQueryType,
6
+ ProductRequest,
6
7
  ProductSubscription,
7
8
  Purchase,
8
9
  PurchaseError,
@@ -25,16 +26,19 @@ import {
25
26
  validateNitroProduct,
26
27
  validateNitroPurchase,
27
28
  } from './utils/type-bridge';
29
+ import {warnLegacyOnce} from './utils/deprecation';
30
+ import {selectCanonicalPlatformRequest} from './utils/platform-request';
28
31
 
29
32
  export * from './types';
30
33
  export * from './utils/error';
31
34
  export * from './vega';
32
35
  export {useIAP, type UseIapOptions} from './hooks/useIAP';
33
- export {connectWebhookStream, parseWebhookEventData} from './webhook-client';
34
36
  export {kitApi, KitApiError} from './kit-api';
35
37
  export type {
36
38
  EntitlementsResponse,
37
39
  KitApiOptions,
40
+ KitClientPayloadCache,
41
+ KitClientPayloadOptions,
38
42
  KitClientPayloadResponse,
39
43
  KitProduct,
40
44
  KitProductClientPayload,
@@ -46,8 +50,17 @@ export type {
46
50
  StatusResponse,
47
51
  } from './kit-api';
48
52
 
53
+ /**
54
+ * Product type accepted by the Amazon Vega entry point.
55
+ *
56
+ * Compatibility note: the `'inapp'` member is deprecated. Use `'in-app'`
57
+ * instead; the alias will be removed in react-native-iap 16.0.0.
58
+ */
49
59
  export type ProductTypeInput = 'inapp' | 'in-app' | 'subs';
50
60
 
61
+ const LEGACY_INAPP_WARNING =
62
+ "[react-native-iap] `type: 'inapp'` is deprecated and will be removed in react-native-iap 16.0.0. Use 'in-app' instead.";
63
+
51
64
  export interface EventSubscription {
52
65
  remove(): void;
53
66
  }
@@ -80,7 +93,10 @@ const normalizeProductQueryType = (
80
93
  ): ProductTypeInput | 'all' => {
81
94
  if (type === 'subs') return 'subs';
82
95
  if (type === 'all') return 'all';
83
- return 'inapp';
96
+ if (type === 'inapp') {
97
+ warnLegacyOnce('product-type.inapp', LEGACY_INAPP_WARNING);
98
+ }
99
+ return 'in-app';
84
100
  };
85
101
 
86
102
  const mapProducts = (
@@ -113,13 +129,16 @@ export const endConnection: MutationField<'endConnection'> = async () => {
113
129
  return getModule().endConnection();
114
130
  };
115
131
 
116
- export const fetchProducts: QueryField<'fetchProducts'> = async (request) => {
132
+ export const fetchProducts = async (
133
+ request: Omit<ProductRequest, 'type'> & {
134
+ type?: ProductQueryType | ProductTypeInput | null;
135
+ },
136
+ ): ReturnType<QueryField<'fetchProducts'>> => {
117
137
  const {skus, type} = request;
118
138
  const normalizedType = normalizeProductQueryType(type);
119
139
  const nitroProducts = await getModule().fetchProducts(skus, normalizedType);
120
140
  return mapProducts(nitroProducts, normalizedType) as
121
- | Product[]
122
- | ProductSubscription[];
141
+ Product[] | ProductSubscription[];
123
142
  };
124
143
 
125
144
  export const requestPurchase: MutationField<'requestPurchase'> = async (
@@ -129,8 +148,19 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
129
148
  | RequestPurchasePropsByPlatforms
130
149
  | RequestSubscriptionPropsByPlatforms
131
150
  | undefined;
132
- const androidRequest =
133
- perPlatformRequest?.google ?? perPlatformRequest?.android;
151
+ const selection = perPlatformRequest
152
+ ? selectCanonicalPlatformRequest<
153
+ | RequestPurchasePropsByPlatforms['google']
154
+ | RequestSubscriptionPropsByPlatforms['google']
155
+ >(perPlatformRequest, 'google', 'android')
156
+ : {usesLegacyKey: false, value: undefined};
157
+ if (selection.usesLegacyKey && selection.value != null) {
158
+ warnLegacyOnce(
159
+ 'request-purchase.android',
160
+ '[react-native-iap] `request.android` is deprecated and will be removed in react-native-iap 16.0.0. Use `request.google` instead.',
161
+ );
162
+ }
163
+ const androidRequest = selection.value;
134
164
 
135
165
  if (!androidRequest?.skus?.length) {
136
166
  throw new Error(
@@ -140,7 +170,6 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
140
170
 
141
171
  const nitroRequest: NitroPurchaseRequest = {
142
172
  google: androidRequest,
143
- android: androidRequest,
144
173
  };
145
174
  const result = await getModule().requestPurchase(nitroRequest);
146
175
  if (!Array.isArray(result)) return null;
@@ -247,11 +276,20 @@ export const verifyPurchaseWithProvider: MutationField<
247
276
  >;
248
277
  };
249
278
 
279
+ /**
280
+ * @deprecated Use `verifyPurchase` instead. This compatibility operation will
281
+ * be removed in react-native-iap 16.0.0.
282
+ */
250
283
  export const validateReceipt: MutationField<'validateReceipt'> = async () => {
251
284
  return unsupported('validateReceipt');
252
285
  };
253
286
 
254
287
  export const verifyPurchase: MutationField<'verifyPurchase'> = validateReceipt;
288
+
289
+ /**
290
+ * @deprecated Use `verifyPurchase` instead. This iOS compatibility operation
291
+ * will be removed in react-native-iap 16.0.0.
292
+ */
255
293
  export const validateReceiptIOS: QueryField<'validateReceiptIOS'> = async () =>
256
294
  unsupported('validateReceiptIOS');
257
295
  export const syncIOS: MutationField<'syncIOS'> = async () =>
@@ -262,7 +300,18 @@ export const getAppTransactionIOS: QueryField<
262
300
  export const getPromotedProductIOS: QueryField<
263
301
  'getPromotedProductIOS'
264
302
  > = async () => null;
303
+
304
+ /**
305
+ * @deprecated Use `getPromotedProductIOS` instead. This compatibility alias
306
+ * will be removed in react-native-iap 16.0.0.
307
+ */
265
308
  export const requestPromotedProductIOS = getPromotedProductIOS;
309
+
310
+ /**
311
+ * @deprecated Use `promotedProductListenerIOS` followed by `requestPurchase`
312
+ * instead. This compatibility operation will be removed in react-native-iap
313
+ * 16.0.0.
314
+ */
266
315
  export const requestPurchaseOnPromotedProductIOS = async (): Promise<void> =>
267
316
  unsupported('requestPurchaseOnPromotedProductIOS');
268
317
  export const showManageSubscriptionsIOS: MutationField<
@@ -291,15 +340,44 @@ export const consumePurchaseAndroid: MutationField<
291
340
  > = async (purchaseToken) => {
292
341
  return getVegaModule().consumePurchaseAndroid(purchaseToken);
293
342
  };
343
+
344
+ /**
345
+ * @deprecated Use `acknowledgePurchaseAndroid` instead. This alias will be
346
+ * removed in react-native-iap 16.0.0.
347
+ */
294
348
  export const acknowledgePurchase = acknowledgePurchaseAndroid;
349
+
350
+ /**
351
+ * @deprecated Use `consumePurchaseAndroid` instead. This alias will be removed
352
+ * in react-native-iap 16.0.0.
353
+ */
295
354
  export const consumePurchase = consumePurchaseAndroid;
296
355
 
356
+ export const openRedeemOfferCodeAndroid: MutationField<
357
+ 'openRedeemOfferCodeAndroid'
358
+ > = async () => false;
359
+
360
+ /**
361
+ * @deprecated Use `isBillingProgramAvailableAndroid('external-offer')`
362
+ * instead. Scheduled for removal in react-native-iap 16.0.0.
363
+ */
297
364
  export const checkAlternativeBillingAvailabilityAndroid: MutationField<
298
365
  'checkAlternativeBillingAvailabilityAndroid'
299
366
  > = async () => unsupported('checkAlternativeBillingAvailabilityAndroid');
367
+
368
+ /**
369
+ * @deprecated Use `launchExternalLinkAndroid` instead. Scheduled for removal
370
+ * in react-native-iap 16.0.0.
371
+ */
300
372
  export const showAlternativeBillingDialogAndroid: MutationField<
301
373
  'showAlternativeBillingDialogAndroid'
302
374
  > = async () => unsupported('showAlternativeBillingDialogAndroid');
375
+
376
+ /**
377
+ * @deprecated Use `createBillingProgramReportingDetailsAndroid` with the
378
+ * external-offer program instead. Scheduled for removal in react-native-iap
379
+ * 16.0.0.
380
+ */
303
381
  export const createAlternativeBillingTokenAndroid: MutationField<
304
382
  'createAlternativeBillingTokenAndroid'
305
383
  > = async () => unsupported('createAlternativeBillingTokenAndroid');
package/src/index.ts CHANGED
@@ -57,6 +57,8 @@ import {
57
57
  DUPLICATE_PURCHASE_CODE,
58
58
  } from './utils/errorMapping';
59
59
  import {RnIapConsole} from './utils/debug';
60
+ import {warnLegacyOnce} from './utils/deprecation';
61
+ import {selectCanonicalPlatformRequest} from './utils/platform-request';
60
62
  import {getSuccessFromPurchaseVariant} from './utils/purchase';
61
63
  import {parseAppTransactionPayload} from './utils';
62
64
  import {getVegaIapModule, isVegaOS} from './vega';
@@ -91,10 +93,16 @@ export * from './types';
91
93
  export * from './utils/error';
92
94
  export * from './vega';
93
95
 
96
+ /**
97
+ * Product type accepted by public query and purchase helpers.
98
+ *
99
+ * Compatibility note: the `'inapp'` member is deprecated. Use `'in-app'`
100
+ * instead; the alias will be removed in react-native-iap 16.0.0.
101
+ */
94
102
  export type ProductTypeInput = 'inapp' | 'in-app' | 'subs';
95
103
 
96
104
  const LEGACY_INAPP_WARNING =
97
- "[react-native-iap] `type: 'inapp'` is deprecated and will be removed in a future major version. Use 'in-app' instead.";
105
+ "[react-native-iap] `type: 'inapp'` is deprecated and will be removed in react-native-iap 16.0.0. Use 'in-app' instead.";
98
106
 
99
107
  type NitroPurchaseRequest = Parameters<RnIap['requestPurchase']>[0];
100
108
  type NitroAvailablePurchasesOptions = NonNullable<
@@ -148,23 +156,11 @@ export interface EventSubscription {
148
156
 
149
157
  // Export hooks
150
158
  export {useIAP} from './hooks/useIAP';
151
- export {useWebhookEvents} from './hooks/useWebhookEvents';
152
- export type {
153
- UseWebhookEventsOptions,
154
- UseWebhookEventsResult,
155
- } from './hooks/useWebhookEvents';
156
- export {connectWebhookStream, parseWebhookEventData} from './webhook-client';
157
- export type {
158
- WebhookEventPayload,
159
- WebhookEventStream,
160
- WebhookEventType as WebhookEventTypeName,
161
- WebhookListener,
162
- WebhookListenerError,
163
- WebhookListenerOptions,
164
- } from './webhook-client';
165
159
  export {kitApi, KitApiError} from './kit-api';
166
160
  export type {
167
161
  KitApiOptions,
162
+ KitClientPayloadCache,
163
+ KitClientPayloadOptions,
168
164
  KitClientPayloadResponse,
169
165
  KitProduct,
170
166
  KitProductClientPayload,
@@ -512,6 +508,14 @@ export const purchaseErrorListener = (
512
508
  return {
513
509
  remove: () => {
514
510
  purchaseErrorJsListeners.delete(listener);
511
+ if (purchaseErrorJsListeners.size === 0 && purchaseErrorNativeAttached) {
512
+ try {
513
+ IAP.instance.removePurchaseErrorListener(purchaseErrorNativeHandler);
514
+ purchaseErrorNativeAttached = false;
515
+ } catch (e) {
516
+ RnIapConsole.warn('[purchaseErrorListener] native remove failed:', e);
517
+ }
518
+ }
515
519
  },
516
520
  };
517
521
  };
@@ -871,8 +875,7 @@ export const fetchProducts: QueryField<'fetchProducts'> = async (request) => {
871
875
 
872
876
  if (normalizedType === 'all') {
873
877
  const converted = (await fetchAndConvert('all')) as (
874
- | Product
875
- | ProductSubscription
878
+ Product | ProductSubscription
876
879
  )[];
877
880
 
878
881
  RnIapConsole.debug(
@@ -1104,6 +1107,10 @@ export const getPromotedProductIOS: QueryField<
1104
1107
  }
1105
1108
  };
1106
1109
 
1110
+ /**
1111
+ * @deprecated Use `getPromotedProductIOS` instead. This compatibility alias
1112
+ * will be removed in react-native-iap 16.0.0.
1113
+ */
1107
1114
  export const requestPromotedProductIOS = getPromotedProductIOS;
1108
1115
 
1109
1116
  /**
@@ -1118,6 +1125,8 @@ export const requestPromotedProductIOS = getPromotedProductIOS;
1118
1125
  * ```
1119
1126
  *
1120
1127
  * @see {@link https://openiap.dev/docs/apis/ios/get-storefront-ios}
1128
+ * @deprecated Use `getStorefront` instead. Scheduled for removal in
1129
+ * react-native-iap 16.0.0.
1121
1130
  */
1122
1131
  export const getStorefrontIOS: QueryField<'getStorefrontIOS'> = async () => {
1123
1132
  if (Platform.OS !== 'ios') {
@@ -1521,13 +1530,21 @@ export const getReceiptDataIOS: QueryField<'getReceiptDataIOS'> = async () => {
1521
1530
  }
1522
1531
  };
1523
1532
 
1533
+ /**
1534
+ * Legacy alias for `getReceiptDataIOS`.
1535
+ *
1536
+ * @deprecated Use `getReceiptDataIOS` for the cumulative App Store receipt or
1537
+ * `getTransactionJwsIOS(productId)` for one transaction. This alias will be
1538
+ * removed in react-native-iap 16.0.0.
1539
+ */
1524
1540
  export const getReceiptIOS = async (): Promise<string> => {
1525
1541
  if (Platform.OS !== 'ios') {
1526
1542
  throw new Error('getReceiptIOS is only available on iOS');
1527
1543
  }
1528
1544
 
1529
1545
  RnIapConsole.warn(
1530
- '[getReceiptIOS] ⚠️ iOS receipts contain ALL transactions, not just the latest one. ' +
1546
+ '[getReceiptIOS] is deprecated and will be removed in react-native-iap 16.0.0. ' +
1547
+ 'Use getReceiptDataIOS() for the cumulative receipt. iOS receipts contain ALL transactions, not just the latest one. ' +
1531
1548
  'For individual purchase validation, use getTransactionJwsIOS(productId) instead. ' +
1532
1549
  'See: https://react-native-iap.hyo.dev/docs/guides/receipt-validation',
1533
1550
  );
@@ -1791,19 +1808,22 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
1791
1808
  throw new Error('Missing purchase request configuration');
1792
1809
  }
1793
1810
 
1811
+ const iosRequestSource =
1812
+ Platform.OS === 'ios'
1813
+ ? selectApplePurchaseRequest(perPlatformRequest)
1814
+ : undefined;
1815
+ const androidRequestSource = isAndroidStoreRuntime()
1816
+ ? selectGooglePurchaseRequest(perPlatformRequest)
1817
+ : undefined;
1818
+
1794
1819
  if (Platform.OS === 'ios') {
1795
- // Support both 'apple' (recommended) and 'ios' (deprecated) fields
1796
- const iosRequest = perPlatformRequest.apple ?? perPlatformRequest.ios;
1797
- if (!iosRequest?.sku) {
1820
+ if (!iosRequestSource?.sku) {
1798
1821
  throw new Error(
1799
1822
  'Invalid request for iOS. The `sku` property is required.',
1800
1823
  );
1801
1824
  }
1802
1825
  } else if (isAndroidStoreRuntime()) {
1803
- // Support both 'google' (recommended) and 'android' (deprecated) fields
1804
- const androidRequest =
1805
- perPlatformRequest.google ?? perPlatformRequest.android;
1806
- if (!androidRequest?.skus?.length) {
1826
+ if (!androidRequestSource?.skus?.length) {
1807
1827
  throw new Error(
1808
1828
  'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
1809
1829
  );
@@ -1814,14 +1834,12 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
1814
1834
 
1815
1835
  const unifiedRequest: NitroPurchaseRequest = {};
1816
1836
 
1817
- // Support both 'apple' (recommended) and 'ios' (deprecated) fields
1818
- const iosRequestSource = perPlatformRequest.apple ?? perPlatformRequest.ios;
1819
1837
  if (Platform.OS === 'ios' && iosRequestSource) {
1820
1838
  const iosRequest = isSubs
1821
1839
  ? (iosRequestSource as RequestSubscriptionIosProps)
1822
1840
  : (iosRequestSource as RequestPurchaseIosProps);
1823
1841
 
1824
- const iosPayload: NonNullable<NitroPurchaseRequest['ios']> = {
1842
+ const iosPayload: NonNullable<NitroPurchaseRequest['apple']> = {
1825
1843
  sku: iosRequest.sku,
1826
1844
  };
1827
1845
 
@@ -1861,18 +1879,15 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
1861
1879
  }
1862
1880
  }
1863
1881
 
1864
- unifiedRequest.ios = iosPayload;
1882
+ unifiedRequest.apple = iosPayload;
1865
1883
  }
1866
1884
 
1867
- // Support both 'google' (recommended) and 'android' (deprecated) fields
1868
- const androidRequestSource =
1869
- perPlatformRequest.google ?? perPlatformRequest.android;
1870
1885
  if (isAndroidStoreRuntime() && androidRequestSource) {
1871
1886
  const androidRequest = isSubs
1872
1887
  ? (androidRequestSource as RequestSubscriptionAndroidProps)
1873
1888
  : (androidRequestSource as RequestPurchaseAndroidProps);
1874
1889
 
1875
- const androidPayload: NonNullable<NitroPurchaseRequest['android']> = {
1890
+ const androidPayload: NonNullable<NitroPurchaseRequest['google']> = {
1876
1891
  skus: androidRequest.skus,
1877
1892
  };
1878
1893
 
@@ -1926,7 +1941,7 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
1926
1941
  }));
1927
1942
  }
1928
1943
 
1929
- unifiedRequest.android = androidPayload;
1944
+ unifiedRequest.google = androidPayload;
1930
1945
  }
1931
1946
 
1932
1947
  return await IAP.instance.requestPurchase(unifiedRequest);
@@ -2112,13 +2127,40 @@ export const consumePurchaseAndroid: MutationField<
2112
2127
  }
2113
2128
  };
2114
2129
 
2130
+ /**
2131
+ * Open the Google Play offer/promo code redemption page (Android only).
2132
+ * On Google Play builds, launches the Play Store redeem page so the user can
2133
+ * enter a code. Returns false on store flavors without an equivalent flow.
2134
+ * Does not require the billing client to be initialized. Reconcile purchases
2135
+ * when the app resumes because listener delivery depends on connection state.
2136
+ *
2137
+ * @returns Promise<boolean> - true when the redemption page was launched
2138
+ * @platform Android
2139
+ *
2140
+ * @example
2141
+ * ```typescript
2142
+ * await openRedeemOfferCodeAndroid();
2143
+ * // Reconcile with getAvailablePurchases() when the app resumes.
2144
+ * ```
2145
+ *
2146
+ * @see {@link https://openiap.dev/docs/apis/android/open-redeem-offer-code-android}
2147
+ */
2148
+ export const openRedeemOfferCodeAndroid: MutationField<
2149
+ 'openRedeemOfferCodeAndroid'
2150
+ > = async () => {
2151
+ if (Platform.OS !== 'android') {
2152
+ throw new Error('openRedeemOfferCodeAndroid is only supported on Android');
2153
+ }
2154
+ return IAP.instance.openRedeemOfferCodeAndroid();
2155
+ };
2156
+
2115
2157
  // ============================================================================
2116
2158
  // iOS-SPECIFIC FUNCTIONS
2117
2159
  // ============================================================================
2118
2160
 
2119
2161
  /**
2120
2162
  * Validate receipt on both iOS and Android platforms
2121
- * @deprecated Use `verifyPurchase` instead. This function will be removed in a future version.
2163
+ * @deprecated Use `verifyPurchase` instead. This function will be removed in react-native-iap 16.0.0.
2122
2164
  * @param options - Platform-specific verification options
2123
2165
  * @param options.apple - Apple App Store verification options (iOS)
2124
2166
  * @param options.google - Google Play verification options (Android)
@@ -2280,9 +2322,8 @@ export const verifyPurchase: MutationField<'verifyPurchase'> = validateReceipt;
2280
2322
  /**
2281
2323
  * iOS-only receipt validation alias.
2282
2324
  *
2283
- * @deprecated Use `verifyPurchase` (or `validateReceipt`) instead. Kept so
2284
- * consumers who imported `validateReceiptIOS` which is still declared on the
2285
- * OpenIAP Query interface — keep working. Throws on non-iOS platforms.
2325
+ * @deprecated Use `verifyPurchase` instead. This compatibility alias will be
2326
+ * removed in react-native-iap 16.0.0. Throws on non-iOS platforms.
2286
2327
  *
2287
2328
  * @see {@link https://openiap.dev/docs/apis/ios/validate-receipt-ios}
2288
2329
  */
@@ -2430,7 +2471,9 @@ export const presentCodeRedemptionSheetIOS: MutationField<
2430
2471
 
2431
2472
  /**
2432
2473
  * Buy promoted product on iOS
2433
- * @deprecated In StoreKit 2, promoted products can be purchased directly via the standard `requestPurchase()` flow.
2474
+ * @deprecated In StoreKit 2, promoted products can be purchased directly via
2475
+ * the standard `requestPurchase()` flow. This alias will be removed in
2476
+ * react-native-iap 16.0.0.
2434
2477
  * Use `promotedProductListenerIOS` to receive the product ID when a user taps a promoted product,
2435
2478
  * then call `requestPurchase()` with the received SKU directly.
2436
2479
  *
@@ -2638,6 +2681,7 @@ export const getActiveSubscriptions: QueryField<
2638
2681
  willAutoRenew: sub.renewalInfoIOS.willAutoRenew ?? false,
2639
2682
  autoRenewPreference:
2640
2683
  sub.renewalInfoIOS.autoRenewPreference ?? null,
2684
+ commitmentInfo: sub.renewalInfoIOS.commitmentInfo ?? null,
2641
2685
  pendingUpgradeProductId:
2642
2686
  sub.renewalInfoIOS.pendingUpgradeProductId ?? null,
2643
2687
  renewalDate: sub.renewalInfoIOS.renewalDate ?? null,
@@ -2647,8 +2691,11 @@ export const getActiveSubscriptions: QueryField<
2647
2691
  sub.renewalInfoIOS.gracePeriodExpirationDate ?? null,
2648
2692
  priceIncreaseStatus:
2649
2693
  sub.renewalInfoIOS.priceIncreaseStatus ?? null,
2694
+ renewalBillingPlanType:
2695
+ sub.renewalInfoIOS.renewalBillingPlanType ?? null,
2650
2696
  renewalOfferType: sub.renewalInfoIOS.renewalOfferType ?? null,
2651
2697
  renewalOfferId: sub.renewalInfoIOS.renewalOfferId ?? null,
2698
+ jsonRepresentation: sub.renewalInfoIOS.jsonRepresentation ?? null,
2652
2699
  }
2653
2700
  : null,
2654
2701
  // Android specific fields
@@ -2712,12 +2759,14 @@ export {
2712
2759
 
2713
2760
  // Deprecated exports for backward compatibility
2714
2761
  /**
2715
- * @deprecated Use acknowledgePurchaseAndroid instead
2762
+ * @deprecated Use acknowledgePurchaseAndroid instead. This alias will be
2763
+ * removed in react-native-iap 16.0.0.
2716
2764
  */
2717
2765
  export const acknowledgePurchase = acknowledgePurchaseAndroid;
2718
2766
 
2719
2767
  /**
2720
- * @deprecated Use consumePurchaseAndroid instead
2768
+ * @deprecated Use consumePurchaseAndroid instead. This alias will be removed
2769
+ * in react-native-iap 16.0.0.
2721
2770
  */
2722
2771
  export const consumePurchase = consumePurchaseAndroid;
2723
2772
 
@@ -2726,9 +2775,45 @@ export const consumePurchase = consumePurchaseAndroid;
2726
2775
  // ============================================================================
2727
2776
 
2728
2777
  type NitroDiscountOfferRecord = NonNullable<
2729
- NonNullable<NitroPurchaseRequest['ios']>['withOffer']
2778
+ NonNullable<NitroPurchaseRequest['apple']>['withOffer']
2730
2779
  >;
2731
2780
 
2781
+ const selectApplePurchaseRequest = (
2782
+ request:
2783
+ RequestPurchasePropsByPlatforms | RequestSubscriptionPropsByPlatforms,
2784
+ ): RequestPurchaseIosProps | RequestSubscriptionIosProps | null | undefined => {
2785
+ const selection = selectCanonicalPlatformRequest<
2786
+ RequestPurchaseIosProps | RequestSubscriptionIosProps
2787
+ >(request, 'apple', 'ios');
2788
+ if (selection.usesLegacyKey && selection.value != null) {
2789
+ warnLegacyOnce(
2790
+ 'request-purchase.ios',
2791
+ '[react-native-iap] `request.ios` is deprecated and will be removed in react-native-iap 16.0.0. Use `request.apple` instead.',
2792
+ );
2793
+ }
2794
+ return selection.value;
2795
+ };
2796
+
2797
+ const selectGooglePurchaseRequest = (
2798
+ request:
2799
+ RequestPurchasePropsByPlatforms | RequestSubscriptionPropsByPlatforms,
2800
+ ):
2801
+ | RequestPurchaseAndroidProps
2802
+ | RequestSubscriptionAndroidProps
2803
+ | null
2804
+ | undefined => {
2805
+ const selection = selectCanonicalPlatformRequest<
2806
+ RequestPurchaseAndroidProps | RequestSubscriptionAndroidProps
2807
+ >(request, 'google', 'android');
2808
+ if (selection.usesLegacyKey && selection.value != null) {
2809
+ warnLegacyOnce(
2810
+ 'request-purchase.android',
2811
+ '[react-native-iap] `request.android` is deprecated and will be removed in react-native-iap 16.0.0. Use `request.google` instead.',
2812
+ );
2813
+ }
2814
+ return selection.value;
2815
+ };
2816
+
2732
2817
  const toDiscountOfferRecordIOS = (
2733
2818
  offer: DiscountOfferInputIOS | null | undefined,
2734
2819
  ): NitroDiscountOfferRecord | undefined => {
@@ -2746,7 +2831,7 @@ const toDiscountOfferRecordIOS = (
2746
2831
 
2747
2832
  const toNitroProductType = (
2748
2833
  type?: ProductTypeInput | ProductQueryType | null,
2749
- ): 'inapp' | 'subs' | 'all' => {
2834
+ ): 'in-app' | 'subs' | 'all' => {
2750
2835
  if (type === 'subs') {
2751
2836
  return 'subs';
2752
2837
  }
@@ -2754,10 +2839,10 @@ const toNitroProductType = (
2754
2839
  return 'all';
2755
2840
  }
2756
2841
  if (type === 'inapp') {
2757
- RnIapConsole.warn(LEGACY_INAPP_WARNING);
2758
- return 'inapp';
2842
+ warnLegacyOnce('product-type.inapp', LEGACY_INAPP_WARNING);
2843
+ return 'in-app';
2759
2844
  }
2760
- return 'inapp';
2845
+ return 'in-app';
2761
2846
  };
2762
2847
 
2763
2848
  const isSubscriptionQuery = (type?: ProductQueryType | null): boolean =>
@@ -2780,7 +2865,7 @@ const normalizeProductQueryType = (
2780
2865
  return 'subs';
2781
2866
  }
2782
2867
  if (normalized === 'inapp') {
2783
- RnIapConsole.warn(LEGACY_INAPP_WARNING);
2868
+ warnLegacyOnce('product-type.inapp', LEGACY_INAPP_WARNING);
2784
2869
  return 'in-app';
2785
2870
  }
2786
2871
  if (normalized === 'in-app') {
@@ -2815,6 +2900,8 @@ const normalizeProductQueryType = (
2815
2900
  * ```
2816
2901
  *
2817
2902
  * @see {@link https://openiap.dev/docs/apis/android/check-alternative-billing-availability-android}
2903
+ * @deprecated Use `isBillingProgramAvailableAndroid('external-offer')`
2904
+ * instead. Scheduled for removal in react-native-iap 16.0.0.
2818
2905
  */
2819
2906
  export const checkAlternativeBillingAvailabilityAndroid: MutationField<
2820
2907
  'checkAlternativeBillingAvailabilityAndroid'
@@ -2857,6 +2944,8 @@ export const checkAlternativeBillingAvailabilityAndroid: MutationField<
2857
2944
  * ```
2858
2945
  *
2859
2946
  * @see {@link https://openiap.dev/docs/apis/android/show-alternative-billing-dialog-android}
2947
+ * @deprecated Use `launchExternalLinkAndroid` instead. Scheduled for removal in
2948
+ * react-native-iap 16.0.0.
2860
2949
  */
2861
2950
  export const showAlternativeBillingDialogAndroid: MutationField<
2862
2951
  'showAlternativeBillingDialogAndroid'
@@ -2896,6 +2985,9 @@ export const showAlternativeBillingDialogAndroid: MutationField<
2896
2985
  * ```
2897
2986
  *
2898
2987
  * @see {@link https://openiap.dev/docs/apis/android/create-alternative-billing-token-android}
2988
+ * @deprecated Use
2989
+ * `createBillingProgramReportingDetailsAndroid('external-offer')` instead.
2990
+ * Scheduled for removal in react-native-iap 16.0.0.
2899
2991
  */
2900
2992
  export const createAlternativeBillingTokenAndroid: MutationField<
2901
2993
  'createAlternativeBillingTokenAndroid'