react-native-iap 14.3.2 → 14.3.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/lib/module/helpers/subscription.js +2 -2
- package/lib/module/helpers/subscription.js.map +1 -1
- package/lib/module/hooks/useIAP.js +14 -8
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +76 -23
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +90 -190
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/error.js +4 -4
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/errorMapping.js +34 -10
- package/lib/module/utils/errorMapping.js.map +1 -1
- package/lib/module/utils/type-bridge.js +217 -173
- 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 +8 -11
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +11 -10
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +2 -2
- package/lib/typescript/src/types.d.ts +606 -518
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/errorMapping.d.ts +2 -1
- package/lib/typescript/src/utils/errorMapping.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts +13 -14
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +4 -4
- package/nitrogen/generated/android/c++/{JNitroAndroidReceiptValidationOptions.hpp → JNitroReceiptValidationAndroidOptions.hpp} +9 -9
- package/nitrogen/generated/android/c++/JNitroReceiptValidationParams.hpp +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/{NitroAndroidReceiptValidationOptions.kt → NitroReceiptValidationAndroidOptions.kt} +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationParams.kt +1 -1
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +10 -10
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +3 -3
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +3 -3
- package/nitrogen/generated/ios/swift/{NitroAndroidReceiptValidationOptions.swift → NitroReceiptValidationAndroidOptions.swift} +5 -5
- package/nitrogen/generated/ios/swift/NitroReceiptValidationParams.swift +9 -9
- package/nitrogen/generated/shared/c++/{NitroAndroidReceiptValidationOptions.hpp → NitroReceiptValidationAndroidOptions.hpp} +10 -10
- package/nitrogen/generated/shared/c++/NitroReceiptValidationParams.hpp +8 -8
- package/package.json +1 -1
- package/src/helpers/subscription.ts +8 -9
- package/src/hooks/useIAP.ts +52 -47
- package/src/index.ts +123 -35
- package/src/specs/RnIap.nitro.ts +2 -2
- package/src/types.ts +651 -616
- package/src/utils/error.ts +4 -4
- package/src/utils/errorMapping.ts +47 -19
- package/src/utils/type-bridge.ts +308 -204
package/src/utils/type-bridge.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type Bridge Utilities
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Purpose: Prevent type fragmentation between native (Nitro) and TypeScript sides
|
|
4
|
+
* Converts the loose Nitro shapes coming from native into the strongly typed
|
|
5
|
+
* structures that our generated TypeScript types expect.
|
|
8
6
|
*/
|
|
9
7
|
|
|
10
8
|
import type {
|
|
@@ -12,258 +10,367 @@ import type {
|
|
|
12
10
|
NitroPurchase,
|
|
13
11
|
NitroSubscriptionStatus,
|
|
14
12
|
} from '../specs/RnIap.nitro';
|
|
13
|
+
import {
|
|
14
|
+
Platform as IapPlatform,
|
|
15
|
+
PaymentModeIOS,
|
|
16
|
+
ProductType,
|
|
17
|
+
ProductTypeIOS,
|
|
18
|
+
PurchaseState,
|
|
19
|
+
SubscriptionPeriodIOS,
|
|
20
|
+
} from '../types';
|
|
15
21
|
import type {
|
|
16
22
|
Product,
|
|
23
|
+
ProductSubscription,
|
|
17
24
|
Purchase,
|
|
18
|
-
SubscriptionProduct,
|
|
19
25
|
SubscriptionStatusIOS,
|
|
20
26
|
} from '../types';
|
|
21
|
-
import {PurchaseState, ProductTypeIOS} from '../types';
|
|
22
|
-
import {Platform} from 'react-native';
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const PLATFORM_IOS = 'ios';
|
|
29
|
+
const PRODUCT_TYPE_SUBS = 'subs';
|
|
30
|
+
const PURCHASE_STATE_DEFERRED = 'deferred';
|
|
31
|
+
const PURCHASE_STATE_FAILED = 'failed';
|
|
32
|
+
const PURCHASE_STATE_PENDING = 'pending';
|
|
33
|
+
const PURCHASE_STATE_PURCHASED = 'purchased';
|
|
34
|
+
const PURCHASE_STATE_RESTORED = 'restored';
|
|
35
|
+
const DEFAULT_JSON_REPR = '{}';
|
|
36
|
+
|
|
37
|
+
type Nullable<T> = T | null | undefined;
|
|
38
|
+
|
|
39
|
+
function normalizePlatform(value?: Nullable<string>): IapPlatform {
|
|
40
|
+
return value?.toLowerCase() === PLATFORM_IOS
|
|
41
|
+
? IapPlatform.Ios
|
|
42
|
+
: IapPlatform.Android;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function normalizeProductType(value?: Nullable<string>): ProductType {
|
|
46
|
+
return value?.toLowerCase() === PRODUCT_TYPE_SUBS
|
|
47
|
+
? ProductType.Subs
|
|
48
|
+
: ProductType.InApp;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeProductTypeIOS(value?: Nullable<string>): ProductTypeIOS {
|
|
52
|
+
switch ((value ?? '').toLowerCase()) {
|
|
53
|
+
case 'consumable':
|
|
54
|
+
return ProductTypeIOS.Consumable;
|
|
55
|
+
case 'nonconsumable':
|
|
56
|
+
case 'non_consumable':
|
|
57
|
+
case 'non-consumable':
|
|
58
|
+
return ProductTypeIOS.NonConsumable;
|
|
59
|
+
case 'autorenewablesubscription':
|
|
60
|
+
case 'auto_renewable_subscription':
|
|
61
|
+
case 'autorenewable':
|
|
62
|
+
return ProductTypeIOS.AutoRenewableSubscription;
|
|
63
|
+
case 'nonrenewingsubscription':
|
|
64
|
+
case 'non_renewing_subscription':
|
|
65
|
+
return ProductTypeIOS.NonRenewingSubscription;
|
|
66
|
+
default:
|
|
67
|
+
if (value) {
|
|
68
|
+
console.warn(
|
|
69
|
+
`[react-native-iap] Unknown iOS product type "${value}", defaulting to NonConsumable.`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return ProductTypeIOS.NonConsumable;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizePaymentMode(value?: Nullable<string>): PaymentModeIOS | null {
|
|
77
|
+
switch ((value ?? '').toUpperCase()) {
|
|
78
|
+
case 'FREE_TRIAL':
|
|
79
|
+
case 'FREETRIAL':
|
|
80
|
+
return PaymentModeIOS.FreeTrial;
|
|
81
|
+
case 'PAY_AS_YOU_GO':
|
|
82
|
+
case 'PAYASYOUGO':
|
|
83
|
+
return PaymentModeIOS.PayAsYouGo;
|
|
84
|
+
case 'PAY_UP_FRONT':
|
|
85
|
+
case 'PAYUPFRONT':
|
|
86
|
+
return PaymentModeIOS.PayUpFront;
|
|
87
|
+
default:
|
|
88
|
+
return PaymentModeIOS.Empty;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeSubscriptionPeriod(
|
|
93
|
+
value?: Nullable<string>,
|
|
94
|
+
): SubscriptionPeriodIOS | null {
|
|
95
|
+
switch ((value ?? '').toUpperCase()) {
|
|
96
|
+
case 'DAY':
|
|
97
|
+
return SubscriptionPeriodIOS.Day;
|
|
98
|
+
case 'WEEK':
|
|
99
|
+
return SubscriptionPeriodIOS.Week;
|
|
100
|
+
case 'MONTH':
|
|
101
|
+
return SubscriptionPeriodIOS.Month;
|
|
102
|
+
case 'YEAR':
|
|
103
|
+
return SubscriptionPeriodIOS.Year;
|
|
104
|
+
default:
|
|
105
|
+
return SubscriptionPeriodIOS.Empty;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function normalizePurchaseState(state: unknown): PurchaseState {
|
|
110
|
+
if (typeof state === 'string') {
|
|
111
|
+
switch (state.toLowerCase()) {
|
|
112
|
+
case PURCHASE_STATE_PURCHASED:
|
|
113
|
+
return PurchaseState.Purchased;
|
|
114
|
+
case PURCHASE_STATE_PENDING:
|
|
115
|
+
return PurchaseState.Pending;
|
|
116
|
+
case PURCHASE_STATE_FAILED:
|
|
117
|
+
return PurchaseState.Failed;
|
|
118
|
+
case PURCHASE_STATE_RESTORED:
|
|
119
|
+
return PurchaseState.Restored;
|
|
120
|
+
case PURCHASE_STATE_DEFERRED:
|
|
121
|
+
return PurchaseState.Deferred;
|
|
122
|
+
default:
|
|
123
|
+
return PurchaseState.Unknown;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (typeof state === 'number') {
|
|
128
|
+
switch (state) {
|
|
129
|
+
case 1:
|
|
130
|
+
return PurchaseState.Purchased;
|
|
131
|
+
case 2:
|
|
132
|
+
return PurchaseState.Pending;
|
|
133
|
+
default:
|
|
134
|
+
return PurchaseState.Unknown;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return PurchaseState.Unknown;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function toNullableString(value: unknown): string | null {
|
|
142
|
+
if (value == null) return null;
|
|
143
|
+
return String(value);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function toNullableNumber(value: unknown): number | null {
|
|
147
|
+
if (value == null) return null;
|
|
148
|
+
const num = Number(value);
|
|
149
|
+
return Number.isFinite(num) ? num : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function toNullableBoolean(value: unknown): boolean | null {
|
|
153
|
+
if (value == null) return null;
|
|
154
|
+
if (typeof value === 'boolean') return value;
|
|
155
|
+
if (typeof value === 'number') return value !== 0;
|
|
156
|
+
if (typeof value === 'string') return value.toLowerCase() === 'true';
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function parseSubscriptionOffers(value?: Nullable<string>) {
|
|
161
|
+
if (!value) return undefined;
|
|
162
|
+
try {
|
|
163
|
+
const parsed = JSON.parse(value);
|
|
164
|
+
if (Array.isArray(parsed)) {
|
|
165
|
+
return parsed;
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.warn('Failed to parse subscriptionOfferDetailsAndroid:', error);
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
27
172
|
|
|
28
173
|
/**
|
|
29
|
-
* Convert NitroProduct (from native) to
|
|
30
|
-
* This ensures all fields are properly mapped and accessible
|
|
174
|
+
* Convert NitroProduct (from native) to generated Product type
|
|
31
175
|
*/
|
|
32
176
|
export function convertNitroProductToProduct(
|
|
33
177
|
nitroProduct: NitroProduct,
|
|
34
178
|
): Product {
|
|
35
|
-
|
|
36
|
-
const
|
|
179
|
+
const platform = normalizePlatform(nitroProduct.platform);
|
|
180
|
+
const type = normalizeProductType(nitroProduct.type);
|
|
181
|
+
|
|
182
|
+
const base: any = {
|
|
37
183
|
id: nitroProduct.id,
|
|
38
184
|
title: nitroProduct.title,
|
|
39
185
|
description: nitroProduct.description,
|
|
40
|
-
type
|
|
41
|
-
displayName: nitroProduct.displayName,
|
|
42
|
-
displayPrice: nitroProduct.displayPrice
|
|
43
|
-
currency: nitroProduct.currency
|
|
44
|
-
price: nitroProduct.price,
|
|
45
|
-
|
|
186
|
+
type,
|
|
187
|
+
displayName: nitroProduct.displayName ?? null,
|
|
188
|
+
displayPrice: nitroProduct.displayPrice ?? '',
|
|
189
|
+
currency: nitroProduct.currency ?? '',
|
|
190
|
+
price: toNullableNumber(nitroProduct.price),
|
|
191
|
+
debugDescription: null,
|
|
192
|
+
platform,
|
|
46
193
|
};
|
|
47
194
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
iosProduct.subscriptionPeriodUnitIOS =
|
|
74
|
-
nitroProduct.subscriptionPeriodUnitIOS;
|
|
75
|
-
iosProduct.subscriptionPeriodNumberIOS =
|
|
76
|
-
nitroProduct.subscriptionPeriodNumberIOS;
|
|
77
|
-
iosProduct.introductoryPriceIOS = nitroProduct.introductoryPriceIOS;
|
|
78
|
-
iosProduct.introductoryPriceAsAmountIOS =
|
|
79
|
-
nitroProduct.introductoryPriceAsAmountIOS;
|
|
80
|
-
iosProduct.introductoryPricePaymentModeIOS =
|
|
81
|
-
nitroProduct.introductoryPricePaymentModeIOS;
|
|
82
|
-
iosProduct.introductoryPriceNumberOfPeriodsIOS =
|
|
83
|
-
nitroProduct.introductoryPriceNumberOfPeriodsIOS;
|
|
195
|
+
if (platform === IapPlatform.Ios) {
|
|
196
|
+
const iosProduct: any = {
|
|
197
|
+
...base,
|
|
198
|
+
displayNameIOS: nitroProduct.displayName ?? nitroProduct.title,
|
|
199
|
+
isFamilyShareableIOS: Boolean(
|
|
200
|
+
(nitroProduct as any).isFamilyShareableIOS ?? false,
|
|
201
|
+
),
|
|
202
|
+
jsonRepresentationIOS:
|
|
203
|
+
(nitroProduct as any).jsonRepresentationIOS ?? DEFAULT_JSON_REPR,
|
|
204
|
+
typeIOS: normalizeProductTypeIOS((nitroProduct as any).typeIOS),
|
|
205
|
+
subscriptionInfoIOS: undefined,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
iosProduct.introductoryPriceAsAmountIOS = toNullableString(
|
|
209
|
+
(nitroProduct as any).introductoryPriceAsAmountIOS,
|
|
210
|
+
);
|
|
211
|
+
iosProduct.introductoryPriceIOS = toNullableString(
|
|
212
|
+
(nitroProduct as any).introductoryPriceIOS,
|
|
213
|
+
);
|
|
214
|
+
iosProduct.introductoryPriceNumberOfPeriodsIOS = toNullableString(
|
|
215
|
+
(nitroProduct as any).introductoryPriceNumberOfPeriodsIOS,
|
|
216
|
+
);
|
|
217
|
+
iosProduct.introductoryPricePaymentModeIOS = normalizePaymentMode(
|
|
218
|
+
(nitroProduct as any).introductoryPricePaymentModeIOS,
|
|
219
|
+
);
|
|
84
220
|
iosProduct.introductoryPriceSubscriptionPeriodIOS =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
nitroProduct as any
|
|
95
|
-
).introductoryPriceValueAndroid;
|
|
96
|
-
androidProduct.introductoryPriceCycles = (
|
|
97
|
-
nitroProduct as any
|
|
98
|
-
).introductoryPriceCyclesAndroid;
|
|
99
|
-
androidProduct.introductoryPricePeriod = (
|
|
100
|
-
nitroProduct as any
|
|
101
|
-
).introductoryPricePeriodAndroid;
|
|
102
|
-
androidProduct.subscriptionPeriod = (
|
|
103
|
-
nitroProduct as any
|
|
104
|
-
).subscriptionPeriodAndroid;
|
|
105
|
-
androidProduct.freeTrialPeriod = (
|
|
106
|
-
nitroProduct as any
|
|
107
|
-
).freeTrialPeriodAndroid;
|
|
108
|
-
|
|
109
|
-
// Map subscription offer details (parse from JSON string)
|
|
110
|
-
if (nitroProduct.subscriptionOfferDetailsAndroid) {
|
|
111
|
-
try {
|
|
112
|
-
androidProduct.subscriptionOfferDetailsAndroid = JSON.parse(
|
|
113
|
-
nitroProduct.subscriptionOfferDetailsAndroid,
|
|
114
|
-
);
|
|
115
|
-
} catch (e) {
|
|
116
|
-
console.warn('Failed to parse subscription offer details:', e);
|
|
117
|
-
androidProduct.subscriptionOfferDetailsAndroid = null;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
221
|
+
normalizeSubscriptionPeriod(
|
|
222
|
+
(nitroProduct as any).introductoryPriceSubscriptionPeriodIOS,
|
|
223
|
+
);
|
|
224
|
+
iosProduct.subscriptionPeriodNumberIOS = toNullableString(
|
|
225
|
+
(nitroProduct as any).subscriptionPeriodNumberIOS,
|
|
226
|
+
);
|
|
227
|
+
iosProduct.subscriptionPeriodUnitIOS = normalizeSubscriptionPeriod(
|
|
228
|
+
(nitroProduct as any).subscriptionPeriodUnitIOS,
|
|
229
|
+
);
|
|
120
230
|
|
|
121
|
-
|
|
122
|
-
androidProduct.oneTimePurchaseOfferFormattedPrice =
|
|
123
|
-
nitroProduct.displayPrice;
|
|
124
|
-
androidProduct.oneTimePurchaseOfferPriceAmountMicros = (
|
|
125
|
-
nitroProduct as any
|
|
126
|
-
).originalPriceAmountMicrosAndroid;
|
|
127
|
-
androidProduct.oneTimePurchaseOfferPriceCurrencyCode =
|
|
128
|
-
nitroProduct.currency;
|
|
231
|
+
return iosProduct as Product;
|
|
129
232
|
}
|
|
130
233
|
|
|
131
|
-
|
|
132
|
-
|
|
234
|
+
const androidProduct: any = {
|
|
235
|
+
...base,
|
|
236
|
+
nameAndroid: (nitroProduct as any).nameAndroid ?? nitroProduct.title,
|
|
237
|
+
oneTimePurchaseOfferDetailsAndroid: (nitroProduct as any)
|
|
238
|
+
.oneTimePurchaseOfferDetailsAndroid,
|
|
239
|
+
subscriptionOfferDetailsAndroid: parseSubscriptionOffers(
|
|
240
|
+
(nitroProduct as any).subscriptionOfferDetailsAndroid,
|
|
241
|
+
),
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if (type === ProductType.Subs) {
|
|
245
|
+
if (!Array.isArray(androidProduct.subscriptionOfferDetailsAndroid)) {
|
|
246
|
+
androidProduct.subscriptionOfferDetailsAndroid = [];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
133
249
|
|
|
134
|
-
|
|
250
|
+
return androidProduct as Product;
|
|
251
|
+
}
|
|
135
252
|
|
|
136
253
|
/**
|
|
137
|
-
* Convert Product to
|
|
254
|
+
* Convert Product to ProductSubscription (type-safe casting helper)
|
|
138
255
|
*/
|
|
139
|
-
export function
|
|
256
|
+
export function convertProductToProductSubscription(
|
|
140
257
|
product: Product,
|
|
141
|
-
):
|
|
142
|
-
if (product.type !==
|
|
258
|
+
): ProductSubscription {
|
|
259
|
+
if (product.type !== ProductType.Subs) {
|
|
143
260
|
console.warn(
|
|
144
|
-
'Converting non-subscription product to
|
|
261
|
+
'Converting non-subscription product to ProductSubscription:',
|
|
145
262
|
product.id,
|
|
146
263
|
);
|
|
147
264
|
}
|
|
148
|
-
// Since SubscriptionProduct is now an intersection type, we need to cast properly
|
|
149
|
-
return {
|
|
150
|
-
...product,
|
|
151
|
-
type: 'subs' as const,
|
|
152
|
-
} as SubscriptionProduct;
|
|
153
|
-
}
|
|
154
265
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
266
|
+
const output: any = {...(product as any)};
|
|
267
|
+
|
|
268
|
+
if (output.platform === IapPlatform.Android) {
|
|
269
|
+
if (!Array.isArray(output.subscriptionOfferDetailsAndroid)) {
|
|
270
|
+
output.subscriptionOfferDetailsAndroid = [];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return output;
|
|
275
|
+
}
|
|
158
276
|
|
|
159
277
|
/**
|
|
160
|
-
* Convert NitroPurchase (from native) to
|
|
278
|
+
* Convert NitroPurchase (from native) to generated Purchase type
|
|
161
279
|
*/
|
|
162
280
|
export function convertNitroPurchaseToPurchase(
|
|
163
281
|
nitroPurchase: NitroPurchase,
|
|
164
282
|
): Purchase {
|
|
165
|
-
|
|
283
|
+
const platform = normalizePlatform(nitroPurchase.platform);
|
|
166
284
|
const purchase: any = {
|
|
167
285
|
id: nitroPurchase.id,
|
|
168
286
|
productId: nitroPurchase.productId,
|
|
169
|
-
transactionDate: nitroPurchase.transactionDate,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
platform
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
isAutoRenewing: nitroPurchase.isAutoRenewing
|
|
287
|
+
transactionDate: nitroPurchase.transactionDate ?? Date.now(),
|
|
288
|
+
purchaseToken:
|
|
289
|
+
nitroPurchase.purchaseToken ?? nitroPurchase.purchaseTokenAndroid ?? null,
|
|
290
|
+
platform,
|
|
291
|
+
quantity: nitroPurchase.quantity ?? 1,
|
|
292
|
+
purchaseState: normalizePurchaseState(
|
|
293
|
+
nitroPurchase.purchaseState ?? nitroPurchase.purchaseStateAndroid,
|
|
294
|
+
),
|
|
295
|
+
isAutoRenewing: Boolean(nitroPurchase.isAutoRenewing),
|
|
178
296
|
};
|
|
179
297
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
nitroPurchase.
|
|
186
|
-
|
|
187
|
-
nitroPurchase.originalTransactionIdentifierIOS;
|
|
188
|
-
iosPurchase.appAccountToken = nitroPurchase.appAccountToken;
|
|
189
|
-
// Fill common quantity from iOS-specific quantity when available
|
|
190
|
-
if (typeof nitroPurchase.quantityIOS === 'number') {
|
|
191
|
-
purchase.quantity = nitroPurchase.quantityIOS;
|
|
192
|
-
}
|
|
193
|
-
} else if (Platform.OS === 'android') {
|
|
194
|
-
const androidPurchase = purchase as any;
|
|
195
|
-
androidPurchase.purchaseTokenAndroid = nitroPurchase.purchaseTokenAndroid;
|
|
196
|
-
androidPurchase.dataAndroid = nitroPurchase.dataAndroid;
|
|
197
|
-
androidPurchase.signatureAndroid = nitroPurchase.signatureAndroid;
|
|
198
|
-
// Support both old and new field names for backward compatibility
|
|
199
|
-
androidPurchase.autoRenewingAndroid =
|
|
200
|
-
nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing;
|
|
201
|
-
// no longer surface purchaseStateAndroid on TS side
|
|
202
|
-
androidPurchase.isAcknowledgedAndroid = nitroPurchase.isAcknowledgedAndroid;
|
|
203
|
-
androidPurchase.packageNameAndroid = nitroPurchase.packageNameAndroid;
|
|
204
|
-
androidPurchase.obfuscatedAccountIdAndroid =
|
|
205
|
-
nitroPurchase.obfuscatedAccountIdAndroid;
|
|
206
|
-
androidPurchase.obfuscatedProfileIdAndroid =
|
|
207
|
-
nitroPurchase.obfuscatedProfileIdAndroid;
|
|
208
|
-
|
|
209
|
-
// Use the common isAutoRenewing field from NitroPurchase
|
|
210
|
-
purchase.isAutoRenewing = nitroPurchase.isAutoRenewing;
|
|
211
|
-
|
|
212
|
-
// Map numeric Android purchase state to common PurchaseState
|
|
213
|
-
switch (nitroPurchase.purchaseStateAndroid) {
|
|
214
|
-
case 1:
|
|
215
|
-
purchase.purchaseState = PurchaseState.purchased;
|
|
216
|
-
break;
|
|
217
|
-
case 2:
|
|
218
|
-
purchase.purchaseState = PurchaseState.pending;
|
|
219
|
-
break;
|
|
220
|
-
case 0:
|
|
221
|
-
default:
|
|
222
|
-
purchase.purchaseState = PurchaseState.unknown;
|
|
223
|
-
break;
|
|
224
|
-
}
|
|
298
|
+
if (
|
|
299
|
+
purchase.purchaseState === PurchaseState.Unknown &&
|
|
300
|
+
nitroPurchase.purchaseStateAndroid != null
|
|
301
|
+
) {
|
|
302
|
+
purchase.purchaseState = normalizePurchaseState(
|
|
303
|
+
nitroPurchase.purchaseStateAndroid,
|
|
304
|
+
);
|
|
225
305
|
}
|
|
226
306
|
|
|
227
|
-
|
|
228
|
-
|
|
307
|
+
if (platform === IapPlatform.Ios) {
|
|
308
|
+
const iosPurchase: any = purchase;
|
|
309
|
+
iosPurchase.quantityIOS = toNullableNumber(nitroPurchase.quantityIOS);
|
|
310
|
+
iosPurchase.originalTransactionDateIOS = toNullableNumber(
|
|
311
|
+
nitroPurchase.originalTransactionDateIOS,
|
|
312
|
+
);
|
|
313
|
+
iosPurchase.originalTransactionIdentifierIOS = toNullableString(
|
|
314
|
+
nitroPurchase.originalTransactionIdentifierIOS,
|
|
315
|
+
);
|
|
316
|
+
iosPurchase.appAccountToken = toNullableString(
|
|
317
|
+
nitroPurchase.appAccountToken,
|
|
318
|
+
);
|
|
319
|
+
return iosPurchase as Purchase;
|
|
320
|
+
}
|
|
229
321
|
|
|
230
|
-
|
|
322
|
+
const androidPurchase: any = purchase;
|
|
323
|
+
androidPurchase.autoRenewingAndroid = toNullableBoolean(
|
|
324
|
+
nitroPurchase.autoRenewingAndroid ?? nitroPurchase.isAutoRenewing,
|
|
325
|
+
);
|
|
326
|
+
androidPurchase.dataAndroid = toNullableString(nitroPurchase.dataAndroid);
|
|
327
|
+
androidPurchase.signatureAndroid = toNullableString(
|
|
328
|
+
nitroPurchase.signatureAndroid,
|
|
329
|
+
);
|
|
330
|
+
androidPurchase.isAcknowledgedAndroid = toNullableBoolean(
|
|
331
|
+
nitroPurchase.isAcknowledgedAndroid,
|
|
332
|
+
);
|
|
333
|
+
androidPurchase.packageNameAndroid = toNullableString(
|
|
334
|
+
nitroPurchase.packageNameAndroid,
|
|
335
|
+
);
|
|
336
|
+
androidPurchase.obfuscatedAccountIdAndroid = toNullableString(
|
|
337
|
+
nitroPurchase.obfuscatedAccountIdAndroid,
|
|
338
|
+
);
|
|
339
|
+
androidPurchase.obfuscatedProfileIdAndroid = toNullableString(
|
|
340
|
+
nitroPurchase.obfuscatedProfileIdAndroid,
|
|
341
|
+
);
|
|
231
342
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// ============================================================================
|
|
343
|
+
return androidPurchase as Purchase;
|
|
344
|
+
}
|
|
235
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Convert Nitro subscription status (iOS) to generated type
|
|
348
|
+
*/
|
|
236
349
|
export function convertNitroSubscriptionStatusToSubscriptionStatusIOS(
|
|
237
350
|
nitro: NitroSubscriptionStatus,
|
|
238
351
|
): SubscriptionStatusIOS {
|
|
239
352
|
return {
|
|
240
|
-
state: nitro.state,
|
|
241
|
-
platform: 'ios',
|
|
353
|
+
state: String(nitro.state ?? ''),
|
|
242
354
|
renewalInfo: nitro.renewalInfo
|
|
243
355
|
? {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
currentProductID: nitro.renewalInfo.currentProductID,
|
|
250
|
-
platform: 'ios',
|
|
356
|
+
autoRenewPreference: toNullableString(
|
|
357
|
+
nitro.renewalInfo.autoRenewPreference,
|
|
358
|
+
),
|
|
359
|
+
jsonRepresentation: JSON.stringify(nitro.renewalInfo),
|
|
360
|
+
willAutoRenew: Boolean(nitro.renewalInfo.autoRenewStatus),
|
|
251
361
|
}
|
|
252
362
|
: undefined,
|
|
253
363
|
};
|
|
254
364
|
}
|
|
255
365
|
|
|
256
|
-
// ============================================================================
|
|
257
|
-
// TYPE VALIDATION
|
|
258
|
-
// ============================================================================
|
|
259
|
-
|
|
260
366
|
/**
|
|
261
|
-
* Validate that a NitroProduct has
|
|
367
|
+
* Validate that a NitroProduct has the expected shape
|
|
262
368
|
*/
|
|
263
369
|
export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
|
|
264
370
|
if (!nitroProduct || typeof nitroProduct !== 'object') {
|
|
265
371
|
return false;
|
|
266
372
|
}
|
|
373
|
+
|
|
267
374
|
const required = ['id', 'title', 'description', 'type', 'platform'];
|
|
268
375
|
for (const field of required) {
|
|
269
376
|
if (
|
|
@@ -277,16 +384,18 @@ export function validateNitroProduct(nitroProduct: NitroProduct): boolean {
|
|
|
277
384
|
return false;
|
|
278
385
|
}
|
|
279
386
|
}
|
|
387
|
+
|
|
280
388
|
return true;
|
|
281
389
|
}
|
|
282
390
|
|
|
283
391
|
/**
|
|
284
|
-
* Validate that a NitroPurchase has
|
|
392
|
+
* Validate that a NitroPurchase has the expected shape
|
|
285
393
|
*/
|
|
286
394
|
export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
|
|
287
395
|
if (!nitroPurchase || typeof nitroPurchase !== 'object') {
|
|
288
396
|
return false;
|
|
289
397
|
}
|
|
398
|
+
|
|
290
399
|
const required = ['id', 'productId', 'transactionDate', 'platform'];
|
|
291
400
|
for (const field of required) {
|
|
292
401
|
if (
|
|
@@ -300,16 +409,12 @@ export function validateNitroPurchase(nitroPurchase: NitroPurchase): boolean {
|
|
|
300
409
|
return false;
|
|
301
410
|
}
|
|
302
411
|
}
|
|
412
|
+
|
|
303
413
|
return true;
|
|
304
414
|
}
|
|
305
415
|
|
|
306
|
-
// ============================================================================
|
|
307
|
-
// TYPE SYNCHRONIZATION HELPERS
|
|
308
|
-
// ============================================================================
|
|
309
|
-
|
|
310
416
|
/**
|
|
311
|
-
*
|
|
312
|
-
* This function can be run in development to detect type mismatches
|
|
417
|
+
* Development helper to check that type conversions stay valid
|
|
313
418
|
*/
|
|
314
419
|
export function checkTypeSynchronization(): {
|
|
315
420
|
isSync: boolean;
|
|
@@ -318,24 +423,23 @@ export function checkTypeSynchronization(): {
|
|
|
318
423
|
const issues: string[] = [];
|
|
319
424
|
|
|
320
425
|
try {
|
|
321
|
-
// Simple test: can we convert between types?
|
|
322
426
|
const testNitroProduct: NitroProduct = {
|
|
323
427
|
id: 'test',
|
|
324
428
|
title: 'Test',
|
|
325
|
-
description: 'Test',
|
|
429
|
+
description: 'Test product',
|
|
326
430
|
type: 'inapp',
|
|
327
|
-
platform:
|
|
431
|
+
platform: PLATFORM_IOS,
|
|
328
432
|
displayPrice: '$1.00',
|
|
329
433
|
currency: 'USD',
|
|
434
|
+
price: 1,
|
|
330
435
|
};
|
|
331
436
|
|
|
332
437
|
const converted = convertNitroProductToProduct(testNitroProduct);
|
|
333
|
-
|
|
334
438
|
if (!converted.id || !converted.title) {
|
|
335
439
|
issues.push('Type conversion failed');
|
|
336
440
|
}
|
|
337
441
|
} catch (error) {
|
|
338
|
-
issues.push(`Type conversion error: ${error}`);
|
|
442
|
+
issues.push(`Type conversion error: ${String(error)}`);
|
|
339
443
|
}
|
|
340
444
|
|
|
341
445
|
return {
|