react-native-iap 14.2.2 → 14.2.3

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 (32) hide show
  1. package/NitroIap.podspec +4 -1
  2. package/README.md +10 -0
  3. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +5 -1
  4. package/app.plugin.js +1 -1
  5. package/lib/module/helpers/subscription.js.map +1 -1
  6. package/lib/module/hooks/useIAP.js.map +1 -1
  7. package/lib/module/index.js.map +1 -1
  8. package/lib/module/types/react-test-renderer.d.js +2 -0
  9. package/lib/module/types/react-test-renderer.d.js.map +1 -0
  10. package/lib/module/utils/error.js.map +1 -1
  11. package/lib/module/utils/errorMapping.js.map +1 -1
  12. package/lib/module/utils/type-bridge.js.map +1 -1
  13. package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
  14. package/lib/typescript/src/helpers/subscription.d.ts.map +1 -1
  15. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  16. package/lib/typescript/src/index.d.ts.map +1 -1
  17. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  18. package/lib/typescript/src/types.d.ts.map +1 -1
  19. package/lib/typescript/src/utils/error.d.ts.map +1 -1
  20. package/lib/typescript/src/utils/errorMapping.d.ts.map +1 -1
  21. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
  22. package/package.json +1 -9
  23. package/plugin/src/withIAP.ts +59 -59
  24. package/src/helpers/subscription.ts +21 -21
  25. package/src/hooks/useIAP.ts +193 -193
  26. package/src/index.ts +346 -344
  27. package/src/specs/RnIap.nitro.ts +160 -157
  28. package/src/types/react-test-renderer.d.ts +7 -0
  29. package/src/types.ts +294 -294
  30. package/src/utils/error.ts +19 -19
  31. package/src/utils/errorMapping.ts +13 -13
  32. package/src/utils/type-bridge.ts +94 -93
@@ -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
  }
@@ -1,10 +1,10 @@
1
- import { ErrorCode, type PurchaseError } from '../types'
1
+ import {ErrorCode, type PurchaseError} from '../types';
2
2
 
3
3
  export function isUserCancelledError(error: PurchaseError): boolean {
4
4
  return (
5
5
  error.code === ErrorCode.E_USER_CANCELLED ||
6
6
  error.code === 'E_USER_CANCELED'
7
- )
7
+ );
8
8
  }
9
9
 
10
10
  export function isRecoverableError(error: PurchaseError): boolean {
@@ -16,29 +16,29 @@ export function isRecoverableError(error: PurchaseError): boolean {
16
16
  ErrorCode.E_SERVICE_DISCONNECTED,
17
17
  ErrorCode.E_INIT_CONNECTION,
18
18
  ErrorCode.E_SYNC_ERROR,
19
- ])
20
- return recoverable.has(error.code)
19
+ ]);
20
+ return recoverable.has(error.code);
21
21
  }
22
22
 
23
23
  export function getUserFriendlyErrorMessage(error: PurchaseError): string {
24
24
  switch (error.code) {
25
25
  case ErrorCode.E_USER_CANCELLED:
26
- return 'Purchase cancelled'
26
+ return 'Purchase cancelled';
27
27
  case ErrorCode.E_NETWORK_ERROR:
28
- return 'Network connection error'
28
+ return 'Network connection error';
29
29
  case ErrorCode.E_SERVICE_ERROR:
30
- return 'Store service error'
30
+ return 'Store service error';
31
31
  case ErrorCode.E_REMOTE_ERROR:
32
- return 'Remote service error'
32
+ return 'Remote service error';
33
33
  case ErrorCode.E_IAP_NOT_AVAILABLE:
34
- return 'In-app purchases are not available on this device'
34
+ return 'In-app purchases are not available on this device';
35
35
  case ErrorCode.E_DEFERRED_PAYMENT:
36
- return 'Payment was deferred (pending approval)'
36
+ return 'Payment was deferred (pending approval)';
37
37
  case ErrorCode.E_TRANSACTION_VALIDATION_FAILED:
38
- return 'Transaction validation failed'
38
+ return 'Transaction validation failed';
39
39
  case ErrorCode.E_SKU_NOT_FOUND:
40
- return 'Product not found'
40
+ return 'Product not found';
41
41
  default:
42
- return error.message || 'Unknown error occurred'
42
+ return error.message || 'Unknown error occurred';
43
43
  }
44
44
  }
@@ -11,15 +11,15 @@ import type {
11
11
  NitroProduct,
12
12
  NitroPurchase,
13
13
  NitroSubscriptionStatus,
14
- } from '../specs/RnIap.nitro'
14
+ } from '../specs/RnIap.nitro';
15
15
  import type {
16
16
  Product,
17
17
  Purchase,
18
18
  SubscriptionProduct,
19
19
  SubscriptionStatusIOS,
20
- } from '../types'
21
- import { PurchaseState, ProductTypeIOS } from '../types'
22
- import { Platform } from 'react-native'
20
+ } from '../types';
21
+ import {PurchaseState, ProductTypeIOS} from '../types';
22
+ import {Platform} from 'react-native';
23
23
 
24
24
  // ============================================================================
25
25
  // PRODUCT CONVERSION
@@ -30,7 +30,7 @@ import { Platform } from 'react-native'
30
30
  * This ensures all fields are properly mapped and accessible
31
31
  */
32
32
  export function convertNitroProductToProduct(
33
- nitroProduct: NitroProduct
33
+ nitroProduct: NitroProduct,
34
34
  ): Product {
35
35
  // Create base product with common fields, handling platform casting
36
36
  const product: any = {
@@ -43,91 +43,92 @@ export function convertNitroProductToProduct(
43
43
  currency: nitroProduct.currency || '',
44
44
  price: nitroProduct.price,
45
45
  platform: nitroProduct.platform as 'ios' | 'android',
46
- }
46
+ };
47
47
 
48
48
  // Add platform-specific fields based on current platform
49
49
  if (Platform.OS === 'ios') {
50
50
  // Map iOS fields from Nitro to TypeScript types
51
- const iosProduct = product as any // Temporarily cast to access iOS fields
52
- iosProduct.isFamilyShareable = (nitroProduct as any).isFamilyShareableIOS
53
- iosProduct.jsonRepresentation = (nitroProduct as any).jsonRepresentationIOS
51
+ const iosProduct = product as any; // Temporarily cast to access iOS fields
52
+ iosProduct.isFamilyShareable = (nitroProduct as any).isFamilyShareableIOS;
53
+ iosProduct.jsonRepresentation = (nitroProduct as any).jsonRepresentationIOS;
54
54
  // Detailed iOS product type - directly from the native field
55
- const typeIOSValue: string | undefined = (nitroProduct as any).typeIOS
55
+ const typeIOSValue: string | undefined = (nitroProduct as any).typeIOS;
56
56
 
57
57
  switch (typeIOSValue) {
58
58
  case 'consumable':
59
- iosProduct.typeIOS = ProductTypeIOS.consumable
60
- break
59
+ iosProduct.typeIOS = ProductTypeIOS.consumable;
60
+ break;
61
61
  case 'nonConsumable':
62
- iosProduct.typeIOS = ProductTypeIOS.nonConsumable
63
- break
62
+ iosProduct.typeIOS = ProductTypeIOS.nonConsumable;
63
+ break;
64
64
  case 'autoRenewableSubscription':
65
- iosProduct.typeIOS = ProductTypeIOS.autoRenewableSubscription
66
- break
65
+ iosProduct.typeIOS = ProductTypeIOS.autoRenewableSubscription;
66
+ break;
67
67
  case 'nonRenewingSubscription':
68
- iosProduct.typeIOS = ProductTypeIOS.nonRenewingSubscription
69
- break
68
+ iosProduct.typeIOS = ProductTypeIOS.nonRenewingSubscription;
69
+ break;
70
70
  default:
71
- iosProduct.typeIOS = undefined
71
+ iosProduct.typeIOS = undefined;
72
72
  }
73
73
  iosProduct.subscriptionPeriodUnitIOS =
74
- nitroProduct.subscriptionPeriodUnitIOS
74
+ nitroProduct.subscriptionPeriodUnitIOS;
75
75
  iosProduct.subscriptionPeriodNumberIOS =
76
- nitroProduct.subscriptionPeriodNumberIOS
77
- iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS
76
+ nitroProduct.subscriptionPeriodNumberIOS;
77
+ iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS;
78
78
  iosProduct.introductoryPriceAsAmountIOS =
79
- nitroProduct.introductoryPriceAsAmountIOS
79
+ nitroProduct.introductoryPriceAsAmountIOS;
80
80
  iosProduct.introductoryPricePaymentModeIOS =
81
- nitroProduct.introductoryPricePaymentModeIOS
81
+ nitroProduct.introductoryPricePaymentModeIOS;
82
82
  iosProduct.introductoryPriceNumberOfPeriodsIOS =
83
- nitroProduct.introductoryPriceNumberOfPeriodsIOS
83
+ nitroProduct.introductoryPriceNumberOfPeriodsIOS;
84
84
  iosProduct.introductoryPriceSubscriptionPeriodIOS =
85
- nitroProduct.introductoryPriceSubscriptionPeriodIOS
85
+ nitroProduct.introductoryPriceSubscriptionPeriodIOS;
86
86
  } else if (Platform.OS === 'android') {
87
87
  // Map Android fields from Nitro to TypeScript types
88
- const androidProduct = product as any // Temporarily cast to access Android fields
89
- androidProduct.originalPrice = (nitroProduct as any).originalPriceAndroid
88
+ const androidProduct = product as any; // Temporarily cast to access Android fields
89
+ androidProduct.originalPrice = (nitroProduct as any).originalPriceAndroid;
90
90
  androidProduct.originalPriceAmountMicros = (
91
91
  nitroProduct as any
92
- ).originalPriceAmountMicrosAndroid
92
+ ).originalPriceAmountMicrosAndroid;
93
93
  androidProduct.introductoryPriceValue = (
94
94
  nitroProduct as any
95
- ).introductoryPriceValueAndroid
95
+ ).introductoryPriceValueAndroid;
96
96
  androidProduct.introductoryPriceCycles = (
97
97
  nitroProduct as any
98
- ).introductoryPriceCyclesAndroid
98
+ ).introductoryPriceCyclesAndroid;
99
99
  androidProduct.introductoryPricePeriod = (
100
100
  nitroProduct as any
101
- ).introductoryPricePeriodAndroid
101
+ ).introductoryPricePeriodAndroid;
102
102
  androidProduct.subscriptionPeriod = (
103
103
  nitroProduct as any
104
- ).subscriptionPeriodAndroid
104
+ ).subscriptionPeriodAndroid;
105
105
  androidProduct.freeTrialPeriod = (
106
106
  nitroProduct as any
107
- ).freeTrialPeriodAndroid
107
+ ).freeTrialPeriodAndroid;
108
108
 
109
109
  // Map subscription offer details (parse from JSON string)
110
110
  if (nitroProduct.subscriptionOfferDetailsAndroid) {
111
111
  try {
112
112
  androidProduct.subscriptionOfferDetailsAndroid = JSON.parse(
113
- nitroProduct.subscriptionOfferDetailsAndroid
114
- )
113
+ nitroProduct.subscriptionOfferDetailsAndroid,
114
+ );
115
115
  } catch (e) {
116
- console.warn('Failed to parse subscription offer details:', e)
117
- androidProduct.subscriptionOfferDetailsAndroid = null
116
+ console.warn('Failed to parse subscription offer details:', e);
117
+ androidProduct.subscriptionOfferDetailsAndroid = null;
118
118
  }
119
119
  }
120
120
 
121
121
  // Create flattened offer fields for easier access in example code
122
122
  androidProduct.oneTimePurchaseOfferFormattedPrice =
123
- nitroProduct.displayPrice
123
+ nitroProduct.displayPrice;
124
124
  androidProduct.oneTimePurchaseOfferPriceAmountMicros = (
125
125
  nitroProduct as any
126
- ).originalPriceAmountMicrosAndroid
127
- androidProduct.oneTimePurchaseOfferPriceCurrencyCode = nitroProduct.currency
126
+ ).originalPriceAmountMicrosAndroid;
127
+ androidProduct.oneTimePurchaseOfferPriceCurrencyCode =
128
+ nitroProduct.currency;
128
129
  }
129
130
 
130
- return product as Product
131
+ return product as Product;
131
132
  }
132
133
 
133
134
  // Note: Use nitroProducts.map(convertNitroProductToProduct) instead of a separate function
@@ -136,19 +137,19 @@ export function convertNitroProductToProduct(
136
137
  * Convert Product to SubscriptionProduct (type-safe casting)
137
138
  */
138
139
  export function convertProductToSubscriptionProduct(
139
- product: Product
140
+ product: Product,
140
141
  ): SubscriptionProduct {
141
142
  if (product.type !== 'subs') {
142
143
  console.warn(
143
144
  'Converting non-subscription product to SubscriptionProduct:',
144
- product.id
145
- )
145
+ product.id,
146
+ );
146
147
  }
147
148
  // Since SubscriptionProduct is now an intersection type, we need to cast properly
148
149
  return {
149
150
  ...product,
150
151
  type: 'subs' as const,
151
- } as SubscriptionProduct
152
+ } as SubscriptionProduct;
152
153
  }
153
154
 
154
155
  // ============================================================================
@@ -159,7 +160,7 @@ export function convertProductToSubscriptionProduct(
159
160
  * Convert NitroPurchase (from native) to TypeScript Purchase (for library consumers)
160
161
  */
161
162
  export function convertNitroPurchaseToPurchase(
162
- nitroPurchase: NitroPurchase
163
+ nitroPurchase: NitroPurchase,
163
164
  ): Purchase {
164
165
  // Create base purchase with common fields
165
166
  const purchase: any = {
@@ -174,56 +175,56 @@ export function convertNitroPurchaseToPurchase(
174
175
  purchaseState:
175
176
  (nitroPurchase.purchaseState as PurchaseState) || PurchaseState.unknown,
176
177
  isAutoRenewing: nitroPurchase.isAutoRenewing || false,
177
- }
178
+ };
178
179
 
179
180
  // Add platform-specific fields
180
181
  if (Platform.OS === 'ios') {
181
- const iosPurchase = purchase as any
182
- iosPurchase.quantityIOS = nitroPurchase.quantityIOS
182
+ const iosPurchase = purchase as any;
183
+ iosPurchase.quantityIOS = nitroPurchase.quantityIOS;
183
184
  iosPurchase.originalTransactionDateIOS =
184
- nitroPurchase.originalTransactionDateIOS
185
+ nitroPurchase.originalTransactionDateIOS;
185
186
  iosPurchase.originalTransactionIdentifierIOS =
186
- nitroPurchase.originalTransactionIdentifierIOS
187
- iosPurchase.appAccountToken = nitroPurchase.appAccountToken
187
+ nitroPurchase.originalTransactionIdentifierIOS;
188
+ iosPurchase.appAccountToken = nitroPurchase.appAccountToken;
188
189
  // Fill common quantity from iOS-specific quantity when available
189
190
  if (typeof nitroPurchase.quantityIOS === 'number') {
190
- purchase.quantity = nitroPurchase.quantityIOS
191
+ purchase.quantity = nitroPurchase.quantityIOS;
191
192
  }
192
193
  } else if (Platform.OS === 'android') {
193
- const androidPurchase = purchase as any
194
- androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid
195
- androidPurchase.dataAndroid = nitroPurchase.dataAndroid
196
- androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid
194
+ const androidPurchase = purchase as any;
195
+ androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid;
196
+ androidPurchase.dataAndroid = nitroPurchase.dataAndroid;
197
+ androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid;
197
198
  // Support both old and new field names for backward compatibility
198
199
  androidPurchase.autoRenewingAndroid =
199
- nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing
200
+ nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing;
200
201
  // no longer surface purchaseStateAndroid on TS side
201
- androidPurchase.isAcknowledgedAndroid = nitroPurchase.isAcknowledgedAndroid
202
- androidPurchase.packageNameAndroid = nitroPurchase.packageNameAndroid
202
+ androidPurchase.isAcknowledgedAndroid = nitroPurchase.isAcknowledgedAndroid;
203
+ androidPurchase.packageNameAndroid = nitroPurchase.packageNameAndroid;
203
204
  androidPurchase.obfuscatedAccountIdAndroid =
204
- nitroPurchase.obfuscatedAccountIdAndroid
205
+ nitroPurchase.obfuscatedAccountIdAndroid;
205
206
  androidPurchase.obfuscatedProfileIdAndroid =
206
- nitroPurchase.obfuscatedProfileIdAndroid
207
+ nitroPurchase.obfuscatedProfileIdAndroid;
207
208
 
208
209
  // Use the common isAutoRenewing field from NitroPurchase
209
- purchase.isAutoRenewing = nitroPurchase.isAutoRenewing
210
+ purchase.isAutoRenewing = nitroPurchase.isAutoRenewing;
210
211
 
211
212
  // Map numeric Android purchase state to common PurchaseState
212
213
  switch (nitroPurchase.purchaseStateAndroid) {
213
214
  case 1:
214
- purchase.purchaseState = PurchaseState.purchased
215
- break
215
+ purchase.purchaseState = PurchaseState.purchased;
216
+ break;
216
217
  case 2:
217
- purchase.purchaseState = PurchaseState.pending
218
- break
218
+ purchase.purchaseState = PurchaseState.pending;
219
+ break;
219
220
  case 0:
220
221
  default:
221
- purchase.purchaseState = PurchaseState.unknown
222
- break
222
+ purchase.purchaseState = PurchaseState.unknown;
223
+ break;
223
224
  }
224
225
  }
225
226
 
226
- return purchase as Purchase
227
+ return purchase as Purchase;
227
228
  }
228
229
 
229
230
  // Note: Use nitroPurchases.map(convertNitroPurchaseToPurchase) instead of a separate function
@@ -233,7 +234,7 @@ export function convertNitroPurchaseToPurchase(
233
234
  // ============================================================================
234
235
 
235
236
  export function convertNitroSubscriptionStatusToSubscriptionStatusIOS(
236
- nitro: NitroSubscriptionStatus
237
+ nitro: NitroSubscriptionStatus,
237
238
  ): SubscriptionStatusIOS {
238
239
  return {
239
240
  state: nitro.state,
@@ -249,7 +250,7 @@ export function convertNitroSubscriptionStatusToSubscriptionStatusIOS(
249
250
  platform: 'ios',
250
251
  }
251
252
  : undefined,
252
- }
253
+ };
253
254
  }
254
255
 
255
256
  // ============================================================================
@@ -261,9 +262,9 @@ export function convertNitroSubscriptionStatusToSubscriptionStatusIOS(
261
262
  */
262
263
  export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
263
264
  if (!nitroProduct || typeof nitroProduct !== 'object') {
264
- return false
265
+ return false;
265
266
  }
266
- const required = ['id', 'title', 'description', 'type', 'platform']
267
+ const required = ['id', 'title', 'description', 'type', 'platform'];
267
268
  for (const field of required) {
268
269
  if (
269
270
  !(field in nitroProduct) ||
@@ -271,12 +272,12 @@ export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
271
272
  ) {
272
273
  console.error(
273
274
  `NitroProduct missing required field: ${field}`,
274
- nitroProduct
275
- )
276
- return false
275
+ nitroProduct,
276
+ );
277
+ return false;
277
278
  }
278
279
  }
279
- return true
280
+ return true;
280
281
  }
281
282
 
282
283
  /**
@@ -284,9 +285,9 @@ export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
284
285
  */
285
286
  export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
286
287
  if (!nitroPurchase || typeof nitroPurchase !== 'object') {
287
- return false
288
+ return false;
288
289
  }
289
- const required = ['id', 'productId', 'transactionDate', 'platform']
290
+ const required = ['id', 'productId', 'transactionDate', 'platform'];
290
291
  for (const field of required) {
291
292
  if (
292
293
  !(field in nitroPurchase) ||
@@ -294,12 +295,12 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
294
295
  ) {
295
296
  console.error(
296
297
  `NitroPurchase missing required field: ${field}`,
297
- nitroPurchase
298
- )
299
- return false
298
+ nitroPurchase,
299
+ );
300
+ return false;
300
301
  }
301
302
  }
302
- return true
303
+ return true;
303
304
  }
304
305
 
305
306
  // ============================================================================
@@ -311,10 +312,10 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
311
312
  * This function can be run in development to detect type mismatches
312
313
  */
313
314
  export function checkTypeSynchronization(): {
314
- isSync: boolean
315
- issues: string[]
315
+ isSync: boolean;
316
+ issues: string[];
316
317
  } {
317
- const issues: string[] = []
318
+ const issues: string[] = [];
318
319
 
319
320
  try {
320
321
  // Simple test: can we convert between types?
@@ -326,19 +327,19 @@ export function checkTypeSynchronization(): {
326
327
  platform: 'ios',
327
328
  displayPrice: '$1.00',
328
329
  currency: 'USD',
329
- }
330
+ };
330
331
 
331
- const converted = convertNitroProductToProduct(testNitroProduct)
332
+ const converted = convertNitroProductToProduct(testNitroProduct);
332
333
 
333
334
  if (!converted.id || !converted.title) {
334
- issues.push('Type conversion failed')
335
+ issues.push('Type conversion failed');
335
336
  }
336
337
  } catch (error) {
337
- issues.push(`Type conversion error: ${error}`)
338
+ issues.push(`Type conversion error: ${error}`);
338
339
  }
339
340
 
340
341
  return {
341
342
  isSync: issues.length === 0,
342
343
  issues,
343
- }
344
+ };
344
345
  }