react-native-iap 14.0.1 → 14.1.1-rc.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 (55) hide show
  1. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +81 -22
  2. package/app.plugin.js +1 -1
  3. package/ios/HybridRnIap.swift +167 -11
  4. package/ios/ProductStore.swift +10 -0
  5. package/ios/reactnativeiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  6. package/ios/reactnativeiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  7. package/lib/module/helpers/subscription.js +9 -1
  8. package/lib/module/helpers/subscription.js.map +1 -1
  9. package/lib/module/hooks/useIAP.js +9 -13
  10. package/lib/module/hooks/useIAP.js.map +1 -1
  11. package/lib/module/index.js +43 -28
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/module/types.js +24 -16
  14. package/lib/module/types.js.map +1 -1
  15. package/lib/module/utils/error.js.map +1 -1
  16. package/lib/module/utils/type-bridge.js +64 -13
  17. package/lib/module/utils/type-bridge.js.map +1 -1
  18. package/lib/typescript/src/helpers/subscription.d.ts.map +1 -1
  19. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  20. package/lib/typescript/src/index.d.ts +12 -22
  21. package/lib/typescript/src/index.d.ts.map +1 -1
  22. package/lib/typescript/src/specs/RnIap.nitro.d.ts +15 -11
  23. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  24. package/lib/typescript/src/types.d.ts +58 -58
  25. package/lib/typescript/src/types.d.ts.map +1 -1
  26. package/lib/typescript/src/utils/error.d.ts.map +1 -1
  27. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
  28. package/nitro.json +5 -1
  29. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +13 -4
  30. package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +1 -1
  31. package/nitrogen/generated/android/c++/JNitroProduct.hpp +40 -36
  32. package/nitrogen/generated/android/c++/JNitroPurchase.hpp +12 -0
  33. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +1 -1
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroProduct.kt +12 -9
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +9 -0
  36. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +1 -1
  37. package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +1 -1
  38. package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +13 -7
  39. package/nitrogen/generated/ios/swift/NitroProduct.swift +73 -43
  40. package/nitrogen/generated/ios/swift/NitroPurchase.swift +35 -2
  41. package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +1 -1
  42. package/nitrogen/generated/shared/c++/NitroProduct.hpp +41 -37
  43. package/nitrogen/generated/shared/c++/NitroPurchase.hpp +13 -1
  44. package/package.json +9 -2
  45. package/plugin/build/src/withIAP.d.ts +3 -0
  46. package/plugin/build/src/withIAP.js +81 -0
  47. package/plugin/build/tsconfig.tsbuildinfo +1 -0
  48. package/plugin/tsconfig.tsbuildinfo +1 -1
  49. package/src/helpers/subscription.ts +36 -25
  50. package/src/hooks/useIAP.ts +188 -201
  51. package/src/index.ts +377 -356
  52. package/src/specs/RnIap.nitro.ts +15 -11
  53. package/src/types.ts +66 -62
  54. package/src/utils/error.ts +19 -19
  55. package/src/utils/type-bridge.ts +138 -75
@@ -198,6 +198,9 @@ export interface NitroPurchase {
198
198
  transactionDate: number
199
199
  purchaseToken?: string
200
200
  platform: string
201
+ quantity: number
202
+ purchaseState: string
203
+ isAutoRenewing: boolean
201
204
 
202
205
  // iOS specific fields
203
206
  quantityIOS?: number
@@ -233,8 +236,9 @@ export interface NitroProduct {
233
236
  platform: string
234
237
 
235
238
  // iOS specific fields
236
- isFamilyShareable?: boolean
237
- jsonRepresentation?: string
239
+ typeIOS?: string
240
+ isFamilyShareableIOS?: boolean
241
+ jsonRepresentationIOS?: string
238
242
  subscriptionPeriodUnitIOS?: string
239
243
  subscriptionPeriodNumberIOS?: number
240
244
  introductoryPriceIOS?: string
@@ -244,13 +248,13 @@ export interface NitroProduct {
244
248
  introductoryPriceSubscriptionPeriodIOS?: string
245
249
 
246
250
  // Android specific fields
247
- originalPrice?: string
248
- originalPriceAmountMicros?: number
249
- introductoryPriceValue?: number
250
- introductoryPriceCycles?: number
251
- introductoryPricePeriod?: string
252
- subscriptionPeriod?: string
253
- freeTrialPeriod?: string
251
+ originalPriceAndroid?: string
252
+ originalPriceAmountMicrosAndroid?: number
253
+ introductoryPriceValueAndroid?: number
254
+ introductoryPriceCyclesAndroid?: number
255
+ introductoryPricePeriodAndroid?: string
256
+ subscriptionPeriodAndroid?: string
257
+ freeTrialPeriodAndroid?: string
254
258
  subscriptionOfferDetailsAndroid?: string // Android subscription offer details as JSON string
255
259
  }
256
260
 
@@ -452,10 +456,10 @@ export interface RnIap
452
456
 
453
457
  /**
454
458
  * Show manage subscriptions screen (iOS only)
455
- * @returns Promise<boolean> - Success flag
459
+ * @returns Promise<NitroPurchase[]> - Array of updated subscriptions with renewal info
456
460
  * @platform iOS
457
461
  */
458
- showManageSubscriptionsIOS(): Promise<boolean>
462
+ showManageSubscriptionsIOS(): Promise<NitroPurchase[]>
459
463
 
460
464
  /**
461
465
  * Check if user is eligible for intro offer (iOS only)
package/src/types.ts CHANGED
@@ -16,7 +16,13 @@ export type ChangeEventPayload = {
16
16
  value: string
17
17
  }
18
18
 
19
- export type ProductType = 'inapp' | 'subs'
19
+ // iOS detailed product types (4 types)
20
+ export enum ProductTypeIOS {
21
+ consumable = 'consumable',
22
+ nonConsumable = 'nonConsumable',
23
+ autoRenewableSubscription = 'autoRenewableSubscription',
24
+ nonRenewingSubscription = 'nonRenewingSubscription',
25
+ }
20
26
 
21
27
  // ============================================================================
22
28
  // COMMON TYPES (Base types shared across all platforms)
@@ -32,8 +38,8 @@ export type ProductCommon = {
32
38
  title: string
33
39
  /** Product description */
34
40
  description: string
35
- /** Product type: 'inapp' for one-time purchases (consumable/non-consumable), 'subs' for subscriptions */
36
- type: ProductType
41
+ /** Product type: 'inapp' or 'subs' for Android compatibility */
42
+ type: 'inapp' | 'subs' // Note: this is the actual product type, not for filtering
37
43
  /** Display name for the product */
38
44
  displayName?: string
39
45
  /** Formatted price string for display (e.g., "$9.99") */
@@ -69,6 +75,21 @@ export type PurchaseCommon = {
69
75
  purchaseToken?: string
70
76
  /** Platform identifier ('ios' or 'android') */
71
77
  platform?: string
78
+ /** Purchase quantity (defaults to 1) */
79
+ quantity: number
80
+ /** Purchase state (common field) */
81
+ purchaseState: PurchaseState
82
+ /** Auto-renewable subscription flag (common field) */
83
+ isAutoRenewing: boolean
84
+ }
85
+
86
+ export enum PurchaseState {
87
+ pending = 'pending',
88
+ purchased = 'purchased',
89
+ failed = 'failed',
90
+ restored = 'restored', // iOS only
91
+ deferred = 'deferred', // iOS only
92
+ unknown = 'unknown',
72
93
  }
73
94
 
74
95
  export type ProductSubscriptionCommon = ProductCommon & {
@@ -128,6 +149,7 @@ export type ProductIOS = ProductCommon & {
128
149
  jsonRepresentationIOS: string
129
150
  platform: 'ios'
130
151
  subscriptionInfoIOS?: SubscriptionInfo
152
+ typeIOS: ProductTypeIOS // Detailed iOS product type
131
153
  // deprecated fields
132
154
  displayName?: string
133
155
  isFamilyShareable?: boolean
@@ -245,12 +267,6 @@ export type ProductSubscriptionAndroid = ProductAndroid & {
245
267
  subscriptionOfferDetails?: ProductSubscriptionAndroidOfferDetails[]
246
268
  }
247
269
 
248
- export enum PurchaseAndroidState {
249
- UNSPECIFIED_STATE = 0,
250
- PURCHASED = 1,
251
- PENDING = 2,
252
- }
253
-
254
270
  export type PurchaseAndroid = PurchaseCommon & {
255
271
  platform: 'android'
256
272
  /**
@@ -259,8 +275,8 @@ export type PurchaseAndroid = PurchaseCommon & {
259
275
  purchaseTokenAndroid?: string
260
276
  dataAndroid?: string
261
277
  signatureAndroid?: string
278
+ /** @deprecated Use the common `isAutoRenewing` field instead */
262
279
  autoRenewingAndroid?: boolean
263
- purchaseStateAndroid?: PurchaseAndroidState
264
280
  isAcknowledgedAndroid?: boolean
265
281
  packageNameAndroid?: string
266
282
  developerPayloadAndroid?: string
@@ -322,13 +338,21 @@ export type Purchase =
322
338
  // REQUEST TYPES
323
339
  // ============================================================================
324
340
 
341
+ // Product request parameters for fetching products from the store
342
+ export interface ProductRequest {
343
+ /** Product SKUs to fetch */
344
+ skus: string[]
345
+ /** Filter type: "inapp" (default), "subs", or "all" */
346
+ type?: 'inapp' | 'subs' | 'all'
347
+ }
348
+
325
349
  // iOS-specific purchase request parameters
326
350
  export interface RequestPurchaseIosProps {
327
351
  readonly sku: string
328
352
  readonly andDangerouslyFinishTransactionAutomatically?: boolean
329
353
  readonly appAccountToken?: string
330
354
  readonly quantity?: number
331
- readonly withOffer?: PaymentDiscount
355
+ readonly withOffer?: DiscountOffer
332
356
  }
333
357
 
334
358
  // Android-specific purchase request parameters
@@ -418,7 +442,7 @@ export type PurchaseResult = {
418
442
  }
419
443
 
420
444
  // Additional iOS types
421
- export type PaymentDiscount = {
445
+ export type DiscountOffer = {
422
446
  identifier: string
423
447
  keyIdentifier: string
424
448
  nonce: string
@@ -447,22 +471,12 @@ export type AppTransactionIOS = {
447
471
  // ============================================================================
448
472
 
449
473
  /**
450
- * Options for getAvailablePurchases methods
474
+ * Options for getAvailablePurchases and getPurchaseHistories methods
451
475
  */
452
476
  export interface PurchaseOptions {
453
- /**
454
- * @deprecated Use alsoPublishToEventListenerIOS instead
455
- * Whether to also publish purchases to event listener (iOS only)
456
- */
457
- alsoPublishToEventListener?: boolean
458
- /**
459
- * @deprecated Use onlyIncludeActiveItemsIOS instead
460
- * Whether to only include active items (iOS only)
461
- */
462
- onlyIncludeActiveItems?: boolean
463
- /** Whether to also publish purchases to event listener (iOS only) */
477
+ /** Whether to also publish purchases to event listener */
464
478
  alsoPublishToEventListenerIOS?: boolean
465
- /** Whether to only include active items (subscriptions that are still active) (iOS only) */
479
+ /** Whether to only include active items (subscriptions that are still active) */
466
480
  onlyIncludeActiveItemsIOS?: boolean
467
481
  }
468
482
 
@@ -513,7 +527,7 @@ export interface IapContext {
513
527
  /** Initialize connection to the store */
514
528
  initConnection(): Promise<boolean>
515
529
  /** End connection to the store */
516
- endConnection(): Promise<void>
530
+ endConnection(): Promise<boolean>
517
531
  /** Sync purchases (iOS only) */
518
532
  sync(): Promise<void>
519
533
 
@@ -521,11 +535,11 @@ export interface IapContext {
521
535
  /**
522
536
  * Fetch products from the store
523
537
  * @param params.skus - Array of product SKUs to fetch
524
- * @param params.type - Type of products: 'inapp' for regular products or 'subs' for subscriptions
538
+ * @param params.type - Type of products: 'inapp' for regular products, 'subs' for subscriptions, 'all' to fetch both. Defaults to 'inapp'
525
539
  */
526
540
  fetchProducts(params: {
527
541
  skus: string[]
528
- type?: ProductType // 'inapp' | 'subs', defaults to 'inapp'
542
+ type?: 'inapp' | 'subs' | 'all' // Defaults to 'inapp'
529
543
  }): Promise<Product[] | SubscriptionProduct[]>
530
544
 
531
545
  // Purchase methods
@@ -561,7 +575,13 @@ export interface IapContext {
561
575
  // Receipt validation
562
576
  /** Validate a receipt (server-side validation recommended) */
563
577
  validateReceipt(
564
- options: ValidateReceiptProps
578
+ sku: string,
579
+ androidOptions?: {
580
+ packageName: string
581
+ productToken: string
582
+ accessToken: string
583
+ isSub?: boolean
584
+ }
565
585
  ): Promise<ReceiptValidationResult>
566
586
  }
567
587
 
@@ -580,7 +600,7 @@ export interface PurchaseError {
580
600
  /**
581
601
  * Validation options for receipt validation
582
602
  */
583
- export interface ValidateReceiptProps {
603
+ export interface ReceiptValidationProps {
584
604
  /** Product SKU to validate */
585
605
  sku: string
586
606
  /** Android-specific validation options */
@@ -595,7 +615,7 @@ export interface ValidateReceiptProps {
595
615
  /**
596
616
  * iOS receipt validation result
597
617
  */
598
- export interface ReceiptIOS {
618
+ export interface ReceiptValidationResultIOS {
599
619
  /** Whether the receipt is valid */
600
620
  isValid: boolean
601
621
  /** Receipt data string */
@@ -609,35 +629,6 @@ export interface ReceiptIOS {
609
629
  /**
610
630
  * Android receipt validation result
611
631
  */
612
- export interface ReceiptAndroid {
613
- /** Whether the receipt is valid */
614
- isValid: boolean
615
- /** Receipt data string */
616
- receiptData: string
617
- /** JWS representation */
618
- jwsRepresentation: string
619
- /** Latest transaction if available */
620
- latestTransaction?: Purchase
621
- }
622
-
623
- /**
624
- * Receipt validation result from receipt validation
625
- */
626
- export type ReceiptValidationResult = ReceiptAndroid | ReceiptIOS
627
-
628
- /**
629
- * New iOS receipt validation result (matches user specification)
630
- */
631
- export interface ReceiptValidationResultIOS {
632
- isValid: boolean
633
- receiptData: string
634
- jwsRepresentation: string
635
- latestTransaction?: Purchase
636
- }
637
-
638
- /**
639
- * New Android receipt validation result (matches user specification)
640
- */
641
632
  export interface ReceiptValidationResultAndroid {
642
633
  autoRenewing: boolean
643
634
  betaProduct: boolean
@@ -649,7 +640,7 @@ export interface ReceiptValidationResultAndroid {
649
640
  gracePeriodEndDate: number
650
641
  parentProductId: string
651
642
  productId: string
652
- productType: string
643
+ productType: 'inapp' | 'subs'
653
644
  purchaseDate: number
654
645
  quantity: number
655
646
  receiptId: string
@@ -659,6 +650,13 @@ export interface ReceiptValidationResultAndroid {
659
650
  testTransaction: boolean
660
651
  }
661
652
 
653
+ /**
654
+ * Receipt validation result from receipt validation
655
+ */
656
+ export type ReceiptValidationResult =
657
+ | ReceiptValidationResultAndroid
658
+ | ReceiptValidationResultIOS
659
+
662
660
  /**
663
661
  * Represents an active subscription with platform-specific details
664
662
  */
@@ -667,9 +665,15 @@ export interface ActiveSubscription {
667
665
  productId: string
668
666
  /** Whether the subscription is currently active */
669
667
  isActive: boolean
668
+ /** Transaction identifier for backend validation */
669
+ transactionId: string
670
+ /** JWT token (iOS) or purchase token (Android) for backend validation */
671
+ purchaseToken?: string
672
+ /** Transaction timestamp */
673
+ transactionDate: number
670
674
  /** iOS: Subscription expiration date */
671
675
  expirationDateIOS?: Date
672
- /** Android: Whether the subscription auto-renews */
676
+ /** @deprecated Use the common `isAutoRenewing` field instead */
673
677
  autoRenewingAndroid?: boolean
674
678
  /** iOS: Environment where the subscription was purchased (Production/Sandbox) */
675
679
  environmentIOS?: string
@@ -2,15 +2,15 @@
2
2
  * Error utilities for parsing platform-specific error responses
3
3
  */
4
4
 
5
- import { ErrorCode } from '../types'
5
+ import {ErrorCode} from '../types';
6
6
 
7
7
  export interface IapError {
8
- code: string
9
- message: string
10
- responseCode?: number
11
- debugMessage?: string
12
- productId?: string
13
- [key: string]: any // Allow additional platform-specific fields
8
+ code: string;
9
+ message: string;
10
+ responseCode?: number;
11
+ debugMessage?: string;
12
+ productId?: string;
13
+ [key: string]: any; // Allow additional platform-specific fields
14
14
  }
15
15
 
16
16
  /**
@@ -25,11 +25,11 @@ export interface IapError {
25
25
  * @returns Parsed error object with code and message
26
26
  */
27
27
  export function parseErrorStringToJsonObj(
28
- errorString: string | Error | unknown
28
+ errorString: string | Error | unknown,
29
29
  ): IapError {
30
30
  // Handle Error objects
31
31
  if (errorString instanceof Error) {
32
- errorString = errorString.message
32
+ errorString = errorString.message;
33
33
  }
34
34
 
35
35
  // Handle non-string inputs
@@ -37,35 +37,35 @@ export function parseErrorStringToJsonObj(
37
37
  return {
38
38
  code: ErrorCode.E_UNKNOWN,
39
39
  message: 'Unknown error occurred',
40
- }
40
+ };
41
41
  }
42
42
 
43
43
  // Try to parse as JSON first
44
44
  try {
45
- const parsed = JSON.parse(errorString)
45
+ const parsed = JSON.parse(errorString);
46
46
  if (typeof parsed === 'object' && parsed !== null) {
47
47
  // Ensure it has at least code and message
48
48
  return {
49
49
  code: parsed.code || ErrorCode.E_UNKNOWN,
50
50
  message: parsed.message || errorString,
51
51
  ...parsed,
52
- }
52
+ };
53
53
  }
54
54
  } catch {
55
55
  // Not JSON, continue with other formats
56
56
  }
57
57
 
58
58
  // Try to parse "CODE: message" format
59
- const colonIndex = errorString.indexOf(':')
59
+ const colonIndex = errorString.indexOf(':');
60
60
  if (colonIndex > 0 && colonIndex < 50) {
61
61
  // Reasonable position for error code
62
- const potentialCode = errorString.substring(0, colonIndex).trim()
62
+ const potentialCode = errorString.substring(0, colonIndex).trim();
63
63
  // Check if it looks like an error code (starts with E_ or contains uppercase)
64
64
  if (potentialCode.startsWith('E_') || /^[A-Z_]+$/.test(potentialCode)) {
65
65
  return {
66
66
  code: potentialCode,
67
67
  message: errorString.substring(colonIndex + 1).trim(),
68
- }
68
+ };
69
69
  }
70
70
  }
71
71
 
@@ -73,7 +73,7 @@ export function parseErrorStringToJsonObj(
73
73
  return {
74
74
  code: ErrorCode.E_UNKNOWN,
75
75
  message: errorString,
76
- }
76
+ };
77
77
  }
78
78
 
79
79
  /**
@@ -82,16 +82,16 @@ export function parseErrorStringToJsonObj(
82
82
  * @returns true if the error is a user cancellation
83
83
  */
84
84
  export function isUserCancelledError(
85
- error: IapError | string | Error | unknown
85
+ error: IapError | string | Error | unknown,
86
86
  ): boolean {
87
87
  const errorObj =
88
88
  typeof error === 'object' && error !== null && 'code' in error
89
89
  ? (error as IapError)
90
- : parseErrorStringToJsonObj(error)
90
+ : parseErrorStringToJsonObj(error);
91
91
 
92
92
  return (
93
93
  errorObj.code === ErrorCode.E_USER_CANCELLED ||
94
94
  errorObj.code === 'E_USER_CANCELED' || // Alternative spelling
95
95
  errorObj.responseCode === 1
96
- ) // Android BillingClient.BillingResponseCode.USER_CANCELED
96
+ ); // Android BillingClient.BillingResponseCode.USER_CANCELED
97
97
  }