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.
- package/app.plugin.js +1 -1
- package/ios/HybridRnIap.swift +18 -2
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/lib/module/helpers/subscription.js.map +1 -1
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/typescript/src/helpers/subscription.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/error.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/package.json +1 -1
- package/plugin/build/src/withIAP.d.ts +3 -0
- package/plugin/build/src/withIAP.js +81 -0
- package/plugin/build/tsconfig.tsbuildinfo +1 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/helpers/subscription.ts +30 -30
- package/src/hooks/useIAP.ts +183 -183
- package/src/index.ts +338 -337
- package/src/utils/error.ts +19 -19
- package/src/utils/type-bridge.ts +93 -94
package/src/utils/error.ts
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* Error utilities for parsing platform-specific error responses
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
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
|
}
|
package/src/utils/type-bridge.ts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* Purpose: Prevent type fragmentation between native (Nitro) and TypeScript sides
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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 =
|
|
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
|
}
|