react-native-iap 14.1.0 → 14.1.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 +5 -2
- 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/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// External dependencies
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {Platform} from 'react-native';
|
|
3
|
+
import {NitroModules} from 'react-native-nitro-modules';
|
|
4
4
|
|
|
5
5
|
// Internal modules
|
|
6
6
|
import type {
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
NitroReceiptValidationParams,
|
|
10
10
|
NitroReceiptValidationResultIOS,
|
|
11
11
|
NitroReceiptValidationResultAndroid,
|
|
12
|
-
} from './specs/RnIap.nitro'
|
|
12
|
+
} from './specs/RnIap.nitro';
|
|
13
13
|
import type {
|
|
14
14
|
Product,
|
|
15
15
|
Purchase,
|
|
@@ -23,15 +23,15 @@ import type {
|
|
|
23
23
|
ReceiptValidationResultAndroid,
|
|
24
24
|
RequestPurchaseIosProps,
|
|
25
25
|
RequestPurchaseAndroidProps,
|
|
26
|
-
} from './types'
|
|
27
|
-
import type {
|
|
26
|
+
} from './types';
|
|
27
|
+
import type {ProductRequest} from './types';
|
|
28
28
|
import {
|
|
29
29
|
convertNitroProductToProduct,
|
|
30
30
|
convertNitroPurchaseToPurchase,
|
|
31
31
|
validateNitroProduct,
|
|
32
32
|
validateNitroPurchase,
|
|
33
|
-
} from './utils/type-bridge'
|
|
34
|
-
import {
|
|
33
|
+
} from './utils/type-bridge';
|
|
34
|
+
import {parseErrorStringToJsonObj} from './utils/error';
|
|
35
35
|
|
|
36
36
|
// Export all types
|
|
37
37
|
export type {
|
|
@@ -39,48 +39,48 @@ export type {
|
|
|
39
39
|
NitroProduct,
|
|
40
40
|
NitroPurchase,
|
|
41
41
|
NitroPurchaseResult,
|
|
42
|
-
} from './specs/RnIap.nitro'
|
|
43
|
-
export * from './types'
|
|
44
|
-
export * from './utils/error'
|
|
42
|
+
} from './specs/RnIap.nitro';
|
|
43
|
+
export * from './types';
|
|
44
|
+
export * from './utils/error';
|
|
45
45
|
|
|
46
46
|
// Types for event listeners
|
|
47
47
|
export interface EventSubscription {
|
|
48
|
-
remove(): void
|
|
48
|
+
remove(): void;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// ActiveSubscription and PurchaseError types are already exported via 'export * from ./types'
|
|
52
52
|
|
|
53
53
|
// Export hooks
|
|
54
|
-
export {
|
|
54
|
+
export {useIAP} from './hooks/useIAP';
|
|
55
55
|
|
|
56
56
|
// Development utilities removed - use type bridge functions directly if needed
|
|
57
57
|
|
|
58
58
|
// Create the RnIap HybridObject instance (internal use only)
|
|
59
|
-
const iap = NitroModules.createHybridObject<RnIap>('RnIap')
|
|
59
|
+
const iap = NitroModules.createHybridObject<RnIap>('RnIap');
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Initialize connection to the store
|
|
63
63
|
*/
|
|
64
64
|
export const initConnection = async (): Promise<boolean> => {
|
|
65
65
|
try {
|
|
66
|
-
return await iap.initConnection()
|
|
66
|
+
return await iap.initConnection();
|
|
67
67
|
} catch (error) {
|
|
68
|
-
console.error('Failed to initialize IAP connection:', error)
|
|
69
|
-
throw error
|
|
68
|
+
console.error('Failed to initialize IAP connection:', error);
|
|
69
|
+
throw error;
|
|
70
70
|
}
|
|
71
|
-
}
|
|
71
|
+
};
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* End connection to the store
|
|
75
75
|
*/
|
|
76
76
|
export const endConnection = async (): Promise<boolean> => {
|
|
77
77
|
try {
|
|
78
|
-
return await iap.endConnection()
|
|
78
|
+
return await iap.endConnection();
|
|
79
79
|
} catch (error) {
|
|
80
|
-
console.error('Failed to end IAP connection:', error)
|
|
81
|
-
throw error
|
|
80
|
+
console.error('Failed to end IAP connection:', error);
|
|
81
|
+
throw error;
|
|
82
82
|
}
|
|
83
|
-
}
|
|
83
|
+
};
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Fetch products from the store
|
|
@@ -104,41 +104,41 @@ export const fetchProducts = async ({
|
|
|
104
104
|
}: ProductRequest): Promise<Product[]> => {
|
|
105
105
|
try {
|
|
106
106
|
if (!skus || skus.length === 0) {
|
|
107
|
-
throw new Error('No SKUs provided')
|
|
107
|
+
throw new Error('No SKUs provided');
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
if (type === 'all') {
|
|
111
111
|
const [inappNitro, subsNitro] = await Promise.all([
|
|
112
112
|
iap.fetchProducts(skus, 'inapp'),
|
|
113
113
|
iap.fetchProducts(skus, 'subs'),
|
|
114
|
-
])
|
|
115
|
-
const allNitro = [...inappNitro, ...subsNitro]
|
|
116
|
-
const validAll = allNitro.filter(validateNitroProduct)
|
|
114
|
+
]);
|
|
115
|
+
const allNitro = [...inappNitro, ...subsNitro];
|
|
116
|
+
const validAll = allNitro.filter(validateNitroProduct);
|
|
117
117
|
if (validAll.length !== allNitro.length) {
|
|
118
118
|
console.warn(
|
|
119
|
-
`[fetchProducts] Some products failed validation: ${allNitro.length - validAll.length} invalid
|
|
120
|
-
)
|
|
119
|
+
`[fetchProducts] Some products failed validation: ${allNitro.length - validAll.length} invalid`,
|
|
120
|
+
);
|
|
121
121
|
}
|
|
122
|
-
return validAll.map(convertNitroProductToProduct)
|
|
122
|
+
return validAll.map(convertNitroProductToProduct);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
const nitroProducts = await iap.fetchProducts(skus, type)
|
|
125
|
+
const nitroProducts = await iap.fetchProducts(skus, type);
|
|
126
126
|
|
|
127
127
|
// Validate and convert NitroProducts to TypeScript Products
|
|
128
|
-
const validProducts = nitroProducts.filter(validateNitroProduct)
|
|
128
|
+
const validProducts = nitroProducts.filter(validateNitroProduct);
|
|
129
129
|
if (validProducts.length !== nitroProducts.length) {
|
|
130
130
|
console.warn(
|
|
131
|
-
`[fetchProducts] Some products failed validation: ${nitroProducts.length - validProducts.length} invalid
|
|
132
|
-
)
|
|
131
|
+
`[fetchProducts] Some products failed validation: ${nitroProducts.length - validProducts.length} invalid`,
|
|
132
|
+
);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
const typedProducts = validProducts.map(convertNitroProductToProduct)
|
|
136
|
-
return typedProducts
|
|
135
|
+
const typedProducts = validProducts.map(convertNitroProductToProduct);
|
|
136
|
+
return typedProducts;
|
|
137
137
|
} catch (error) {
|
|
138
|
-
console.error('[fetchProducts] Failed:', error)
|
|
139
|
-
throw error
|
|
138
|
+
console.error('[fetchProducts] Failed:', error);
|
|
139
|
+
throw error;
|
|
140
140
|
}
|
|
141
|
-
}
|
|
141
|
+
};
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* Request a purchase for products or subscriptions
|
|
@@ -182,65 +182,64 @@ export const requestPurchase = async ({
|
|
|
182
182
|
request,
|
|
183
183
|
type = 'inapp',
|
|
184
184
|
}: {
|
|
185
|
-
request: RequestPurchaseProps | RequestSubscriptionProps
|
|
186
|
-
type?: 'inapp' | 'subs'
|
|
185
|
+
request: RequestPurchaseProps | RequestSubscriptionProps;
|
|
186
|
+
type?: 'inapp' | 'subs';
|
|
187
187
|
}): Promise<void> => {
|
|
188
188
|
try {
|
|
189
189
|
// Validate platform-specific requests
|
|
190
190
|
if (Platform.OS === 'ios') {
|
|
191
|
-
const iosRequest = request.ios
|
|
191
|
+
const iosRequest = request.ios;
|
|
192
192
|
if (!iosRequest?.sku) {
|
|
193
193
|
throw new Error(
|
|
194
|
-
'Invalid request for iOS. The `sku` property is required.'
|
|
195
|
-
)
|
|
194
|
+
'Invalid request for iOS. The `sku` property is required.',
|
|
195
|
+
);
|
|
196
196
|
}
|
|
197
197
|
} else if (Platform.OS === 'android') {
|
|
198
|
-
const androidRequest = request.android
|
|
198
|
+
const androidRequest = request.android;
|
|
199
199
|
if (!androidRequest?.skus?.length) {
|
|
200
200
|
throw new Error(
|
|
201
|
-
'Invalid request for Android. The `skus` property is required and must be a non-empty array.'
|
|
202
|
-
)
|
|
201
|
+
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
202
|
+
);
|
|
203
203
|
}
|
|
204
204
|
} else {
|
|
205
|
-
throw new Error('Unsupported platform')
|
|
205
|
+
throw new Error('Unsupported platform');
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
// Transform the request for the unified interface
|
|
209
|
-
const unifiedRequest: any = {}
|
|
209
|
+
const unifiedRequest: any = {};
|
|
210
210
|
|
|
211
211
|
if (Platform.OS === 'ios' && request.ios) {
|
|
212
|
-
const iosReq = request.ios as RequestPurchaseIosProps
|
|
212
|
+
const iosReq = request.ios as RequestPurchaseIosProps;
|
|
213
213
|
const autoFinishSubs =
|
|
214
|
-
type === 'subs' &&
|
|
215
|
-
iosReq.andDangerouslyFinishTransactionAutomatically == null
|
|
214
|
+
type === 'subs' && iosReq.andDangerouslyFinishTransactionAutomatically == null;
|
|
216
215
|
unifiedRequest.ios = {
|
|
217
216
|
...iosReq,
|
|
218
217
|
// Align with native SwiftUI flow: auto-finish subscriptions by default
|
|
219
218
|
...(autoFinishSubs
|
|
220
|
-
? {
|
|
219
|
+
? {andDangerouslyFinishTransactionAutomatically: true}
|
|
221
220
|
: {}),
|
|
222
|
-
} as RequestPurchaseIosProps
|
|
221
|
+
} as RequestPurchaseIosProps;
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
if (Platform.OS === 'android' && request.android) {
|
|
226
225
|
if (type === 'subs') {
|
|
227
|
-
const subsRequest = request.android as RequestSubscriptionAndroidProps
|
|
226
|
+
const subsRequest = request.android as RequestSubscriptionAndroidProps;
|
|
228
227
|
unifiedRequest.android = {
|
|
229
228
|
...subsRequest,
|
|
230
229
|
subscriptionOffers: subsRequest.subscriptionOffers || [],
|
|
231
|
-
} as RequestPurchaseAndroidProps
|
|
230
|
+
} as RequestPurchaseAndroidProps;
|
|
232
231
|
} else {
|
|
233
|
-
unifiedRequest.android = request.android
|
|
232
|
+
unifiedRequest.android = request.android;
|
|
234
233
|
}
|
|
235
234
|
}
|
|
236
235
|
|
|
237
236
|
// Call unified method - returns void, listen for events instead
|
|
238
|
-
await iap.requestPurchase(unifiedRequest)
|
|
237
|
+
await iap.requestPurchase(unifiedRequest);
|
|
239
238
|
} catch (error) {
|
|
240
|
-
console.error('Failed to request purchase:', error)
|
|
241
|
-
throw error
|
|
239
|
+
console.error('Failed to request purchase:', error);
|
|
240
|
+
throw error;
|
|
242
241
|
}
|
|
243
|
-
}
|
|
242
|
+
};
|
|
244
243
|
|
|
245
244
|
/**
|
|
246
245
|
* Get available purchases (purchased items not yet consumed/finished)
|
|
@@ -261,7 +260,7 @@ export const getAvailablePurchases = async ({
|
|
|
261
260
|
}: PurchaseOptions = {}): Promise<Purchase[]> => {
|
|
262
261
|
try {
|
|
263
262
|
// Create unified options
|
|
264
|
-
const options: any = {}
|
|
263
|
+
const options: any = {};
|
|
265
264
|
|
|
266
265
|
if (Platform.OS === 'ios') {
|
|
267
266
|
// Provide both new and deprecated keys for compatibility
|
|
@@ -270,46 +269,46 @@ export const getAvailablePurchases = async ({
|
|
|
270
269
|
onlyIncludeActiveItemsIOS,
|
|
271
270
|
alsoPublishToEventListener: alsoPublishToEventListenerIOS,
|
|
272
271
|
onlyIncludeActiveItems: onlyIncludeActiveItemsIOS,
|
|
273
|
-
}
|
|
272
|
+
};
|
|
274
273
|
} else if (Platform.OS === 'android') {
|
|
275
274
|
// For Android, we need to call twice for inapp and subs
|
|
276
275
|
const inappNitroPurchases = await iap.getAvailablePurchases({
|
|
277
|
-
android: {
|
|
278
|
-
})
|
|
276
|
+
android: {type: 'inapp'},
|
|
277
|
+
});
|
|
279
278
|
const subsNitroPurchases = await iap.getAvailablePurchases({
|
|
280
|
-
android: {
|
|
281
|
-
})
|
|
279
|
+
android: {type: 'subs'},
|
|
280
|
+
});
|
|
282
281
|
|
|
283
282
|
// Validate and convert both sets of purchases
|
|
284
|
-
const allNitroPurchases = [...inappNitroPurchases, ...subsNitroPurchases]
|
|
285
|
-
const validPurchases = allNitroPurchases.filter(validateNitroPurchase)
|
|
283
|
+
const allNitroPurchases = [...inappNitroPurchases, ...subsNitroPurchases];
|
|
284
|
+
const validPurchases = allNitroPurchases.filter(validateNitroPurchase);
|
|
286
285
|
if (validPurchases.length !== allNitroPurchases.length) {
|
|
287
286
|
console.warn(
|
|
288
|
-
`[getAvailablePurchases] Some Android purchases failed validation: ${allNitroPurchases.length - validPurchases.length} invalid
|
|
289
|
-
)
|
|
287
|
+
`[getAvailablePurchases] Some Android purchases failed validation: ${allNitroPurchases.length - validPurchases.length} invalid`,
|
|
288
|
+
);
|
|
290
289
|
}
|
|
291
290
|
|
|
292
|
-
return validPurchases.map(convertNitroPurchaseToPurchase)
|
|
291
|
+
return validPurchases.map(convertNitroPurchaseToPurchase);
|
|
293
292
|
} else {
|
|
294
|
-
throw new Error('Unsupported platform')
|
|
293
|
+
throw new Error('Unsupported platform');
|
|
295
294
|
}
|
|
296
295
|
|
|
297
|
-
const nitroPurchases = await iap.getAvailablePurchases(options)
|
|
296
|
+
const nitroPurchases = await iap.getAvailablePurchases(options);
|
|
298
297
|
|
|
299
298
|
// Validate and convert NitroPurchases to TypeScript Purchases
|
|
300
|
-
const validPurchases = nitroPurchases.filter(validateNitroPurchase)
|
|
299
|
+
const validPurchases = nitroPurchases.filter(validateNitroPurchase);
|
|
301
300
|
if (validPurchases.length !== nitroPurchases.length) {
|
|
302
301
|
console.warn(
|
|
303
|
-
`[getAvailablePurchases] Some purchases failed validation: ${nitroPurchases.length - validPurchases.length} invalid
|
|
304
|
-
)
|
|
302
|
+
`[getAvailablePurchases] Some purchases failed validation: ${nitroPurchases.length - validPurchases.length} invalid`,
|
|
303
|
+
);
|
|
305
304
|
}
|
|
306
305
|
|
|
307
|
-
return validPurchases.map(convertNitroPurchaseToPurchase)
|
|
306
|
+
return validPurchases.map(convertNitroPurchaseToPurchase);
|
|
308
307
|
} catch (error) {
|
|
309
|
-
console.error('Failed to get available purchases:', error)
|
|
310
|
-
throw error
|
|
308
|
+
console.error('Failed to get available purchases:', error);
|
|
309
|
+
throw error;
|
|
311
310
|
}
|
|
312
|
-
}
|
|
311
|
+
};
|
|
313
312
|
|
|
314
313
|
/**
|
|
315
314
|
* Finish a transaction (consume or acknowledge)
|
|
@@ -331,58 +330,58 @@ export const finishTransaction = async ({
|
|
|
331
330
|
}: FinishTransactionParams): Promise<NitroPurchaseResult | boolean> => {
|
|
332
331
|
try {
|
|
333
332
|
// Create unified params
|
|
334
|
-
const params: any = {}
|
|
333
|
+
const params: any = {};
|
|
335
334
|
|
|
336
335
|
if (Platform.OS === 'ios') {
|
|
337
336
|
if (!purchase.id) {
|
|
338
|
-
throw new Error('purchase.id required to finish iOS transaction')
|
|
337
|
+
throw new Error('purchase.id required to finish iOS transaction');
|
|
339
338
|
}
|
|
340
339
|
params.ios = {
|
|
341
340
|
transactionId: purchase.id,
|
|
342
|
-
}
|
|
341
|
+
};
|
|
343
342
|
} else if (Platform.OS === 'android') {
|
|
344
|
-
const androidPurchase = purchase as PurchaseAndroid
|
|
343
|
+
const androidPurchase = purchase as PurchaseAndroid;
|
|
345
344
|
const token =
|
|
346
|
-
androidPurchase.purchaseToken || androidPurchase.purchaseTokenAndroid
|
|
345
|
+
androidPurchase.purchaseToken || androidPurchase.purchaseTokenAndroid;
|
|
347
346
|
|
|
348
347
|
if (!token) {
|
|
349
|
-
throw new Error('purchaseToken required to finish Android transaction')
|
|
348
|
+
throw new Error('purchaseToken required to finish Android transaction');
|
|
350
349
|
}
|
|
351
350
|
|
|
352
351
|
params.android = {
|
|
353
352
|
purchaseToken: token,
|
|
354
353
|
isConsumable,
|
|
355
|
-
}
|
|
354
|
+
};
|
|
356
355
|
} else {
|
|
357
|
-
throw new Error('Unsupported platform')
|
|
356
|
+
throw new Error('Unsupported platform');
|
|
358
357
|
}
|
|
359
358
|
|
|
360
|
-
const result = await iap.finishTransaction(params)
|
|
359
|
+
const result = await iap.finishTransaction(params);
|
|
361
360
|
|
|
362
361
|
// Handle variant return type
|
|
363
362
|
if (typeof result === 'boolean') {
|
|
364
|
-
return result
|
|
363
|
+
return result;
|
|
365
364
|
}
|
|
366
365
|
// It's a PurchaseResult
|
|
367
|
-
return result as NitroPurchaseResult
|
|
366
|
+
return result as NitroPurchaseResult;
|
|
368
367
|
} catch (error) {
|
|
369
368
|
// If iOS transaction has already been auto-finished natively, treat as success
|
|
370
369
|
if (Platform.OS === 'ios') {
|
|
371
|
-
const err = parseErrorStringToJsonObj(error)
|
|
372
|
-
const msg = (err?.message || '').toString()
|
|
373
|
-
const code = (err?.code || '').toString()
|
|
370
|
+
const err = parseErrorStringToJsonObj(error);
|
|
371
|
+
const msg = (err?.message || '').toString();
|
|
372
|
+
const code = (err?.code || '').toString();
|
|
374
373
|
if (
|
|
375
374
|
msg.includes('Transaction not found') ||
|
|
376
375
|
code === 'E_ITEM_UNAVAILABLE'
|
|
377
376
|
) {
|
|
378
377
|
// Consider already finished
|
|
379
|
-
return true
|
|
378
|
+
return true;
|
|
380
379
|
}
|
|
381
380
|
}
|
|
382
|
-
console.error('Failed to finish transaction:', error)
|
|
383
|
-
throw error
|
|
381
|
+
console.error('Failed to finish transaction:', error);
|
|
382
|
+
throw error;
|
|
384
383
|
}
|
|
385
|
-
}
|
|
384
|
+
};
|
|
386
385
|
|
|
387
386
|
/**
|
|
388
387
|
* Acknowledge a purchase (Android only)
|
|
@@ -394,11 +393,13 @@ export const finishTransaction = async ({
|
|
|
394
393
|
* ```
|
|
395
394
|
*/
|
|
396
395
|
export const acknowledgePurchaseAndroid = async (
|
|
397
|
-
purchaseToken: string
|
|
396
|
+
purchaseToken: string,
|
|
398
397
|
): Promise<NitroPurchaseResult> => {
|
|
399
398
|
try {
|
|
400
399
|
if (Platform.OS !== 'android') {
|
|
401
|
-
throw new Error(
|
|
400
|
+
throw new Error(
|
|
401
|
+
'acknowledgePurchaseAndroid is only available on Android',
|
|
402
|
+
);
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
const result = await iap.finishTransaction({
|
|
@@ -406,7 +407,7 @@ export const acknowledgePurchaseAndroid = async (
|
|
|
406
407
|
purchaseToken,
|
|
407
408
|
isConsumable: false,
|
|
408
409
|
},
|
|
409
|
-
})
|
|
410
|
+
});
|
|
410
411
|
|
|
411
412
|
// Result is a variant, extract PurchaseResult
|
|
412
413
|
if (typeof result === 'boolean') {
|
|
@@ -416,14 +417,14 @@ export const acknowledgePurchaseAndroid = async (
|
|
|
416
417
|
code: '0',
|
|
417
418
|
message: 'Success',
|
|
418
419
|
purchaseToken,
|
|
419
|
-
}
|
|
420
|
+
};
|
|
420
421
|
}
|
|
421
|
-
return result as NitroPurchaseResult
|
|
422
|
+
return result as NitroPurchaseResult;
|
|
422
423
|
} catch (error) {
|
|
423
|
-
console.error('Failed to acknowledge purchase Android:', error)
|
|
424
|
-
throw error
|
|
424
|
+
console.error('Failed to acknowledge purchase Android:', error);
|
|
425
|
+
throw error;
|
|
425
426
|
}
|
|
426
|
-
}
|
|
427
|
+
};
|
|
427
428
|
|
|
428
429
|
/**
|
|
429
430
|
* Consume a purchase (Android only)
|
|
@@ -435,11 +436,11 @@ export const acknowledgePurchaseAndroid = async (
|
|
|
435
436
|
* ```
|
|
436
437
|
*/
|
|
437
438
|
export const consumePurchaseAndroid = async (
|
|
438
|
-
purchaseToken: string
|
|
439
|
+
purchaseToken: string,
|
|
439
440
|
): Promise<NitroPurchaseResult> => {
|
|
440
441
|
try {
|
|
441
442
|
if (Platform.OS !== 'android') {
|
|
442
|
-
throw new Error('consumePurchaseAndroid is only available on Android')
|
|
443
|
+
throw new Error('consumePurchaseAndroid is only available on Android');
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
const result = await iap.finishTransaction({
|
|
@@ -447,7 +448,7 @@ export const consumePurchaseAndroid = async (
|
|
|
447
448
|
purchaseToken,
|
|
448
449
|
isConsumable: true,
|
|
449
450
|
},
|
|
450
|
-
})
|
|
451
|
+
});
|
|
451
452
|
|
|
452
453
|
// Result is a variant, extract PurchaseResult
|
|
453
454
|
if (typeof result === 'boolean') {
|
|
@@ -457,21 +458,21 @@ export const consumePurchaseAndroid = async (
|
|
|
457
458
|
code: '0',
|
|
458
459
|
message: 'Success',
|
|
459
460
|
purchaseToken,
|
|
460
|
-
}
|
|
461
|
+
};
|
|
461
462
|
}
|
|
462
|
-
return result as NitroPurchaseResult
|
|
463
|
+
return result as NitroPurchaseResult;
|
|
463
464
|
} catch (error) {
|
|
464
|
-
console.error('Failed to consume purchase Android:', error)
|
|
465
|
-
throw error
|
|
465
|
+
console.error('Failed to consume purchase Android:', error);
|
|
466
|
+
throw error;
|
|
466
467
|
}
|
|
467
|
-
}
|
|
468
|
+
};
|
|
468
469
|
|
|
469
470
|
// ============================================================================
|
|
470
471
|
// EVENT LISTENERS
|
|
471
472
|
// ============================================================================
|
|
472
473
|
|
|
473
474
|
// Store wrapped listeners for proper removal
|
|
474
|
-
const listenerMap = new WeakMap<Function, Function>()
|
|
475
|
+
const listenerMap = new WeakMap<Function, Function>();
|
|
475
476
|
|
|
476
477
|
/**
|
|
477
478
|
* Purchase updated event listener
|
|
@@ -494,35 +495,35 @@ const listenerMap = new WeakMap<Function, Function>()
|
|
|
494
495
|
* ```
|
|
495
496
|
*/
|
|
496
497
|
export const purchaseUpdatedListener = (
|
|
497
|
-
listener: (purchase: Purchase) => void
|
|
498
|
+
listener: (purchase: Purchase) => void,
|
|
498
499
|
): EventSubscription => {
|
|
499
500
|
// Wrap the listener to convert NitroPurchase to Purchase
|
|
500
501
|
const wrappedListener = (nitroPurchase: any) => {
|
|
501
502
|
if (validateNitroPurchase(nitroPurchase)) {
|
|
502
|
-
const convertedPurchase = convertNitroPurchaseToPurchase(nitroPurchase)
|
|
503
|
-
listener(convertedPurchase)
|
|
503
|
+
const convertedPurchase = convertNitroPurchaseToPurchase(nitroPurchase);
|
|
504
|
+
listener(convertedPurchase);
|
|
504
505
|
} else {
|
|
505
506
|
console.error(
|
|
506
507
|
'Invalid purchase data received from native:',
|
|
507
|
-
nitroPurchase
|
|
508
|
-
)
|
|
508
|
+
nitroPurchase,
|
|
509
|
+
);
|
|
509
510
|
}
|
|
510
|
-
}
|
|
511
|
+
};
|
|
511
512
|
|
|
512
513
|
// Store the wrapped listener for removal
|
|
513
|
-
listenerMap.set(listener, wrappedListener)
|
|
514
|
-
iap.addPurchaseUpdatedListener(wrappedListener)
|
|
514
|
+
listenerMap.set(listener, wrappedListener);
|
|
515
|
+
iap.addPurchaseUpdatedListener(wrappedListener);
|
|
515
516
|
|
|
516
517
|
return {
|
|
517
518
|
remove: () => {
|
|
518
|
-
const wrapped = listenerMap.get(listener)
|
|
519
|
+
const wrapped = listenerMap.get(listener);
|
|
519
520
|
if (wrapped) {
|
|
520
|
-
iap.removePurchaseUpdatedListener(wrapped as any)
|
|
521
|
-
listenerMap.delete(listener)
|
|
521
|
+
iap.removePurchaseUpdatedListener(wrapped as any);
|
|
522
|
+
listenerMap.delete(listener);
|
|
522
523
|
}
|
|
523
524
|
},
|
|
524
|
-
}
|
|
525
|
-
}
|
|
525
|
+
};
|
|
526
|
+
};
|
|
526
527
|
|
|
527
528
|
/**
|
|
528
529
|
* Purchase error event listener
|
|
@@ -552,19 +553,19 @@ export const purchaseUpdatedListener = (
|
|
|
552
553
|
* ```
|
|
553
554
|
*/
|
|
554
555
|
export const purchaseErrorListener = (
|
|
555
|
-
listener: (error: NitroPurchaseResult) => void
|
|
556
|
+
listener: (error: NitroPurchaseResult) => void,
|
|
556
557
|
): EventSubscription => {
|
|
557
558
|
// Store the listener for removal
|
|
558
|
-
listenerMap.set(listener, listener)
|
|
559
|
-
iap.addPurchaseErrorListener(listener as any)
|
|
559
|
+
listenerMap.set(listener, listener);
|
|
560
|
+
iap.addPurchaseErrorListener(listener as any);
|
|
560
561
|
|
|
561
562
|
return {
|
|
562
563
|
remove: () => {
|
|
563
|
-
iap.removePurchaseErrorListener(listener as any)
|
|
564
|
-
listenerMap.delete(listener)
|
|
564
|
+
iap.removePurchaseErrorListener(listener as any);
|
|
565
|
+
listenerMap.delete(listener);
|
|
565
566
|
},
|
|
566
|
-
}
|
|
567
|
-
}
|
|
567
|
+
};
|
|
568
|
+
};
|
|
568
569
|
|
|
569
570
|
/**
|
|
570
571
|
* iOS-only listener for App Store promoted product events.
|
|
@@ -587,42 +588,42 @@ export const purchaseErrorListener = (
|
|
|
587
588
|
* @platform iOS
|
|
588
589
|
*/
|
|
589
590
|
export const promotedProductListenerIOS = (
|
|
590
|
-
listener: (product: Product) => void
|
|
591
|
+
listener: (product: Product) => void,
|
|
591
592
|
): EventSubscription => {
|
|
592
593
|
if (Platform.OS !== 'ios') {
|
|
593
594
|
console.warn(
|
|
594
|
-
'promotedProductListenerIOS: This listener is only available on iOS'
|
|
595
|
-
)
|
|
596
|
-
return {
|
|
595
|
+
'promotedProductListenerIOS: This listener is only available on iOS',
|
|
596
|
+
);
|
|
597
|
+
return {remove: () => {}};
|
|
597
598
|
}
|
|
598
599
|
|
|
599
600
|
// Wrap the listener to convert NitroProduct to Product
|
|
600
601
|
const wrappedListener = (nitroProduct: any) => {
|
|
601
602
|
if (validateNitroProduct(nitroProduct)) {
|
|
602
|
-
const convertedProduct = convertNitroProductToProduct(nitroProduct)
|
|
603
|
-
listener(convertedProduct)
|
|
603
|
+
const convertedProduct = convertNitroProductToProduct(nitroProduct);
|
|
604
|
+
listener(convertedProduct);
|
|
604
605
|
} else {
|
|
605
606
|
console.error(
|
|
606
607
|
'Invalid promoted product data received from native:',
|
|
607
|
-
nitroProduct
|
|
608
|
-
)
|
|
608
|
+
nitroProduct,
|
|
609
|
+
);
|
|
609
610
|
}
|
|
610
|
-
}
|
|
611
|
+
};
|
|
611
612
|
|
|
612
613
|
// Store the wrapped listener for removal
|
|
613
|
-
listenerMap.set(listener, wrappedListener)
|
|
614
|
-
iap.addPromotedProductListenerIOS(wrappedListener)
|
|
614
|
+
listenerMap.set(listener, wrappedListener);
|
|
615
|
+
iap.addPromotedProductListenerIOS(wrappedListener);
|
|
615
616
|
|
|
616
617
|
return {
|
|
617
618
|
remove: () => {
|
|
618
|
-
const wrapped = listenerMap.get(listener)
|
|
619
|
+
const wrapped = listenerMap.get(listener);
|
|
619
620
|
if (wrapped) {
|
|
620
|
-
iap.removePromotedProductListenerIOS(wrapped as any)
|
|
621
|
-
listenerMap.delete(listener)
|
|
621
|
+
iap.removePromotedProductListenerIOS(wrapped as any);
|
|
622
|
+
listenerMap.delete(listener);
|
|
622
623
|
}
|
|
623
624
|
},
|
|
624
|
-
}
|
|
625
|
-
}
|
|
625
|
+
};
|
|
626
|
+
};
|
|
626
627
|
|
|
627
628
|
// ============================================================================
|
|
628
629
|
// iOS-SPECIFIC FUNCTIONS
|
|
@@ -637,30 +638,30 @@ export const promotedProductListenerIOS = (
|
|
|
637
638
|
export const validateReceipt = async (
|
|
638
639
|
sku: string,
|
|
639
640
|
androidOptions?: {
|
|
640
|
-
packageName: string
|
|
641
|
-
productToken: string
|
|
642
|
-
accessToken: string
|
|
643
|
-
isSub?: boolean
|
|
644
|
-
}
|
|
641
|
+
packageName: string;
|
|
642
|
+
productToken: string;
|
|
643
|
+
accessToken: string;
|
|
644
|
+
isSub?: boolean;
|
|
645
|
+
},
|
|
645
646
|
): Promise<import('./types').ReceiptValidationResult> => {
|
|
646
647
|
if (!iap) {
|
|
647
648
|
const errorJson = parseErrorStringToJsonObj(
|
|
648
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
649
|
-
)
|
|
650
|
-
throw new Error(errorJson.message)
|
|
649
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
650
|
+
);
|
|
651
|
+
throw new Error(errorJson.message);
|
|
651
652
|
}
|
|
652
653
|
|
|
653
654
|
try {
|
|
654
655
|
const params: NitroReceiptValidationParams = {
|
|
655
656
|
sku,
|
|
656
657
|
androidOptions,
|
|
657
|
-
}
|
|
658
|
+
};
|
|
658
659
|
|
|
659
|
-
const nitroResult = await iap.validateReceipt(params)
|
|
660
|
+
const nitroResult = await iap.validateReceipt(params);
|
|
660
661
|
|
|
661
662
|
// Convert Nitro result to public API result
|
|
662
663
|
if (Platform.OS === 'ios') {
|
|
663
|
-
const iosResult = nitroResult as NitroReceiptValidationResultIOS
|
|
664
|
+
const iosResult = nitroResult as NitroReceiptValidationResultIOS;
|
|
664
665
|
const result: ReceiptValidationResultIOS = {
|
|
665
666
|
isValid: iosResult.isValid,
|
|
666
667
|
receiptData: iosResult.receiptData,
|
|
@@ -668,11 +669,11 @@ export const validateReceipt = async (
|
|
|
668
669
|
latestTransaction: iosResult.latestTransaction
|
|
669
670
|
? convertNitroPurchaseToPurchase(iosResult.latestTransaction)
|
|
670
671
|
: undefined,
|
|
671
|
-
}
|
|
672
|
-
return result
|
|
672
|
+
};
|
|
673
|
+
return result;
|
|
673
674
|
} else {
|
|
674
675
|
// Android
|
|
675
|
-
const androidResult = nitroResult as NitroReceiptValidationResultAndroid
|
|
676
|
+
const androidResult = nitroResult as NitroReceiptValidationResultAndroid;
|
|
676
677
|
const result: ReceiptValidationResultAndroid = {
|
|
677
678
|
autoRenewing: androidResult.autoRenewing,
|
|
678
679
|
betaProduct: androidResult.betaProduct,
|
|
@@ -684,7 +685,7 @@ export const validateReceipt = async (
|
|
|
684
685
|
gracePeriodEndDate: androidResult.gracePeriodEndDate,
|
|
685
686
|
parentProductId: androidResult.parentProductId,
|
|
686
687
|
productId: androidResult.productId,
|
|
687
|
-
productType: androidResult.productType === 'subs' ? 'subs' : 'inapp',
|
|
688
|
+
productType: (androidResult.productType === 'subs' ? 'subs' : 'inapp'),
|
|
688
689
|
purchaseDate: androidResult.purchaseDate,
|
|
689
690
|
quantity: androidResult.quantity,
|
|
690
691
|
receiptId: androidResult.receiptId,
|
|
@@ -692,15 +693,15 @@ export const validateReceipt = async (
|
|
|
692
693
|
term: androidResult.term,
|
|
693
694
|
termSku: androidResult.termSku,
|
|
694
695
|
testTransaction: androidResult.testTransaction,
|
|
695
|
-
}
|
|
696
|
-
return result
|
|
696
|
+
};
|
|
697
|
+
return result;
|
|
697
698
|
}
|
|
698
699
|
} catch (error) {
|
|
699
|
-
console.error('[validateReceipt] Failed:', error)
|
|
700
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
701
|
-
throw new Error(errorJson.message)
|
|
700
|
+
console.error('[validateReceipt] Failed:', error);
|
|
701
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
702
|
+
throw new Error(errorJson.message);
|
|
702
703
|
}
|
|
703
|
-
}
|
|
704
|
+
};
|
|
704
705
|
|
|
705
706
|
/**
|
|
706
707
|
* Sync iOS purchases with App Store (iOS only)
|
|
@@ -709,24 +710,24 @@ export const validateReceipt = async (
|
|
|
709
710
|
*/
|
|
710
711
|
export const syncIOS = async (): Promise<boolean> => {
|
|
711
712
|
if (Platform.OS !== 'ios') {
|
|
712
|
-
throw new Error('syncIOS is only available on iOS')
|
|
713
|
+
throw new Error('syncIOS is only available on iOS');
|
|
713
714
|
}
|
|
714
715
|
|
|
715
716
|
if (!iap) {
|
|
716
717
|
const errorJson = parseErrorStringToJsonObj(
|
|
717
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
718
|
-
)
|
|
719
|
-
throw new Error(errorJson.message)
|
|
718
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
719
|
+
);
|
|
720
|
+
throw new Error(errorJson.message);
|
|
720
721
|
}
|
|
721
722
|
|
|
722
723
|
try {
|
|
723
|
-
return await iap.syncIOS()
|
|
724
|
+
return await iap.syncIOS();
|
|
724
725
|
} catch (error) {
|
|
725
|
-
console.error('[syncIOS] Failed:', error)
|
|
726
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
727
|
-
throw new Error(errorJson.message)
|
|
726
|
+
console.error('[syncIOS] Failed:', error);
|
|
727
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
728
|
+
throw new Error(errorJson.message);
|
|
728
729
|
}
|
|
729
|
-
}
|
|
730
|
+
};
|
|
730
731
|
|
|
731
732
|
/**
|
|
732
733
|
* Request the promoted product from the App Store (iOS only)
|
|
@@ -735,28 +736,28 @@ export const syncIOS = async (): Promise<boolean> => {
|
|
|
735
736
|
*/
|
|
736
737
|
export const requestPromotedProductIOS = async (): Promise<Product | null> => {
|
|
737
738
|
if (Platform.OS !== 'ios') {
|
|
738
|
-
return null
|
|
739
|
+
return null;
|
|
739
740
|
}
|
|
740
741
|
|
|
741
742
|
if (!iap) {
|
|
742
743
|
const errorJson = parseErrorStringToJsonObj(
|
|
743
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
744
|
-
)
|
|
745
|
-
throw new Error(errorJson.message)
|
|
744
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
745
|
+
);
|
|
746
|
+
throw new Error(errorJson.message);
|
|
746
747
|
}
|
|
747
748
|
|
|
748
749
|
try {
|
|
749
|
-
const nitroProduct = await iap.requestPromotedProductIOS()
|
|
750
|
+
const nitroProduct = await iap.requestPromotedProductIOS();
|
|
750
751
|
if (nitroProduct) {
|
|
751
|
-
return convertNitroProductToProduct(nitroProduct)
|
|
752
|
+
return convertNitroProductToProduct(nitroProduct);
|
|
752
753
|
}
|
|
753
|
-
return null
|
|
754
|
+
return null;
|
|
754
755
|
} catch (error) {
|
|
755
|
-
console.error('[getPromotedProductIOS] Failed:', error)
|
|
756
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
757
|
-
throw new Error(errorJson.message)
|
|
756
|
+
console.error('[getPromotedProductIOS] Failed:', error);
|
|
757
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
758
|
+
throw new Error(errorJson.message);
|
|
758
759
|
}
|
|
759
|
-
}
|
|
760
|
+
};
|
|
760
761
|
|
|
761
762
|
/**
|
|
762
763
|
* Present the code redemption sheet for offer codes (iOS only)
|
|
@@ -765,24 +766,24 @@ export const requestPromotedProductIOS = async (): Promise<Product | null> => {
|
|
|
765
766
|
*/
|
|
766
767
|
export const presentCodeRedemptionSheetIOS = async (): Promise<boolean> => {
|
|
767
768
|
if (Platform.OS !== 'ios') {
|
|
768
|
-
return false
|
|
769
|
+
return false;
|
|
769
770
|
}
|
|
770
771
|
|
|
771
772
|
if (!iap) {
|
|
772
773
|
const errorJson = parseErrorStringToJsonObj(
|
|
773
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
774
|
-
)
|
|
775
|
-
throw new Error(errorJson.message)
|
|
774
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
775
|
+
);
|
|
776
|
+
throw new Error(errorJson.message);
|
|
776
777
|
}
|
|
777
778
|
|
|
778
779
|
try {
|
|
779
|
-
return await iap.presentCodeRedemptionSheetIOS()
|
|
780
|
+
return await iap.presentCodeRedemptionSheetIOS();
|
|
780
781
|
} catch (error) {
|
|
781
|
-
console.error('[presentCodeRedemptionSheetIOS] Failed:', error)
|
|
782
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
783
|
-
throw new Error(errorJson.message)
|
|
782
|
+
console.error('[presentCodeRedemptionSheetIOS] Failed:', error);
|
|
783
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
784
|
+
throw new Error(errorJson.message);
|
|
784
785
|
}
|
|
785
|
-
}
|
|
786
|
+
};
|
|
786
787
|
|
|
787
788
|
/**
|
|
788
789
|
* Buy promoted product on iOS
|
|
@@ -791,24 +792,24 @@ export const presentCodeRedemptionSheetIOS = async (): Promise<boolean> => {
|
|
|
791
792
|
*/
|
|
792
793
|
export const buyPromotedProductIOS = async (): Promise<void> => {
|
|
793
794
|
if (Platform.OS !== 'ios') {
|
|
794
|
-
throw new Error('buyPromotedProductIOS is only available on iOS')
|
|
795
|
+
throw new Error('buyPromotedProductIOS is only available on iOS');
|
|
795
796
|
}
|
|
796
797
|
|
|
797
798
|
if (!iap) {
|
|
798
799
|
const errorJson = parseErrorStringToJsonObj(
|
|
799
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
800
|
-
)
|
|
801
|
-
throw new Error(errorJson.message)
|
|
800
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
801
|
+
);
|
|
802
|
+
throw new Error(errorJson.message);
|
|
802
803
|
}
|
|
803
804
|
|
|
804
805
|
try {
|
|
805
|
-
await iap.buyPromotedProductIOS()
|
|
806
|
+
await iap.buyPromotedProductIOS();
|
|
806
807
|
} catch (error) {
|
|
807
|
-
console.error('[buyPromotedProductIOS] Failed:', error)
|
|
808
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
809
|
-
throw new Error(errorJson.message)
|
|
808
|
+
console.error('[buyPromotedProductIOS] Failed:', error);
|
|
809
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
810
|
+
throw new Error(errorJson.message);
|
|
810
811
|
}
|
|
811
|
-
}
|
|
812
|
+
};
|
|
812
813
|
|
|
813
814
|
/**
|
|
814
815
|
* Clear unfinished transactions on iOS
|
|
@@ -817,24 +818,24 @@ export const buyPromotedProductIOS = async (): Promise<void> => {
|
|
|
817
818
|
*/
|
|
818
819
|
export const clearTransactionIOS = async (): Promise<void> => {
|
|
819
820
|
if (Platform.OS !== 'ios') {
|
|
820
|
-
return
|
|
821
|
+
return;
|
|
821
822
|
}
|
|
822
823
|
|
|
823
824
|
if (!iap) {
|
|
824
825
|
const errorJson = parseErrorStringToJsonObj(
|
|
825
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
826
|
-
)
|
|
827
|
-
throw new Error(errorJson.message)
|
|
826
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
827
|
+
);
|
|
828
|
+
throw new Error(errorJson.message);
|
|
828
829
|
}
|
|
829
830
|
|
|
830
831
|
try {
|
|
831
|
-
await iap.clearTransactionIOS()
|
|
832
|
+
await iap.clearTransactionIOS();
|
|
832
833
|
} catch (error) {
|
|
833
|
-
console.error('[clearTransactionIOS] Failed:', error)
|
|
834
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
835
|
-
throw new Error(errorJson.message)
|
|
834
|
+
console.error('[clearTransactionIOS] Failed:', error);
|
|
835
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
836
|
+
throw new Error(errorJson.message);
|
|
836
837
|
}
|
|
837
|
-
}
|
|
838
|
+
};
|
|
838
839
|
|
|
839
840
|
/**
|
|
840
841
|
* Begin a refund request for a product on iOS 15+
|
|
@@ -843,27 +844,27 @@ export const clearTransactionIOS = async (): Promise<void> => {
|
|
|
843
844
|
* @platform iOS
|
|
844
845
|
*/
|
|
845
846
|
export const beginRefundRequestIOS = async (
|
|
846
|
-
sku: string
|
|
847
|
+
sku: string,
|
|
847
848
|
): Promise<string | null> => {
|
|
848
849
|
if (Platform.OS !== 'ios') {
|
|
849
|
-
return null
|
|
850
|
+
return null;
|
|
850
851
|
}
|
|
851
852
|
|
|
852
853
|
if (!iap) {
|
|
853
854
|
const errorJson = parseErrorStringToJsonObj(
|
|
854
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
855
|
-
)
|
|
856
|
-
throw new Error(errorJson.message)
|
|
855
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
856
|
+
);
|
|
857
|
+
throw new Error(errorJson.message);
|
|
857
858
|
}
|
|
858
859
|
|
|
859
860
|
try {
|
|
860
|
-
return await iap.beginRefundRequestIOS(sku)
|
|
861
|
+
return await iap.beginRefundRequestIOS(sku);
|
|
861
862
|
} catch (error) {
|
|
862
|
-
console.error('[beginRefundRequestIOS] Failed:', error)
|
|
863
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
864
|
-
throw new Error(errorJson.message)
|
|
863
|
+
console.error('[beginRefundRequestIOS] Failed:', error);
|
|
864
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
865
|
+
throw new Error(errorJson.message);
|
|
865
866
|
}
|
|
866
|
-
}
|
|
867
|
+
};
|
|
867
868
|
|
|
868
869
|
/**
|
|
869
870
|
* Get subscription status for a product (iOS only)
|
|
@@ -873,25 +874,25 @@ export const beginRefundRequestIOS = async (
|
|
|
873
874
|
*/
|
|
874
875
|
export const subscriptionStatusIOS = async (sku: string): Promise<any[]> => {
|
|
875
876
|
if (Platform.OS !== 'ios') {
|
|
876
|
-
throw new Error('subscriptionStatusIOS is only available on iOS')
|
|
877
|
+
throw new Error('subscriptionStatusIOS is only available on iOS');
|
|
877
878
|
}
|
|
878
879
|
|
|
879
880
|
if (!iap) {
|
|
880
881
|
const errorJson = parseErrorStringToJsonObj(
|
|
881
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
882
|
-
)
|
|
883
|
-
throw new Error(errorJson.message)
|
|
882
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
883
|
+
);
|
|
884
|
+
throw new Error(errorJson.message);
|
|
884
885
|
}
|
|
885
886
|
|
|
886
887
|
try {
|
|
887
|
-
const statuses = await iap.subscriptionStatusIOS(sku)
|
|
888
|
-
return statuses || []
|
|
888
|
+
const statuses = await iap.subscriptionStatusIOS(sku);
|
|
889
|
+
return statuses || [];
|
|
889
890
|
} catch (error) {
|
|
890
|
-
console.error('[subscriptionStatusIOS] Failed:', error)
|
|
891
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
892
|
-
throw new Error(errorJson.message)
|
|
891
|
+
console.error('[subscriptionStatusIOS] Failed:', error);
|
|
892
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
893
|
+
throw new Error(errorJson.message);
|
|
893
894
|
}
|
|
894
|
-
}
|
|
895
|
+
};
|
|
895
896
|
|
|
896
897
|
/**
|
|
897
898
|
* Get current entitlement for a product (iOS only)
|
|
@@ -900,31 +901,31 @@ export const subscriptionStatusIOS = async (sku: string): Promise<any[]> => {
|
|
|
900
901
|
* @platform iOS
|
|
901
902
|
*/
|
|
902
903
|
export const currentEntitlementIOS = async (
|
|
903
|
-
sku: string
|
|
904
|
+
sku: string,
|
|
904
905
|
): Promise<Purchase | null> => {
|
|
905
906
|
if (Platform.OS !== 'ios') {
|
|
906
|
-
return null
|
|
907
|
+
return null;
|
|
907
908
|
}
|
|
908
909
|
|
|
909
910
|
if (!iap) {
|
|
910
911
|
const errorJson = parseErrorStringToJsonObj(
|
|
911
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
912
|
-
)
|
|
913
|
-
throw new Error(errorJson.message)
|
|
912
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
913
|
+
);
|
|
914
|
+
throw new Error(errorJson.message);
|
|
914
915
|
}
|
|
915
916
|
|
|
916
917
|
try {
|
|
917
|
-
const nitroPurchase = await iap.currentEntitlementIOS(sku)
|
|
918
|
+
const nitroPurchase = await iap.currentEntitlementIOS(sku);
|
|
918
919
|
if (nitroPurchase) {
|
|
919
|
-
return convertNitroPurchaseToPurchase(nitroPurchase)
|
|
920
|
+
return convertNitroPurchaseToPurchase(nitroPurchase);
|
|
920
921
|
}
|
|
921
|
-
return null
|
|
922
|
+
return null;
|
|
922
923
|
} catch (error) {
|
|
923
|
-
console.error('[currentEntitlementIOS] Failed:', error)
|
|
924
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
925
|
-
throw new Error(errorJson.message)
|
|
924
|
+
console.error('[currentEntitlementIOS] Failed:', error);
|
|
925
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
926
|
+
throw new Error(errorJson.message);
|
|
926
927
|
}
|
|
927
|
-
}
|
|
928
|
+
};
|
|
928
929
|
|
|
929
930
|
/**
|
|
930
931
|
* Get latest transaction for a product (iOS only)
|
|
@@ -933,31 +934,31 @@ export const currentEntitlementIOS = async (
|
|
|
933
934
|
* @platform iOS
|
|
934
935
|
*/
|
|
935
936
|
export const latestTransactionIOS = async (
|
|
936
|
-
sku: string
|
|
937
|
+
sku: string,
|
|
937
938
|
): Promise<Purchase | null> => {
|
|
938
939
|
if (Platform.OS !== 'ios') {
|
|
939
|
-
return null
|
|
940
|
+
return null;
|
|
940
941
|
}
|
|
941
942
|
|
|
942
943
|
if (!iap) {
|
|
943
944
|
const errorJson = parseErrorStringToJsonObj(
|
|
944
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
945
|
-
)
|
|
946
|
-
throw new Error(errorJson.message)
|
|
945
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
946
|
+
);
|
|
947
|
+
throw new Error(errorJson.message);
|
|
947
948
|
}
|
|
948
949
|
|
|
949
950
|
try {
|
|
950
|
-
const nitroPurchase = await iap.latestTransactionIOS(sku)
|
|
951
|
+
const nitroPurchase = await iap.latestTransactionIOS(sku);
|
|
951
952
|
if (nitroPurchase) {
|
|
952
|
-
return convertNitroPurchaseToPurchase(nitroPurchase)
|
|
953
|
+
return convertNitroPurchaseToPurchase(nitroPurchase);
|
|
953
954
|
}
|
|
954
|
-
return null
|
|
955
|
+
return null;
|
|
955
956
|
} catch (error) {
|
|
956
|
-
console.error('[latestTransactionIOS] Failed:', error)
|
|
957
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
958
|
-
throw new Error(errorJson.message)
|
|
957
|
+
console.error('[latestTransactionIOS] Failed:', error);
|
|
958
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
959
|
+
throw new Error(errorJson.message);
|
|
959
960
|
}
|
|
960
|
-
}
|
|
961
|
+
};
|
|
961
962
|
|
|
962
963
|
/**
|
|
963
964
|
* Get pending transactions (iOS only)
|
|
@@ -966,25 +967,25 @@ export const latestTransactionIOS = async (
|
|
|
966
967
|
*/
|
|
967
968
|
export const getPendingTransactionsIOS = async (): Promise<Purchase[]> => {
|
|
968
969
|
if (Platform.OS !== 'ios') {
|
|
969
|
-
return []
|
|
970
|
+
return [];
|
|
970
971
|
}
|
|
971
972
|
|
|
972
973
|
if (!iap) {
|
|
973
974
|
const errorJson = parseErrorStringToJsonObj(
|
|
974
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
975
|
-
)
|
|
976
|
-
throw new Error(errorJson.message)
|
|
975
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
976
|
+
);
|
|
977
|
+
throw new Error(errorJson.message);
|
|
977
978
|
}
|
|
978
979
|
|
|
979
980
|
try {
|
|
980
|
-
const nitroPurchases = await iap.getPendingTransactionsIOS()
|
|
981
|
-
return nitroPurchases.map(convertNitroPurchaseToPurchase)
|
|
981
|
+
const nitroPurchases = await iap.getPendingTransactionsIOS();
|
|
982
|
+
return nitroPurchases.map(convertNitroPurchaseToPurchase);
|
|
982
983
|
} catch (error) {
|
|
983
|
-
console.error('[getPendingTransactionsIOS] Failed:', error)
|
|
984
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
985
|
-
throw new Error(errorJson.message)
|
|
984
|
+
console.error('[getPendingTransactionsIOS] Failed:', error);
|
|
985
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
986
|
+
throw new Error(errorJson.message);
|
|
986
987
|
}
|
|
987
|
-
}
|
|
988
|
+
};
|
|
988
989
|
|
|
989
990
|
/**
|
|
990
991
|
* Show manage subscriptions screen (iOS only)
|
|
@@ -993,25 +994,25 @@ export const getPendingTransactionsIOS = async (): Promise<Purchase[]> => {
|
|
|
993
994
|
*/
|
|
994
995
|
export const showManageSubscriptionsIOS = async (): Promise<Purchase[]> => {
|
|
995
996
|
if (Platform.OS !== 'ios') {
|
|
996
|
-
return []
|
|
997
|
+
return [];
|
|
997
998
|
}
|
|
998
999
|
|
|
999
1000
|
if (!iap) {
|
|
1000
1001
|
const errorJson = parseErrorStringToJsonObj(
|
|
1001
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
1002
|
-
)
|
|
1003
|
-
throw new Error(errorJson.message)
|
|
1002
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
1003
|
+
);
|
|
1004
|
+
throw new Error(errorJson.message);
|
|
1004
1005
|
}
|
|
1005
1006
|
|
|
1006
1007
|
try {
|
|
1007
|
-
const nitroPurchases = await iap.showManageSubscriptionsIOS()
|
|
1008
|
-
return nitroPurchases.map(convertNitroPurchaseToPurchase)
|
|
1008
|
+
const nitroPurchases = await iap.showManageSubscriptionsIOS();
|
|
1009
|
+
return nitroPurchases.map(convertNitroPurchaseToPurchase);
|
|
1009
1010
|
} catch (error) {
|
|
1010
|
-
console.error('[showManageSubscriptionsIOS] Failed:', error)
|
|
1011
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
1012
|
-
throw new Error(errorJson.message)
|
|
1011
|
+
console.error('[showManageSubscriptionsIOS] Failed:', error);
|
|
1012
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
1013
|
+
throw new Error(errorJson.message);
|
|
1013
1014
|
}
|
|
1014
|
-
}
|
|
1015
|
+
};
|
|
1015
1016
|
|
|
1016
1017
|
/**
|
|
1017
1018
|
* Check if user is eligible for intro offer (iOS only)
|
|
@@ -1020,27 +1021,27 @@ export const showManageSubscriptionsIOS = async (): Promise<Purchase[]> => {
|
|
|
1020
1021
|
* @platform iOS
|
|
1021
1022
|
*/
|
|
1022
1023
|
export const isEligibleForIntroOfferIOS = async (
|
|
1023
|
-
groupID: string
|
|
1024
|
+
groupID: string,
|
|
1024
1025
|
): Promise<boolean> => {
|
|
1025
1026
|
if (Platform.OS !== 'ios') {
|
|
1026
|
-
return false
|
|
1027
|
+
return false;
|
|
1027
1028
|
}
|
|
1028
1029
|
|
|
1029
1030
|
if (!iap) {
|
|
1030
1031
|
const errorJson = parseErrorStringToJsonObj(
|
|
1031
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
1032
|
-
)
|
|
1033
|
-
throw new Error(errorJson.message)
|
|
1032
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
1033
|
+
);
|
|
1034
|
+
throw new Error(errorJson.message);
|
|
1034
1035
|
}
|
|
1035
1036
|
|
|
1036
1037
|
try {
|
|
1037
|
-
return await iap.isEligibleForIntroOfferIOS(groupID)
|
|
1038
|
+
return await iap.isEligibleForIntroOfferIOS(groupID);
|
|
1038
1039
|
} catch (error) {
|
|
1039
|
-
console.error('[isEligibleForIntroOfferIOS] Failed:', error)
|
|
1040
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
1041
|
-
throw new Error(errorJson.message)
|
|
1040
|
+
console.error('[isEligibleForIntroOfferIOS] Failed:', error);
|
|
1041
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
1042
|
+
throw new Error(errorJson.message);
|
|
1042
1043
|
}
|
|
1043
|
-
}
|
|
1044
|
+
};
|
|
1044
1045
|
|
|
1045
1046
|
/**
|
|
1046
1047
|
* Get receipt data (iOS only)
|
|
@@ -1049,24 +1050,24 @@ export const isEligibleForIntroOfferIOS = async (
|
|
|
1049
1050
|
*/
|
|
1050
1051
|
export const getReceiptDataIOS = async (): Promise<string> => {
|
|
1051
1052
|
if (Platform.OS !== 'ios') {
|
|
1052
|
-
throw new Error('getReceiptDataIOS is only available on iOS')
|
|
1053
|
+
throw new Error('getReceiptDataIOS is only available on iOS');
|
|
1053
1054
|
}
|
|
1054
1055
|
|
|
1055
1056
|
if (!iap) {
|
|
1056
1057
|
const errorJson = parseErrorStringToJsonObj(
|
|
1057
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
1058
|
-
)
|
|
1059
|
-
throw new Error(errorJson.message)
|
|
1058
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
1059
|
+
);
|
|
1060
|
+
throw new Error(errorJson.message);
|
|
1060
1061
|
}
|
|
1061
1062
|
|
|
1062
1063
|
try {
|
|
1063
|
-
return await iap.getReceiptDataIOS()
|
|
1064
|
+
return await iap.getReceiptDataIOS();
|
|
1064
1065
|
} catch (error) {
|
|
1065
|
-
console.error('[getReceiptDataIOS] Failed:', error)
|
|
1066
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
1067
|
-
throw new Error(errorJson.message)
|
|
1066
|
+
console.error('[getReceiptDataIOS] Failed:', error);
|
|
1067
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
1068
|
+
throw new Error(errorJson.message);
|
|
1068
1069
|
}
|
|
1069
|
-
}
|
|
1070
|
+
};
|
|
1070
1071
|
|
|
1071
1072
|
/**
|
|
1072
1073
|
* Check if transaction is verified (iOS only)
|
|
@@ -1075,27 +1076,27 @@ export const getReceiptDataIOS = async (): Promise<string> => {
|
|
|
1075
1076
|
* @platform iOS
|
|
1076
1077
|
*/
|
|
1077
1078
|
export const isTransactionVerifiedIOS = async (
|
|
1078
|
-
sku: string
|
|
1079
|
+
sku: string,
|
|
1079
1080
|
): Promise<boolean> => {
|
|
1080
1081
|
if (Platform.OS !== 'ios') {
|
|
1081
|
-
return false
|
|
1082
|
+
return false;
|
|
1082
1083
|
}
|
|
1083
1084
|
|
|
1084
1085
|
if (!iap) {
|
|
1085
1086
|
const errorJson = parseErrorStringToJsonObj(
|
|
1086
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
1087
|
-
)
|
|
1088
|
-
throw new Error(errorJson.message)
|
|
1087
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
1088
|
+
);
|
|
1089
|
+
throw new Error(errorJson.message);
|
|
1089
1090
|
}
|
|
1090
1091
|
|
|
1091
1092
|
try {
|
|
1092
|
-
return await iap.isTransactionVerifiedIOS(sku)
|
|
1093
|
+
return await iap.isTransactionVerifiedIOS(sku);
|
|
1093
1094
|
} catch (error) {
|
|
1094
|
-
console.error('[isTransactionVerifiedIOS] Failed:', error)
|
|
1095
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
1096
|
-
throw new Error(errorJson.message)
|
|
1095
|
+
console.error('[isTransactionVerifiedIOS] Failed:', error);
|
|
1096
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
1097
|
+
throw new Error(errorJson.message);
|
|
1097
1098
|
}
|
|
1098
|
-
}
|
|
1099
|
+
};
|
|
1099
1100
|
|
|
1100
1101
|
/**
|
|
1101
1102
|
* Get transaction JWS representation (iOS only)
|
|
@@ -1104,27 +1105,27 @@ export const isTransactionVerifiedIOS = async (
|
|
|
1104
1105
|
* @platform iOS
|
|
1105
1106
|
*/
|
|
1106
1107
|
export const getTransactionJwsIOS = async (
|
|
1107
|
-
sku: string
|
|
1108
|
+
sku: string,
|
|
1108
1109
|
): Promise<string | null> => {
|
|
1109
1110
|
if (Platform.OS !== 'ios') {
|
|
1110
|
-
return null
|
|
1111
|
+
return null;
|
|
1111
1112
|
}
|
|
1112
1113
|
|
|
1113
1114
|
if (!iap) {
|
|
1114
1115
|
const errorJson = parseErrorStringToJsonObj(
|
|
1115
|
-
'RnIap: Service not initialized. Call initConnection() first.'
|
|
1116
|
-
)
|
|
1117
|
-
throw new Error(errorJson.message)
|
|
1116
|
+
'RnIap: Service not initialized. Call initConnection() first.',
|
|
1117
|
+
);
|
|
1118
|
+
throw new Error(errorJson.message);
|
|
1118
1119
|
}
|
|
1119
1120
|
|
|
1120
1121
|
try {
|
|
1121
|
-
return await iap.getTransactionJwsIOS(sku)
|
|
1122
|
+
return await iap.getTransactionJwsIOS(sku);
|
|
1122
1123
|
} catch (error) {
|
|
1123
|
-
console.error('[getTransactionJwsIOS] Failed:', error)
|
|
1124
|
-
const errorJson = parseErrorStringToJsonObj(error)
|
|
1125
|
-
throw new Error(errorJson.message)
|
|
1124
|
+
console.error('[getTransactionJwsIOS] Failed:', error);
|
|
1125
|
+
const errorJson = parseErrorStringToJsonObj(error);
|
|
1126
|
+
throw new Error(errorJson.message);
|
|
1126
1127
|
}
|
|
1127
|
-
}
|
|
1128
|
+
};
|
|
1128
1129
|
|
|
1129
1130
|
/**
|
|
1130
1131
|
* Get the storefront identifier for the user's App Store account (iOS only)
|
|
@@ -1139,18 +1140,18 @@ export const getTransactionJwsIOS = async (
|
|
|
1139
1140
|
*/
|
|
1140
1141
|
export const getStorefrontIOS = async (): Promise<string> => {
|
|
1141
1142
|
if (Platform.OS !== 'ios') {
|
|
1142
|
-
throw new Error('getStorefrontIOS is only available on iOS')
|
|
1143
|
+
throw new Error('getStorefrontIOS is only available on iOS');
|
|
1143
1144
|
}
|
|
1144
1145
|
|
|
1145
1146
|
try {
|
|
1146
1147
|
// Call the native method to get storefront
|
|
1147
|
-
const storefront = await iap.getStorefrontIOS()
|
|
1148
|
-
return storefront
|
|
1148
|
+
const storefront = await iap.getStorefrontIOS();
|
|
1149
|
+
return storefront;
|
|
1149
1150
|
} catch (error) {
|
|
1150
|
-
console.error('Failed to get storefront:', error)
|
|
1151
|
-
throw error
|
|
1151
|
+
console.error('Failed to get storefront:', error);
|
|
1152
|
+
throw error;
|
|
1152
1153
|
}
|
|
1153
|
-
}
|
|
1154
|
+
};
|
|
1154
1155
|
|
|
1155
1156
|
/**
|
|
1156
1157
|
* iOS only - Gets the original app transaction ID if the app was purchased from the App Store
|
|
@@ -1173,24 +1174,24 @@ export const getStorefrontIOS = async (): Promise<string> => {
|
|
|
1173
1174
|
*/
|
|
1174
1175
|
export const getAppTransactionIOS = async (): Promise<string | null> => {
|
|
1175
1176
|
if (Platform.OS !== 'ios') {
|
|
1176
|
-
throw new Error('getAppTransactionIOS is only available on iOS')
|
|
1177
|
+
throw new Error('getAppTransactionIOS is only available on iOS');
|
|
1177
1178
|
}
|
|
1178
1179
|
|
|
1179
1180
|
try {
|
|
1180
1181
|
// Call the native method to get app transaction
|
|
1181
|
-
const appTransaction = await iap.getAppTransactionIOS()
|
|
1182
|
-
return appTransaction
|
|
1182
|
+
const appTransaction = await iap.getAppTransactionIOS();
|
|
1183
|
+
return appTransaction;
|
|
1183
1184
|
} catch (error) {
|
|
1184
|
-
console.error('Failed to get app transaction:', error)
|
|
1185
|
-
throw error
|
|
1185
|
+
console.error('Failed to get app transaction:', error);
|
|
1186
|
+
throw error;
|
|
1186
1187
|
}
|
|
1187
|
-
}
|
|
1188
|
+
};
|
|
1188
1189
|
|
|
1189
1190
|
// Export subscription helpers
|
|
1190
1191
|
export {
|
|
1191
1192
|
getActiveSubscriptions,
|
|
1192
1193
|
hasActiveSubscriptions,
|
|
1193
|
-
} from './helpers/subscription'
|
|
1194
|
+
} from './helpers/subscription';
|
|
1194
1195
|
|
|
1195
1196
|
// Type conversion utilities
|
|
1196
1197
|
export {
|
|
@@ -1200,15 +1201,15 @@ export {
|
|
|
1200
1201
|
validateNitroProduct,
|
|
1201
1202
|
validateNitroPurchase,
|
|
1202
1203
|
checkTypeSynchronization,
|
|
1203
|
-
} from './utils/type-bridge'
|
|
1204
|
+
} from './utils/type-bridge';
|
|
1204
1205
|
|
|
1205
1206
|
// Deprecated exports for backward compatibility
|
|
1206
1207
|
/**
|
|
1207
1208
|
* @deprecated Use acknowledgePurchaseAndroid instead
|
|
1208
1209
|
*/
|
|
1209
|
-
export const acknowledgePurchase = acknowledgePurchaseAndroid
|
|
1210
|
+
export const acknowledgePurchase = acknowledgePurchaseAndroid;
|
|
1210
1211
|
|
|
1211
1212
|
/**
|
|
1212
1213
|
* @deprecated Use consumePurchaseAndroid instead
|
|
1213
1214
|
*/
|
|
1214
|
-
export const consumePurchase = consumePurchaseAndroid
|
|
1215
|
+
export const consumePurchase = consumePurchaseAndroid;
|