react-native-iap 14.1.0 → 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.
@@ -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
  }
@@ -7,10 +7,10 @@
7
7
  * Purpose: Prevent type fragmentation between native (Nitro) and TypeScript sides
8
8
  */
9
9
 
10
- import type { NitroProduct, NitroPurchase } from '../specs/RnIap.nitro'
11
- import type { Product, Purchase, SubscriptionProduct } from '../types'
12
- import { PurchaseState, ProductTypeIOS } from '../types'
13
- import { Platform } from 'react-native'
10
+ import type {NitroProduct, NitroPurchase} from '../specs/RnIap.nitro';
11
+ import type {Product, Purchase, SubscriptionProduct} from '../types';
12
+ import {PurchaseState, ProductTypeIOS} from '../types';
13
+ import {Platform} from 'react-native';
14
14
 
15
15
  // ============================================================================
16
16
  // PRODUCT CONVERSION
@@ -21,7 +21,7 @@ import { Platform } from 'react-native'
21
21
  * This ensures all fields are properly mapped and accessible
22
22
  */
23
23
  export function convertNitroProductToProduct(
24
- nitroProduct: NitroProduct
24
+ nitroProduct: NitroProduct,
25
25
  ): Product {
26
26
  // Create base product with common fields, handling platform casting
27
27
  const product: any = {
@@ -34,91 +34,92 @@ export function convertNitroProductToProduct(
34
34
  currency: nitroProduct.currency || '',
35
35
  price: nitroProduct.price,
36
36
  platform: nitroProduct.platform as 'ios' | 'android',
37
- }
37
+ };
38
38
 
39
39
  // Add platform-specific fields based on current platform
40
40
  if (Platform.OS === 'ios') {
41
41
  // Map iOS fields from Nitro to TypeScript types
42
- const iosProduct = product as any // Temporarily cast to access iOS fields
43
- iosProduct.isFamilyShareable = (nitroProduct as any).isFamilyShareableIOS
44
- iosProduct.jsonRepresentation = (nitroProduct as any).jsonRepresentationIOS
42
+ const iosProduct = product as any; // Temporarily cast to access iOS fields
43
+ iosProduct.isFamilyShareable = (nitroProduct as any).isFamilyShareableIOS;
44
+ iosProduct.jsonRepresentation = (nitroProduct as any).jsonRepresentationIOS;
45
45
  // Detailed iOS product type - directly from the native field
46
- const typeIOSValue: string | undefined = (nitroProduct as any).typeIOS
46
+ const typeIOSValue: string | undefined = (nitroProduct as any).typeIOS;
47
47
 
48
48
  switch (typeIOSValue) {
49
49
  case 'consumable':
50
- iosProduct.typeIOS = ProductTypeIOS.consumable
51
- break
50
+ iosProduct.typeIOS = ProductTypeIOS.consumable;
51
+ break;
52
52
  case 'nonConsumable':
53
- iosProduct.typeIOS = ProductTypeIOS.nonConsumable
54
- break
53
+ iosProduct.typeIOS = ProductTypeIOS.nonConsumable;
54
+ break;
55
55
  case 'autoRenewableSubscription':
56
- iosProduct.typeIOS = ProductTypeIOS.autoRenewableSubscription
57
- break
56
+ iosProduct.typeIOS = ProductTypeIOS.autoRenewableSubscription;
57
+ break;
58
58
  case 'nonRenewingSubscription':
59
- iosProduct.typeIOS = ProductTypeIOS.nonRenewingSubscription
60
- break
59
+ iosProduct.typeIOS = ProductTypeIOS.nonRenewingSubscription;
60
+ break;
61
61
  default:
62
- iosProduct.typeIOS = undefined
62
+ iosProduct.typeIOS = undefined;
63
63
  }
64
64
  iosProduct.subscriptionPeriodUnitIOS =
65
- nitroProduct.subscriptionPeriodUnitIOS
65
+ nitroProduct.subscriptionPeriodUnitIOS;
66
66
  iosProduct.subscriptionPeriodNumberIOS =
67
- nitroProduct.subscriptionPeriodNumberIOS
68
- iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS
67
+ nitroProduct.subscriptionPeriodNumberIOS;
68
+ iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS;
69
69
  iosProduct.introductoryPriceAsAmountIOS =
70
- nitroProduct.introductoryPriceAsAmountIOS
70
+ nitroProduct.introductoryPriceAsAmountIOS;
71
71
  iosProduct.introductoryPricePaymentModeIOS =
72
- nitroProduct.introductoryPricePaymentModeIOS
72
+ nitroProduct.introductoryPricePaymentModeIOS;
73
73
  iosProduct.introductoryPriceNumberOfPeriodsIOS =
74
- nitroProduct.introductoryPriceNumberOfPeriodsIOS
74
+ nitroProduct.introductoryPriceNumberOfPeriodsIOS;
75
75
  iosProduct.introductoryPriceSubscriptionPeriodIOS =
76
- nitroProduct.introductoryPriceSubscriptionPeriodIOS
76
+ nitroProduct.introductoryPriceSubscriptionPeriodIOS;
77
77
  } else if (Platform.OS === 'android') {
78
78
  // Map Android fields from Nitro to TypeScript types
79
- const androidProduct = product as any // Temporarily cast to access Android fields
80
- androidProduct.originalPrice = (nitroProduct as any).originalPriceAndroid
79
+ const androidProduct = product as any; // Temporarily cast to access Android fields
80
+ androidProduct.originalPrice = (nitroProduct as any).originalPriceAndroid;
81
81
  androidProduct.originalPriceAmountMicros = (
82
82
  nitroProduct as any
83
- ).originalPriceAmountMicrosAndroid
83
+ ).originalPriceAmountMicrosAndroid;
84
84
  androidProduct.introductoryPriceValue = (
85
85
  nitroProduct as any
86
- ).introductoryPriceValueAndroid
86
+ ).introductoryPriceValueAndroid;
87
87
  androidProduct.introductoryPriceCycles = (
88
88
  nitroProduct as any
89
- ).introductoryPriceCyclesAndroid
89
+ ).introductoryPriceCyclesAndroid;
90
90
  androidProduct.introductoryPricePeriod = (
91
91
  nitroProduct as any
92
- ).introductoryPricePeriodAndroid
92
+ ).introductoryPricePeriodAndroid;
93
93
  androidProduct.subscriptionPeriod = (
94
94
  nitroProduct as any
95
- ).subscriptionPeriodAndroid
95
+ ).subscriptionPeriodAndroid;
96
96
  androidProduct.freeTrialPeriod = (
97
97
  nitroProduct as any
98
- ).freeTrialPeriodAndroid
98
+ ).freeTrialPeriodAndroid;
99
99
 
100
100
  // Map subscription offer details (parse from JSON string)
101
101
  if (nitroProduct.subscriptionOfferDetailsAndroid) {
102
102
  try {
103
103
  androidProduct.subscriptionOfferDetailsAndroid = JSON.parse(
104
- nitroProduct.subscriptionOfferDetailsAndroid
105
- )
104
+ nitroProduct.subscriptionOfferDetailsAndroid,
105
+ );
106
106
  } catch (e) {
107
- console.warn('Failed to parse subscription offer details:', e)
108
- androidProduct.subscriptionOfferDetailsAndroid = null
107
+ console.warn('Failed to parse subscription offer details:', e);
108
+ androidProduct.subscriptionOfferDetailsAndroid = null;
109
109
  }
110
110
  }
111
111
 
112
112
  // Create flattened offer fields for easier access in example code
113
113
  androidProduct.oneTimePurchaseOfferFormattedPrice =
114
- nitroProduct.displayPrice
114
+ nitroProduct.displayPrice;
115
115
  androidProduct.oneTimePurchaseOfferPriceAmountMicros = (
116
116
  nitroProduct as any
117
- ).originalPriceAmountMicrosAndroid
118
- androidProduct.oneTimePurchaseOfferPriceCurrencyCode = nitroProduct.currency
117
+ ).originalPriceAmountMicrosAndroid;
118
+ androidProduct.oneTimePurchaseOfferPriceCurrencyCode =
119
+ nitroProduct.currency;
119
120
  }
120
121
 
121
- return product as Product
122
+ return product as Product;
122
123
  }
123
124
 
124
125
  // Note: Use nitroProducts.map(convertNitroProductToProduct) instead of a separate function
@@ -127,19 +128,19 @@ export function convertNitroProductToProduct(
127
128
  * Convert Product to SubscriptionProduct (type-safe casting)
128
129
  */
129
130
  export function convertProductToSubscriptionProduct(
130
- product: Product
131
+ product: Product,
131
132
  ): SubscriptionProduct {
132
133
  if (product.type !== 'subs') {
133
134
  console.warn(
134
135
  'Converting non-subscription product to SubscriptionProduct:',
135
- product.id
136
- )
136
+ product.id,
137
+ );
137
138
  }
138
139
  // Since SubscriptionProduct is now an intersection type, we need to cast properly
139
140
  return {
140
141
  ...product,
141
142
  type: 'subs' as const,
142
- } as SubscriptionProduct
143
+ } as SubscriptionProduct;
143
144
  }
144
145
 
145
146
  // ============================================================================
@@ -150,7 +151,7 @@ export function convertProductToSubscriptionProduct(
150
151
  * Convert NitroPurchase (from native) to TypeScript Purchase (for library consumers)
151
152
  */
152
153
  export function convertNitroPurchaseToPurchase(
153
- nitroPurchase: NitroPurchase
154
+ nitroPurchase: NitroPurchase,
154
155
  ): Purchase {
155
156
  // Create base purchase with common fields
156
157
  const purchase: any = {
@@ -162,59 +163,57 @@ export function convertNitroPurchaseToPurchase(
162
163
  platform: nitroPurchase.platform as 'ios' | 'android',
163
164
  // Common fields from NitroPurchase
164
165
  quantity: nitroPurchase.quantity || 1,
165
- purchaseState:
166
- (nitroPurchase.purchaseState as PurchaseState) || PurchaseState.unknown,
166
+ purchaseState: nitroPurchase.purchaseState as PurchaseState || PurchaseState.unknown,
167
167
  isAutoRenewing: nitroPurchase.isAutoRenewing || false,
168
- }
168
+ };
169
169
 
170
170
  // Add platform-specific fields
171
171
  if (Platform.OS === 'ios') {
172
- const iosPurchase = purchase as any
173
- iosPurchase.quantityIOS = nitroPurchase.quantityIOS
172
+ const iosPurchase = purchase as any;
173
+ iosPurchase.quantityIOS = nitroPurchase.quantityIOS;
174
174
  iosPurchase.originalTransactionDateIOS =
175
- nitroPurchase.originalTransactionDateIOS
175
+ nitroPurchase.originalTransactionDateIOS;
176
176
  iosPurchase.originalTransactionIdentifierIOS =
177
- nitroPurchase.originalTransactionIdentifierIOS
178
- iosPurchase.appAccountToken = nitroPurchase.appAccountToken
177
+ nitroPurchase.originalTransactionIdentifierIOS;
178
+ iosPurchase.appAccountToken = nitroPurchase.appAccountToken;
179
179
  // Fill common quantity from iOS-specific quantity when available
180
180
  if (typeof nitroPurchase.quantityIOS === 'number') {
181
- purchase.quantity = nitroPurchase.quantityIOS
181
+ purchase.quantity = nitroPurchase.quantityIOS;
182
182
  }
183
183
  } else if (Platform.OS === 'android') {
184
- const androidPurchase = purchase as any
185
- androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid
186
- androidPurchase.dataAndroid = nitroPurchase.dataAndroid
187
- androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid
184
+ const androidPurchase = purchase as any;
185
+ androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid;
186
+ androidPurchase.dataAndroid = nitroPurchase.dataAndroid;
187
+ androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid;
188
188
  // Support both old and new field names for backward compatibility
189
- androidPurchase.autoRenewingAndroid =
190
- nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing
189
+ androidPurchase.autoRenewingAndroid = nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing;
191
190
  // no longer surface purchaseStateAndroid on TS side
192
- androidPurchase.isAcknowledgedAndroid = nitroPurchase.isAcknowledgedAndroid
193
- androidPurchase.packageNameAndroid = nitroPurchase.packageNameAndroid
191
+ androidPurchase.isAcknowledgedAndroid = nitroPurchase.isAcknowledgedAndroid;
192
+ androidPurchase.packageNameAndroid = nitroPurchase.packageNameAndroid;
194
193
  androidPurchase.obfuscatedAccountIdAndroid =
195
- nitroPurchase.obfuscatedAccountIdAndroid
194
+ nitroPurchase.obfuscatedAccountIdAndroid;
196
195
  androidPurchase.obfuscatedProfileIdAndroid =
197
- nitroPurchase.obfuscatedProfileIdAndroid
196
+ nitroPurchase.obfuscatedProfileIdAndroid;
198
197
 
199
198
  // Use the common isAutoRenewing field from NitroPurchase
200
- purchase.isAutoRenewing = nitroPurchase.isAutoRenewing
199
+ purchase.isAutoRenewing = nitroPurchase.isAutoRenewing;
201
200
 
202
201
  // Map numeric Android purchase state to common PurchaseState
203
202
  switch (nitroPurchase.purchaseStateAndroid) {
204
203
  case 1:
205
- purchase.purchaseState = PurchaseState.purchased
206
- break
204
+ purchase.purchaseState = PurchaseState.purchased;
205
+ break;
207
206
  case 2:
208
- purchase.purchaseState = PurchaseState.pending
209
- break
207
+ purchase.purchaseState = PurchaseState.pending;
208
+ break;
210
209
  case 0:
211
210
  default:
212
- purchase.purchaseState = PurchaseState.unknown
213
- break
211
+ purchase.purchaseState = PurchaseState.unknown;
212
+ break;
214
213
  }
215
214
  }
216
215
 
217
- return purchase as Purchase
216
+ return purchase as Purchase;
218
217
  }
219
218
 
220
219
  // Note: Use nitroPurchases.map(convertNitroPurchaseToPurchase) instead of a separate function
@@ -228,9 +227,9 @@ export function convertNitroPurchaseToPurchase(
228
227
  */
229
228
  export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
230
229
  if (!nitroProduct || typeof nitroProduct !== 'object') {
231
- return false
230
+ return false;
232
231
  }
233
- const required = ['id', 'title', 'description', 'type', 'platform']
232
+ const required = ['id', 'title', 'description', 'type', 'platform'];
234
233
  for (const field of required) {
235
234
  if (
236
235
  !(field in nitroProduct) ||
@@ -238,12 +237,12 @@ export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
238
237
  ) {
239
238
  console.error(
240
239
  `NitroProduct missing required field: ${field}`,
241
- nitroProduct
242
- )
243
- return false
240
+ nitroProduct,
241
+ );
242
+ return false;
244
243
  }
245
244
  }
246
- return true
245
+ return true;
247
246
  }
248
247
 
249
248
  /**
@@ -251,9 +250,9 @@ export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
251
250
  */
252
251
  export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
253
252
  if (!nitroPurchase || typeof nitroPurchase !== 'object') {
254
- return false
253
+ return false;
255
254
  }
256
- const required = ['id', 'productId', 'transactionDate', 'platform']
255
+ const required = ['id', 'productId', 'transactionDate', 'platform'];
257
256
  for (const field of required) {
258
257
  if (
259
258
  !(field in nitroPurchase) ||
@@ -261,12 +260,12 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
261
260
  ) {
262
261
  console.error(
263
262
  `NitroPurchase missing required field: ${field}`,
264
- nitroPurchase
265
- )
266
- return false
263
+ nitroPurchase,
264
+ );
265
+ return false;
267
266
  }
268
267
  }
269
- return true
268
+ return true;
270
269
  }
271
270
 
272
271
  // ============================================================================
@@ -278,10 +277,10 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
278
277
  * This function can be run in development to detect type mismatches
279
278
  */
280
279
  export function checkTypeSynchronization(): {
281
- isSync: boolean
282
- issues: string[]
280
+ isSync: boolean;
281
+ issues: string[];
283
282
  } {
284
- const issues: string[] = []
283
+ const issues: string[] = [];
285
284
 
286
285
  try {
287
286
  // Simple test: can we convert between types?
@@ -293,19 +292,19 @@ export function checkTypeSynchronization(): {
293
292
  platform: 'ios',
294
293
  displayPrice: '$1.00',
295
294
  currency: 'USD',
296
- }
295
+ };
297
296
 
298
- const converted = convertNitroProductToProduct(testNitroProduct)
297
+ const converted = convertNitroProductToProduct(testNitroProduct);
299
298
 
300
299
  if (!converted.id || !converted.title) {
301
- issues.push('Type conversion failed')
300
+ issues.push('Type conversion failed');
302
301
  }
303
302
  } catch (error) {
304
- issues.push(`Type conversion error: ${error}`)
303
+ issues.push(`Type conversion error: ${error}`);
305
304
  }
306
305
 
307
306
  return {
308
307
  isSync: issues.length === 0,
309
308
  issues,
310
- }
309
+ };
311
310
  }