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/hooks/useIAP.ts
CHANGED
|
@@ -16,20 +16,23 @@ import {
|
|
|
16
16
|
getActiveSubscriptions,
|
|
17
17
|
hasActiveSubscriptions,
|
|
18
18
|
restorePurchases as restorePurchasesTopLevel,
|
|
19
|
+
getPromotedProductIOS,
|
|
20
|
+
requestPurchaseOnPromotedProductIOS,
|
|
19
21
|
} from '../';
|
|
20
|
-
import {getPromotedProductIOS, requestPurchaseOnPromotedProductIOS} from '../';
|
|
21
22
|
|
|
22
23
|
// Types
|
|
24
|
+
import {ProductQueryType, ErrorCode} from '../types';
|
|
23
25
|
import type {
|
|
26
|
+
ActiveSubscription,
|
|
24
27
|
Product,
|
|
25
28
|
Purchase,
|
|
26
29
|
PurchaseError,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
RequestPurchaseProps,
|
|
30
|
-
RequestSubscriptionProps,
|
|
31
|
-
ActiveSubscription,
|
|
30
|
+
ProductSubscription,
|
|
31
|
+
PurchaseParams,
|
|
32
32
|
} from '../types';
|
|
33
|
+
import type {FinishTransactionParams} from '../';
|
|
34
|
+
import type {NitroPurchaseResult} from '../specs/RnIap.nitro';
|
|
35
|
+
import {normalizeErrorCodeFromNative} from '../utils/errorMapping';
|
|
33
36
|
|
|
34
37
|
// Types for event subscriptions
|
|
35
38
|
interface EventSubscription {
|
|
@@ -41,7 +44,7 @@ type UseIap = {
|
|
|
41
44
|
products: Product[];
|
|
42
45
|
promotedProductsIOS: Purchase[];
|
|
43
46
|
promotedProductIdIOS?: string;
|
|
44
|
-
subscriptions:
|
|
47
|
+
subscriptions: ProductSubscription[];
|
|
45
48
|
availablePurchases: Purchase[];
|
|
46
49
|
currentPurchase?: Purchase;
|
|
47
50
|
currentPurchaseError?: PurchaseError;
|
|
@@ -52,14 +55,11 @@ type UseIap = {
|
|
|
52
55
|
finishTransaction: ({
|
|
53
56
|
purchase,
|
|
54
57
|
isConsumable,
|
|
55
|
-
}:
|
|
56
|
-
purchase: Purchase;
|
|
57
|
-
isConsumable?: boolean;
|
|
58
|
-
}) => Promise<PurchaseResult | boolean>;
|
|
58
|
+
}: FinishTransactionParams) => Promise<NitroPurchaseResult | boolean>;
|
|
59
59
|
getAvailablePurchases: (skus?: string[]) => Promise<void>;
|
|
60
60
|
fetchProducts: (params: {
|
|
61
61
|
skus: string[];
|
|
62
|
-
type?:
|
|
62
|
+
type?: ProductQueryType | null;
|
|
63
63
|
}) => Promise<void>;
|
|
64
64
|
/**
|
|
65
65
|
* @deprecated Use fetchProducts({ skus, type: 'inapp' }) instead. This method will be removed in version 3.0.0.
|
|
@@ -71,10 +71,7 @@ type UseIap = {
|
|
|
71
71
|
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
72
72
|
*/
|
|
73
73
|
getSubscriptions: (skus: string[]) => Promise<void>;
|
|
74
|
-
requestPurchase: (params:
|
|
75
|
-
request: RequestPurchaseProps | RequestSubscriptionProps;
|
|
76
|
-
type?: 'inapp' | 'subs';
|
|
77
|
-
}) => Promise<any>;
|
|
74
|
+
requestPurchase: (params: PurchaseParams) => Promise<any>;
|
|
78
75
|
validateReceipt: (
|
|
79
76
|
sku: string,
|
|
80
77
|
androidOptions?: {
|
|
@@ -109,7 +106,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
109
106
|
const [connected, setConnected] = useState<boolean>(false);
|
|
110
107
|
const [products, setProducts] = useState<Product[]>([]);
|
|
111
108
|
const [promotedProductsIOS] = useState<Purchase[]>([]);
|
|
112
|
-
const [subscriptions, setSubscriptions] = useState<
|
|
109
|
+
const [subscriptions, setSubscriptions] = useState<ProductSubscription[]>([]);
|
|
113
110
|
const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);
|
|
114
111
|
const [currentPurchase, setCurrentPurchase] = useState<Purchase>();
|
|
115
112
|
const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
|
|
@@ -159,7 +156,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
159
156
|
promotedProductIOS?: EventSubscription;
|
|
160
157
|
}>({});
|
|
161
158
|
|
|
162
|
-
const subscriptionsRefState = useRef<
|
|
159
|
+
const subscriptionsRefState = useRef<ProductSubscription[]>([]);
|
|
163
160
|
|
|
164
161
|
useEffect(() => {
|
|
165
162
|
subscriptionsRefState.current = subscriptions;
|
|
@@ -176,7 +173,10 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
176
173
|
const getProductsInternal = useCallback(
|
|
177
174
|
async (skus: string[]): Promise<void> => {
|
|
178
175
|
try {
|
|
179
|
-
const result = await fetchProducts({
|
|
176
|
+
const result = await fetchProducts({
|
|
177
|
+
skus,
|
|
178
|
+
type: ProductQueryType.InApp,
|
|
179
|
+
});
|
|
180
180
|
setProducts((prevProducts: Product[]) =>
|
|
181
181
|
mergeWithDuplicateCheck(
|
|
182
182
|
prevProducts,
|
|
@@ -194,12 +194,15 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
194
194
|
const getSubscriptionsInternal = useCallback(
|
|
195
195
|
async (skus: string[]): Promise<void> => {
|
|
196
196
|
try {
|
|
197
|
-
const result = await fetchProducts({
|
|
198
|
-
|
|
197
|
+
const result = await fetchProducts({
|
|
198
|
+
skus,
|
|
199
|
+
type: ProductQueryType.Subs,
|
|
200
|
+
});
|
|
201
|
+
setSubscriptions((prevSubscriptions: ProductSubscription[]) =>
|
|
199
202
|
mergeWithDuplicateCheck(
|
|
200
203
|
prevSubscriptions,
|
|
201
|
-
result as
|
|
202
|
-
(subscription:
|
|
204
|
+
result as ProductSubscription[],
|
|
205
|
+
(subscription: ProductSubscription) => subscription.id,
|
|
203
206
|
),
|
|
204
207
|
);
|
|
205
208
|
} catch (error) {
|
|
@@ -212,7 +215,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
212
215
|
const fetchProductsInternal = useCallback(
|
|
213
216
|
async (params: {
|
|
214
217
|
skus: string[];
|
|
215
|
-
type?:
|
|
218
|
+
type?: ProductQueryType | null;
|
|
216
219
|
}): Promise<void> => {
|
|
217
220
|
if (!connectedRef.current) {
|
|
218
221
|
console.warn(
|
|
@@ -222,12 +225,12 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
222
225
|
}
|
|
223
226
|
try {
|
|
224
227
|
const result = await fetchProducts(params);
|
|
225
|
-
if (params.type ===
|
|
226
|
-
setSubscriptions((prevSubscriptions:
|
|
228
|
+
if (params.type === ProductQueryType.Subs) {
|
|
229
|
+
setSubscriptions((prevSubscriptions: ProductSubscription[]) =>
|
|
227
230
|
mergeWithDuplicateCheck(
|
|
228
231
|
prevSubscriptions,
|
|
229
|
-
result as
|
|
230
|
-
(subscription:
|
|
232
|
+
result as ProductSubscription[],
|
|
233
|
+
(subscription: ProductSubscription) => subscription.id,
|
|
231
234
|
),
|
|
232
235
|
);
|
|
233
236
|
} else {
|
|
@@ -296,7 +299,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
296
299
|
}: {
|
|
297
300
|
purchase: Purchase;
|
|
298
301
|
isConsumable?: boolean;
|
|
299
|
-
}): Promise<
|
|
302
|
+
}): Promise<NitroPurchaseResult | boolean> => {
|
|
300
303
|
try {
|
|
301
304
|
return await finishTransactionInternal({
|
|
302
305
|
purchase,
|
|
@@ -322,7 +325,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
322
325
|
);
|
|
323
326
|
|
|
324
327
|
const requestPurchaseWithReset = useCallback(
|
|
325
|
-
async (requestObj:
|
|
328
|
+
async (requestObj: PurchaseParams) => {
|
|
326
329
|
clearCurrentPurchase();
|
|
327
330
|
clearCurrentPurchaseError();
|
|
328
331
|
|
|
@@ -371,23 +374,25 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
371
374
|
},
|
|
372
375
|
);
|
|
373
376
|
|
|
374
|
-
subscriptionsRef.current.purchaseError = purchaseErrorListener(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
377
|
+
subscriptionsRef.current.purchaseError = purchaseErrorListener((error) => {
|
|
378
|
+
const mappedError: PurchaseError = {
|
|
379
|
+
code: normalizeErrorCodeFromNative(error.code),
|
|
380
|
+
message: error.message,
|
|
381
|
+
productId: undefined,
|
|
382
|
+
};
|
|
383
|
+
// Ignore init error until connected
|
|
384
|
+
if (
|
|
385
|
+
mappedError.code === ErrorCode.InitConnection &&
|
|
386
|
+
!connectedRef.current
|
|
387
|
+
) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
setCurrentPurchase(undefined);
|
|
391
|
+
setCurrentPurchaseError(mappedError);
|
|
392
|
+
if (optionsRef.current?.onPurchaseError) {
|
|
393
|
+
optionsRef.current.onPurchaseError(mappedError);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
391
396
|
|
|
392
397
|
if (Platform.OS === 'ios') {
|
|
393
398
|
subscriptionsRef.current.promotedProductsIOS = promotedProductListenerIOS(
|
package/src/index.ts
CHANGED
|
@@ -12,22 +12,24 @@ import type {
|
|
|
12
12
|
NitroReceiptValidationResultIOS,
|
|
13
13
|
NitroReceiptValidationResultAndroid,
|
|
14
14
|
} from './specs/RnIap.nitro';
|
|
15
|
+
import {ProductQueryType} from './types';
|
|
15
16
|
import type {
|
|
16
17
|
Product,
|
|
18
|
+
ProductRequest,
|
|
17
19
|
Purchase,
|
|
18
20
|
PurchaseAndroid,
|
|
19
|
-
RequestPurchaseProps,
|
|
20
|
-
RequestSubscriptionProps,
|
|
21
|
-
RequestSubscriptionAndroidProps,
|
|
22
21
|
PurchaseOptions,
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
PurchaseParams,
|
|
23
|
+
PurchaseError,
|
|
25
24
|
ReceiptValidationResultAndroid,
|
|
26
|
-
|
|
25
|
+
ReceiptValidationResultIOS,
|
|
27
26
|
RequestPurchaseAndroidProps,
|
|
27
|
+
RequestPurchaseIosProps,
|
|
28
|
+
RequestPurchasePropsByPlatforms,
|
|
29
|
+
RequestSubscriptionAndroidProps,
|
|
30
|
+
RequestSubscriptionPropsByPlatforms,
|
|
28
31
|
SubscriptionStatusIOS,
|
|
29
32
|
} from './types';
|
|
30
|
-
import type {ProductRequest} from './types';
|
|
31
33
|
import {
|
|
32
34
|
convertNitroProductToProduct,
|
|
33
35
|
convertNitroPurchaseToPurchase,
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
convertNitroSubscriptionStatusToSubscriptionStatusIOS,
|
|
37
39
|
} from './utils/type-bridge';
|
|
38
40
|
import {parseErrorStringToJsonObj} from './utils/error';
|
|
41
|
+
import {normalizeErrorCodeFromNative} from './utils/errorMapping';
|
|
39
42
|
|
|
40
43
|
// Export all types
|
|
41
44
|
export type {
|
|
@@ -47,11 +50,46 @@ export type {
|
|
|
47
50
|
export * from './types';
|
|
48
51
|
export * from './utils/error';
|
|
49
52
|
|
|
50
|
-
//
|
|
53
|
+
// Internal constants/helpers for bridging legacy Nitro expectations
|
|
54
|
+
const NITRO_PRODUCT_TYPE_INAPP = 'inapp';
|
|
55
|
+
const NITRO_PRODUCT_TYPE_SUBS = 'subs';
|
|
56
|
+
|
|
57
|
+
function toNitroProductType(
|
|
58
|
+
type?: ProductQueryType | null,
|
|
59
|
+
): typeof NITRO_PRODUCT_TYPE_INAPP | typeof NITRO_PRODUCT_TYPE_SUBS {
|
|
60
|
+
return type === ProductQueryType.Subs
|
|
61
|
+
? NITRO_PRODUCT_TYPE_SUBS
|
|
62
|
+
: NITRO_PRODUCT_TYPE_INAPP;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isSubscriptionQuery(type?: ProductQueryType | null): boolean {
|
|
66
|
+
return type === ProductQueryType.Subs;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeProductQueryType(
|
|
70
|
+
type?: ProductQueryType | string | null,
|
|
71
|
+
): ProductQueryType {
|
|
72
|
+
if (type === ProductQueryType.All || type === 'all') {
|
|
73
|
+
return ProductQueryType.All;
|
|
74
|
+
}
|
|
75
|
+
if (type === ProductQueryType.Subs || type === 'subs') {
|
|
76
|
+
return ProductQueryType.Subs;
|
|
77
|
+
}
|
|
78
|
+
if (type === ProductQueryType.InApp || type === 'inapp') {
|
|
79
|
+
return ProductQueryType.InApp;
|
|
80
|
+
}
|
|
81
|
+
return ProductQueryType.InApp;
|
|
82
|
+
}
|
|
83
|
+
|
|
51
84
|
export interface EventSubscription {
|
|
52
85
|
remove(): void;
|
|
53
86
|
}
|
|
54
87
|
|
|
88
|
+
export type FinishTransactionParams = {
|
|
89
|
+
purchase: Purchase;
|
|
90
|
+
isConsumable?: boolean;
|
|
91
|
+
};
|
|
92
|
+
|
|
55
93
|
// ActiveSubscription and PurchaseError types are already exported via 'export * from ./types'
|
|
56
94
|
|
|
57
95
|
// Export hooks
|
|
@@ -150,17 +188,19 @@ export const endConnection = async (): Promise<boolean> => {
|
|
|
150
188
|
*/
|
|
151
189
|
export const fetchProducts = async ({
|
|
152
190
|
skus,
|
|
153
|
-
type =
|
|
191
|
+
type = ProductQueryType.InApp,
|
|
154
192
|
}: ProductRequest): Promise<Product[]> => {
|
|
155
193
|
try {
|
|
156
194
|
if (!skus || skus.length === 0) {
|
|
157
195
|
throw new Error('No SKUs provided');
|
|
158
196
|
}
|
|
159
197
|
|
|
160
|
-
|
|
198
|
+
const normalizedType = normalizeProductQueryType(type);
|
|
199
|
+
|
|
200
|
+
if (normalizedType === ProductQueryType.All) {
|
|
161
201
|
const [inappNitro, subsNitro] = await Promise.all([
|
|
162
|
-
IAP.instance.fetchProducts(skus,
|
|
163
|
-
IAP.instance.fetchProducts(skus,
|
|
202
|
+
IAP.instance.fetchProducts(skus, NITRO_PRODUCT_TYPE_INAPP),
|
|
203
|
+
IAP.instance.fetchProducts(skus, NITRO_PRODUCT_TYPE_SUBS),
|
|
164
204
|
]);
|
|
165
205
|
const allNitro = [...inappNitro, ...subsNitro];
|
|
166
206
|
const validAll = allNitro.filter(validateNitroProduct);
|
|
@@ -172,7 +212,10 @@ export const fetchProducts = async ({
|
|
|
172
212
|
return validAll.map(convertNitroProductToProduct);
|
|
173
213
|
}
|
|
174
214
|
|
|
175
|
-
const nitroProducts = await IAP.instance.fetchProducts(
|
|
215
|
+
const nitroProducts = await IAP.instance.fetchProducts(
|
|
216
|
+
skus,
|
|
217
|
+
toNitroProductType(normalizedType),
|
|
218
|
+
);
|
|
176
219
|
|
|
177
220
|
// Validate and convert NitroProducts to TypeScript Products
|
|
178
221
|
const validProducts = nitroProducts.filter(validateNitroProduct);
|
|
@@ -225,17 +268,53 @@ export const fetchProducts = async ({
|
|
|
225
268
|
* ⚠️ Important: This is an event-based operation, not promise-based.
|
|
226
269
|
* Listen for events through purchaseUpdatedListener or purchaseErrorListener.
|
|
227
270
|
* @param params - Purchase request configuration
|
|
228
|
-
* @param params.
|
|
229
|
-
* @param params.
|
|
271
|
+
* @param params.requestPurchase - Platform-specific purchase parameters (in-app)
|
|
272
|
+
* @param params.requestSubscription - Platform-specific subscription parameters (subs)
|
|
273
|
+
* @param params.type - Type of purchase (defaults to in-app)
|
|
230
274
|
*/
|
|
231
|
-
export const requestPurchase = async (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}: {
|
|
235
|
-
request: RequestPurchaseProps | RequestSubscriptionProps;
|
|
236
|
-
type?: 'inapp' | 'subs';
|
|
237
|
-
}): Promise<void> => {
|
|
275
|
+
export const requestPurchase = async (
|
|
276
|
+
params: PurchaseParams,
|
|
277
|
+
): Promise<void> => {
|
|
238
278
|
try {
|
|
279
|
+
const {requestPurchase: purchaseRequest, requestSubscription} = params;
|
|
280
|
+
const normalizedPurchaseRequest = purchaseRequest ?? undefined;
|
|
281
|
+
const normalizedSubscriptionRequest = requestSubscription ?? undefined;
|
|
282
|
+
|
|
283
|
+
const effectiveType = normalizeProductQueryType(params.type);
|
|
284
|
+
const isSubs = isSubscriptionQuery(effectiveType);
|
|
285
|
+
let request:
|
|
286
|
+
| RequestPurchasePropsByPlatforms
|
|
287
|
+
| RequestSubscriptionPropsByPlatforms
|
|
288
|
+
| undefined;
|
|
289
|
+
|
|
290
|
+
if (isSubs) {
|
|
291
|
+
if (
|
|
292
|
+
__DEV__ &&
|
|
293
|
+
normalizedPurchaseRequest &&
|
|
294
|
+
!normalizedSubscriptionRequest
|
|
295
|
+
) {
|
|
296
|
+
console.warn(
|
|
297
|
+
'[react-native-iap] `requestPurchase` was provided for a subscription request. Did you mean to use `requestSubscription`?',
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
request = normalizedSubscriptionRequest ?? normalizedPurchaseRequest;
|
|
301
|
+
} else {
|
|
302
|
+
if (
|
|
303
|
+
__DEV__ &&
|
|
304
|
+
normalizedSubscriptionRequest &&
|
|
305
|
+
!normalizedPurchaseRequest
|
|
306
|
+
) {
|
|
307
|
+
console.warn(
|
|
308
|
+
'[react-native-iap] `requestSubscription` was provided for an in-app purchase request. Did you mean to use `requestPurchase`?',
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
request = normalizedPurchaseRequest ?? normalizedSubscriptionRequest;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!request) {
|
|
315
|
+
throw new Error('Missing purchase request configuration');
|
|
316
|
+
}
|
|
317
|
+
|
|
239
318
|
// Validate platform-specific requests
|
|
240
319
|
if (Platform.OS === 'ios') {
|
|
241
320
|
const iosRequest = request.ios;
|
|
@@ -261,8 +340,7 @@ export const requestPurchase = async ({
|
|
|
261
340
|
if (Platform.OS === 'ios' && request.ios) {
|
|
262
341
|
const iosReq = request.ios as RequestPurchaseIosProps;
|
|
263
342
|
const autoFinishSubs =
|
|
264
|
-
|
|
265
|
-
iosReq.andDangerouslyFinishTransactionAutomatically == null;
|
|
343
|
+
isSubs && iosReq.andDangerouslyFinishTransactionAutomatically == null;
|
|
266
344
|
unifiedRequest.ios = {
|
|
267
345
|
...iosReq,
|
|
268
346
|
// Align with native SwiftUI flow: auto-finish subscriptions by default
|
|
@@ -273,7 +351,7 @@ export const requestPurchase = async ({
|
|
|
273
351
|
}
|
|
274
352
|
|
|
275
353
|
if (Platform.OS === 'android' && request.android) {
|
|
276
|
-
if (
|
|
354
|
+
if (isSubs) {
|
|
277
355
|
const subsRequest = request.android as RequestSubscriptionAndroidProps;
|
|
278
356
|
unifiedRequest.android = {
|
|
279
357
|
...subsRequest,
|
|
@@ -620,13 +698,20 @@ export const purchaseUpdatedListener = (
|
|
|
620
698
|
* ```
|
|
621
699
|
*/
|
|
622
700
|
export const purchaseErrorListener = (
|
|
623
|
-
listener: (error:
|
|
701
|
+
listener: (error: PurchaseError) => void,
|
|
624
702
|
): EventSubscription => {
|
|
625
|
-
|
|
626
|
-
|
|
703
|
+
const wrapped = (error: NitroPurchaseResult) => {
|
|
704
|
+
listener({
|
|
705
|
+
code: normalizeErrorCodeFromNative(error.code),
|
|
706
|
+
message: error.message,
|
|
707
|
+
productId: undefined,
|
|
708
|
+
});
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
listenerMap.set(listener, wrapped);
|
|
627
712
|
let attached = false;
|
|
628
713
|
try {
|
|
629
|
-
IAP.instance.addPurchaseErrorListener(
|
|
714
|
+
IAP.instance.addPurchaseErrorListener(wrapped as any);
|
|
630
715
|
attached = true;
|
|
631
716
|
} catch (e) {
|
|
632
717
|
const msg = String(e ?? '');
|
|
@@ -641,12 +726,15 @@ export const purchaseErrorListener = (
|
|
|
641
726
|
|
|
642
727
|
return {
|
|
643
728
|
remove: () => {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
729
|
+
const stored = listenerMap.get(listener);
|
|
730
|
+
if (stored) {
|
|
731
|
+
if (attached) {
|
|
732
|
+
try {
|
|
733
|
+
IAP.instance.removePurchaseErrorListener(stored as any);
|
|
734
|
+
} catch {}
|
|
735
|
+
}
|
|
736
|
+
listenerMap.delete(listener);
|
|
648
737
|
}
|
|
649
|
-
listenerMap.delete(listener);
|
|
650
738
|
},
|
|
651
739
|
};
|
|
652
740
|
};
|
|
@@ -1240,7 +1328,7 @@ export {
|
|
|
1240
1328
|
export {
|
|
1241
1329
|
convertNitroProductToProduct,
|
|
1242
1330
|
convertNitroPurchaseToPurchase,
|
|
1243
|
-
|
|
1331
|
+
convertProductToProductSubscription,
|
|
1244
1332
|
validateNitroProduct,
|
|
1245
1333
|
validateNitroPurchase,
|
|
1246
1334
|
checkTypeSynchronization,
|
package/src/specs/RnIap.nitro.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {HybridObject} from 'react-native-nitro-modules';
|
|
|
9
9
|
/**
|
|
10
10
|
* Android-specific receipt validation options
|
|
11
11
|
*/
|
|
12
|
-
export interface
|
|
12
|
+
export interface NitroReceiptValidationAndroidOptions {
|
|
13
13
|
packageName: string;
|
|
14
14
|
productToken: string;
|
|
15
15
|
accessToken: string;
|
|
@@ -21,7 +21,7 @@ export interface NitroAndroidReceiptValidationOptions {
|
|
|
21
21
|
*/
|
|
22
22
|
export interface NitroReceiptValidationParams {
|
|
23
23
|
sku: string;
|
|
24
|
-
androidOptions?:
|
|
24
|
+
androidOptions?: NitroReceiptValidationAndroidOptions;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Purchase request parameters
|