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.
- package/NitroIap.podspec +4 -1
- package/README.md +10 -0
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +5 -1
- package/app.plugin.js +1 -1
- 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/types/react-test-renderer.d.js +2 -0
- package/lib/module/types/react-test-renderer.d.js.map +1 -0
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/errorMapping.js.map +1 -1
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/typescript/plugin/src/withIAP.d.ts.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/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/error.d.ts.map +1 -1
- package/lib/typescript/src/utils/errorMapping.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/package.json +1 -9
- package/plugin/src/withIAP.ts +59 -59
- package/src/helpers/subscription.ts +21 -21
- package/src/hooks/useIAP.ts +193 -193
- package/src/index.ts +346 -344
- package/src/specs/RnIap.nitro.ts +160 -157
- package/src/types/react-test-renderer.d.ts +7 -0
- package/src/types.ts +294 -294
- package/src/utils/error.ts +19 -19
- package/src/utils/errorMapping.ts +13 -13
- package/src/utils/type-bridge.ts +94 -93
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
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
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
|
}
|
package/src/utils/type-bridge.ts
CHANGED
|
@@ -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 {
|
|
22
|
-
import {
|
|
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 =
|
|
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
|
}
|