expo-iap 2.9.7 → 3.0.0
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/CHANGELOG.md +30 -0
- package/README.md +1 -1
- package/android/build.gradle +7 -2
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +195 -650
- package/android/src/main/java/expo/modules/iap/PromiseUtils.kt +85 -0
- package/build/ExpoIap.types.d.ts +0 -6
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js.map +1 -1
- package/build/helpers/subscription.d.ts.map +1 -1
- package/build/helpers/subscription.js +14 -3
- package/build/helpers/subscription.js.map +1 -1
- package/build/index.d.ts +6 -73
- package/build/index.d.ts.map +1 -1
- package/build/index.js +21 -154
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts +2 -2
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +11 -1
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +0 -60
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +2 -121
- package/build/modules/ios.js.map +1 -1
- package/build/types/ExpoIapAndroid.types.d.ts +0 -8
- package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
- package/build/types/ExpoIapAndroid.types.js +0 -1
- package/build/types/ExpoIapAndroid.types.js.map +1 -1
- package/build/types/ExpoIapIOS.types.d.ts +0 -5
- package/build/types/ExpoIapIOS.types.d.ts.map +1 -1
- package/build/types/ExpoIapIOS.types.js.map +1 -1
- package/build/useIAP.d.ts +0 -18
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +1 -18
- package/build/useIAP.js.map +1 -1
- package/bun.lock +340 -137
- package/codecov.yml +17 -21
- package/ios/ExpoIapModule.swift +10 -3
- package/jest.config.js +5 -9
- package/package.json +5 -3
- package/plugin/build/withIAP.d.ts +4 -1
- package/plugin/build/withIAP.js +38 -24
- package/plugin/build/withLocalOpenIAP.d.ts +6 -2
- package/plugin/build/withLocalOpenIAP.js +175 -20
- package/plugin/src/withIAP.ts +66 -30
- package/plugin/src/withLocalOpenIAP.ts +228 -24
- package/src/ExpoIap.types.ts +0 -8
- package/src/helpers/subscription.ts +14 -3
- package/src/index.ts +22 -230
- package/src/modules/android.ts +16 -6
- package/src/modules/ios.ts +2 -168
- package/src/types/ExpoIapAndroid.types.ts +0 -11
- package/src/types/ExpoIapIOS.types.ts +0 -5
- package/src/useIAP.ts +3 -55
- package/android/src/main/java/expo/modules/iap/PlayUtils.kt +0 -178
- package/android/src/main/java/expo/modules/iap/Types.kt +0 -98
package/src/modules/android.ts
CHANGED
|
@@ -38,18 +38,28 @@ export const deepLinkToSubscriptionsAndroid = async ({
|
|
|
38
38
|
sku,
|
|
39
39
|
packageName,
|
|
40
40
|
}: {
|
|
41
|
-
sku
|
|
42
|
-
packageName
|
|
41
|
+
sku?: string;
|
|
42
|
+
packageName?: string;
|
|
43
43
|
}): Promise<void> => {
|
|
44
|
+
// Prefer native deep link implementation via OpenIAP module
|
|
45
|
+
if (ExpoIapModule?.deepLinkToSubscriptionsAndroid) {
|
|
46
|
+
return (ExpoIapModule as any).deepLinkToSubscriptionsAndroid({
|
|
47
|
+
skuAndroid: sku,
|
|
48
|
+
packageNameAndroid: packageName,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fallback to Linking if native method unavailable
|
|
44
53
|
if (!packageName) {
|
|
45
54
|
throw new Error(
|
|
46
55
|
'packageName is required for deepLinkToSubscriptionsAndroid',
|
|
47
56
|
);
|
|
48
57
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
);
|
|
58
|
+
const base = `https://play.google.com/store/account/subscriptions?package=${encodeURIComponent(
|
|
59
|
+
packageName,
|
|
60
|
+
)}`;
|
|
61
|
+
const url = sku ? `${base}&sku=${encodeURIComponent(sku)}` : base;
|
|
62
|
+
return Linking.openURL(url);
|
|
53
63
|
};
|
|
54
64
|
|
|
55
65
|
/**
|
package/src/modules/ios.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// External dependencies
|
|
2
2
|
|
|
3
3
|
// Internal modules
|
|
4
|
-
import
|
|
4
|
+
// import removed: use purchaseUpdatedListener directly in app code
|
|
5
5
|
import ExpoIapModule from '../ExpoIapModule';
|
|
6
6
|
|
|
7
7
|
// Types
|
|
@@ -20,55 +20,6 @@ export type TransactionEvent = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
// Listeners
|
|
23
|
-
/**
|
|
24
|
-
* @deprecated Use `purchaseUpdatedListener` instead. This function will be removed in a future version.
|
|
25
|
-
*
|
|
26
|
-
* The `transactionUpdatedIos` function is redundant as it simply wraps `purchaseUpdatedListener`.
|
|
27
|
-
* You can achieve the same functionality by using `purchaseUpdatedListener` directly.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* // Instead of:
|
|
31
|
-
* // transactionUpdatedIos((event) => { ... });
|
|
32
|
-
*
|
|
33
|
-
* // Use:
|
|
34
|
-
* // purchaseUpdatedListener((purchase) => { ... });
|
|
35
|
-
*/
|
|
36
|
-
export const transactionUpdatedIOS = (
|
|
37
|
-
listener: (event: TransactionEvent) => void,
|
|
38
|
-
) => {
|
|
39
|
-
const isPurchase = (item: unknown): item is Purchase => {
|
|
40
|
-
return (
|
|
41
|
-
item != null &&
|
|
42
|
-
typeof item === 'object' &&
|
|
43
|
-
'id' in item &&
|
|
44
|
-
'transactionId' in item &&
|
|
45
|
-
'platform' in item
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Helper function to safely convert Purchase to TransactionEvent
|
|
50
|
-
const mapPurchaseToTransactionEvent = (
|
|
51
|
-
purchase: Purchase,
|
|
52
|
-
): TransactionEvent => {
|
|
53
|
-
// Validate the purchase object before casting
|
|
54
|
-
if (isPurchase(purchase)) {
|
|
55
|
-
return {
|
|
56
|
-
transaction: purchase as Purchase,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Fallback: create a basic TransactionEvent structure
|
|
61
|
-
return {
|
|
62
|
-
transaction: purchase as Purchase,
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return purchaseUpdatedListener((purchase) => {
|
|
67
|
-
// Convert Purchase to TransactionEvent format for backward compatibility
|
|
68
|
-
const event = mapPurchaseToTransactionEvent(purchase);
|
|
69
|
-
listener(event);
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
23
|
|
|
73
24
|
// Type guards
|
|
74
25
|
export function isProductIOS<T extends {platform?: string}>(
|
|
@@ -310,8 +261,6 @@ export const requestPurchaseOnPromotedProductIOS = (): Promise<void> => {
|
|
|
310
261
|
return ExpoIapModule.requestPurchaseOnPromotedProductIOS();
|
|
311
262
|
};
|
|
312
263
|
|
|
313
|
-
// NOTE: buyPromotedProductIOS removed in v2.9.0. Use requestPurchaseOnPromotedProductIOS.
|
|
314
|
-
|
|
315
264
|
/**
|
|
316
265
|
* Get pending transactions that haven't been finished yet (iOS only).
|
|
317
266
|
*
|
|
@@ -341,119 +290,4 @@ export const clearTransactionIOS = (): Promise<void> => {
|
|
|
341
290
|
export const deepLinkToSubscriptionsIOS = (): Promise<void> =>
|
|
342
291
|
Linking.openURL('https://apps.apple.com/account/subscriptions');
|
|
343
292
|
|
|
344
|
-
//
|
|
345
|
-
// These will be removed in version 3.0.0
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* @deprecated Use `syncIOS` instead. This function will be removed in version 3.0.0.
|
|
349
|
-
*/
|
|
350
|
-
export const sync = (): Promise<null> => {
|
|
351
|
-
console.warn(
|
|
352
|
-
'`sync` is deprecated. Use `syncIOS` instead. This function will be removed in version 3.0.0.',
|
|
353
|
-
);
|
|
354
|
-
return syncIOS();
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* @deprecated Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.
|
|
359
|
-
*/
|
|
360
|
-
export const isEligibleForIntroOffer = (groupId: string): Promise<boolean> => {
|
|
361
|
-
console.warn(
|
|
362
|
-
'`isEligibleForIntroOffer` is deprecated. Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.',
|
|
363
|
-
);
|
|
364
|
-
return isEligibleForIntroOfferIOS(groupId);
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* @deprecated Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.
|
|
369
|
-
*/
|
|
370
|
-
export const subscriptionStatus = (
|
|
371
|
-
sku: string,
|
|
372
|
-
): Promise<SubscriptionStatusIOS[]> => {
|
|
373
|
-
console.warn(
|
|
374
|
-
'`subscriptionStatus` is deprecated. Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.',
|
|
375
|
-
);
|
|
376
|
-
return subscriptionStatusIOS(sku);
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* @deprecated Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.
|
|
381
|
-
*/
|
|
382
|
-
export const currentEntitlement = (sku: string): Promise<Purchase> => {
|
|
383
|
-
console.warn(
|
|
384
|
-
'`currentEntitlement` is deprecated. Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.',
|
|
385
|
-
);
|
|
386
|
-
return currentEntitlementIOS(sku);
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* @deprecated Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.
|
|
391
|
-
*/
|
|
392
|
-
export const latestTransaction = (sku: string): Promise<Purchase> => {
|
|
393
|
-
console.warn(
|
|
394
|
-
'`latestTransaction` is deprecated. Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.',
|
|
395
|
-
);
|
|
396
|
-
return latestTransactionIOS(sku);
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* @deprecated Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.
|
|
401
|
-
*/
|
|
402
|
-
export const beginRefundRequest = (
|
|
403
|
-
sku: string,
|
|
404
|
-
): Promise<RefundRequestStatus> => {
|
|
405
|
-
console.warn(
|
|
406
|
-
'`beginRefundRequest` is deprecated. Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.',
|
|
407
|
-
);
|
|
408
|
-
return beginRefundRequestIOS(sku);
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* @deprecated Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.
|
|
413
|
-
*/
|
|
414
|
-
export const showManageSubscriptions = (): Promise<Purchase[]> => {
|
|
415
|
-
console.warn(
|
|
416
|
-
'`showManageSubscriptions` is deprecated. Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.',
|
|
417
|
-
);
|
|
418
|
-
return showManageSubscriptionsIOS();
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* @deprecated Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.
|
|
423
|
-
*/
|
|
424
|
-
export const isTransactionVerified = (sku: string): Promise<boolean> => {
|
|
425
|
-
console.warn(
|
|
426
|
-
'`isTransactionVerified` is deprecated. Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.',
|
|
427
|
-
);
|
|
428
|
-
return isTransactionVerifiedIOS(sku);
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* @deprecated Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.
|
|
433
|
-
*/
|
|
434
|
-
export const getTransactionJws = (sku: string): Promise<string> => {
|
|
435
|
-
console.warn(
|
|
436
|
-
'`getTransactionJws` is deprecated. Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.',
|
|
437
|
-
);
|
|
438
|
-
return getTransactionJwsIOS(sku);
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* @deprecated Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.
|
|
443
|
-
*/
|
|
444
|
-
export const presentCodeRedemptionSheet = (): Promise<boolean> => {
|
|
445
|
-
console.warn(
|
|
446
|
-
'`presentCodeRedemptionSheet` is deprecated. Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.',
|
|
447
|
-
);
|
|
448
|
-
return presentCodeRedemptionSheetIOS();
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* @deprecated Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.
|
|
453
|
-
*/
|
|
454
|
-
export const getAppTransaction = (): Promise<AppTransactionIOS | null> => {
|
|
455
|
-
console.warn(
|
|
456
|
-
'`getAppTransaction` is deprecated. Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.',
|
|
457
|
-
);
|
|
458
|
-
return getAppTransactionIOS();
|
|
459
|
-
};
|
|
293
|
+
// iOS-specific APIs only; cross-platform wrappers live in src/index.ts
|
|
@@ -72,10 +72,6 @@ type SubscriptionOffer = {
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export type RequestSubscriptionAndroidProps = RequestPurchaseAndroidProps & {
|
|
75
|
-
/**
|
|
76
|
-
* @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
|
|
77
|
-
*/
|
|
78
|
-
purchaseTokenAndroid?: string;
|
|
79
75
|
replacementModeAndroid?: ReplacementModesAndroid;
|
|
80
76
|
subscriptionOffers: SubscriptionOffer[];
|
|
81
77
|
};
|
|
@@ -121,15 +117,10 @@ export enum PurchaseAndroidState {
|
|
|
121
117
|
}
|
|
122
118
|
|
|
123
119
|
// Legacy naming for backward compatibility
|
|
124
|
-
// Removed legacy alias `PurchaseStateAndroid` in v2.9.0
|
|
125
120
|
|
|
126
121
|
// Legacy naming for backward compatibility
|
|
127
122
|
export type ProductPurchaseAndroid = PurchaseCommon & {
|
|
128
123
|
platform: 'android';
|
|
129
|
-
/**
|
|
130
|
-
* @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
|
|
131
|
-
*/
|
|
132
|
-
purchaseTokenAndroid?: string;
|
|
133
124
|
dataAndroid?: string;
|
|
134
125
|
signatureAndroid?: string;
|
|
135
126
|
autoRenewingAndroid?: boolean;
|
|
@@ -143,5 +134,3 @@ export type ProductPurchaseAndroid = PurchaseCommon & {
|
|
|
143
134
|
|
|
144
135
|
// Preferred naming
|
|
145
136
|
export type PurchaseAndroid = ProductPurchaseAndroid;
|
|
146
|
-
|
|
147
|
-
// Removed legacy Android alias types in v2.9.0
|
|
@@ -145,11 +145,6 @@ export type ProductPurchaseIOS = PurchaseCommon & {
|
|
|
145
145
|
currencyCodeIOS?: string;
|
|
146
146
|
currencySymbolIOS?: string;
|
|
147
147
|
countryCodeIOS?: string;
|
|
148
|
-
/**
|
|
149
|
-
* @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
|
|
150
|
-
* iOS 15+ JWS representation is now available through the `purchaseToken` field.
|
|
151
|
-
*/
|
|
152
|
-
jwsRepresentationIOS?: string;
|
|
153
148
|
};
|
|
154
149
|
|
|
155
150
|
// Preferred naming
|
package/src/useIAP.ts
CHANGED
|
@@ -67,24 +67,7 @@ type UseIap = {
|
|
|
67
67
|
skus: string[];
|
|
68
68
|
type?: 'inapp' | 'subs';
|
|
69
69
|
}) => Promise<void>;
|
|
70
|
-
|
|
71
|
-
* @deprecated Use fetchProducts({ skus, type: 'inapp' | 'subs' }) instead. This method will be removed in version 3.0.0.
|
|
72
|
-
* The 'request' prefix should only be used for event-based operations.
|
|
73
|
-
*/
|
|
74
|
-
requestProducts: (params: {
|
|
75
|
-
skus: string[];
|
|
76
|
-
type?: 'inapp' | 'subs';
|
|
77
|
-
}) => Promise<void>;
|
|
78
|
-
/**
|
|
79
|
-
* @deprecated Use fetchProducts({ skus, type: 'inapp' }) instead. This method will be removed in version 3.0.0.
|
|
80
|
-
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
81
|
-
*/
|
|
82
|
-
getProducts: (skus: string[]) => Promise<void>;
|
|
83
|
-
/**
|
|
84
|
-
* @deprecated Use fetchProducts({ skus, type: 'subs' }) instead. This method will be removed in version 3.0.0.
|
|
85
|
-
* Note: This method internally uses fetchProducts, so no deprecation warning is shown.
|
|
86
|
-
*/
|
|
87
|
-
getSubscriptions: (skus: string[]) => Promise<void>;
|
|
70
|
+
|
|
88
71
|
requestPurchase: (params: {
|
|
89
72
|
request: RequestPurchaseProps | RequestSubscriptionProps;
|
|
90
73
|
type?: 'inapp' | 'subs';
|
|
@@ -122,7 +105,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
122
105
|
const [products, setProducts] = useState<Product[]>([]);
|
|
123
106
|
const [promotedProductsIOS] = useState<Purchase[]>([]);
|
|
124
107
|
const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);
|
|
125
|
-
|
|
108
|
+
|
|
126
109
|
const [availablePurchases, setAvailablePurchases] = useState<Purchase[]>([]);
|
|
127
110
|
const [currentPurchase, setCurrentPurchase] = useState<Purchase>();
|
|
128
111
|
const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
|
|
@@ -186,24 +169,6 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
186
169
|
setCurrentPurchaseError(undefined);
|
|
187
170
|
}, []);
|
|
188
171
|
|
|
189
|
-
const getProductsInternal = useCallback(
|
|
190
|
-
async (skus: string[]): Promise<void> => {
|
|
191
|
-
try {
|
|
192
|
-
const result = await fetchProducts({skus, type: 'inapp'});
|
|
193
|
-
setProducts((prevProducts) =>
|
|
194
|
-
mergeWithDuplicateCheck(
|
|
195
|
-
prevProducts,
|
|
196
|
-
result as Product[],
|
|
197
|
-
(product) => product.id,
|
|
198
|
-
),
|
|
199
|
-
);
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.error('Error fetching products:', error);
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
[mergeWithDuplicateCheck],
|
|
205
|
-
);
|
|
206
|
-
|
|
207
172
|
const getSubscriptionsInternal = useCallback(
|
|
208
173
|
async (skus: string[]): Promise<void> => {
|
|
209
174
|
try {
|
|
@@ -254,19 +219,6 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
254
219
|
[mergeWithDuplicateCheck],
|
|
255
220
|
);
|
|
256
221
|
|
|
257
|
-
const requestProductsInternal = useCallback(
|
|
258
|
-
async (params: {
|
|
259
|
-
skus: string[];
|
|
260
|
-
type?: 'inapp' | 'subs';
|
|
261
|
-
}): Promise<void> => {
|
|
262
|
-
console.warn(
|
|
263
|
-
"`requestProducts` is deprecated in useIAP hook. Use the new `fetchProducts` method instead. The 'request' prefix should only be used for event-based operations.",
|
|
264
|
-
);
|
|
265
|
-
return fetchProductsInternal(params);
|
|
266
|
-
},
|
|
267
|
-
[fetchProductsInternal],
|
|
268
|
-
);
|
|
269
|
-
|
|
270
222
|
const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {
|
|
271
223
|
try {
|
|
272
224
|
const result = await getAvailablePurchases({
|
|
@@ -304,8 +256,6 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
304
256
|
[],
|
|
305
257
|
);
|
|
306
258
|
|
|
307
|
-
// NOTE: getPurchaseHistories removed in v2.9.0. Use getAvailablePurchases instead.
|
|
308
|
-
|
|
309
259
|
const finishTransaction = useCallback(
|
|
310
260
|
async ({
|
|
311
261
|
purchase,
|
|
@@ -509,12 +459,10 @@ export function useIAP(options?: UseIAPOptions): UseIap {
|
|
|
509
459
|
clearCurrentPurchaseError,
|
|
510
460
|
getAvailablePurchases: getAvailablePurchasesInternal,
|
|
511
461
|
fetchProducts: fetchProductsInternal,
|
|
512
|
-
requestProducts: requestProductsInternal,
|
|
513
462
|
requestPurchase: requestPurchaseWithReset,
|
|
514
463
|
validateReceipt,
|
|
515
464
|
restorePurchases: restorePurchasesInternal,
|
|
516
|
-
|
|
517
|
-
getSubscriptions: getSubscriptionsInternal,
|
|
465
|
+
// internal getters kept for hook state management
|
|
518
466
|
getPromotedProductIOS,
|
|
519
467
|
requestPurchaseOnPromotedProductIOS,
|
|
520
468
|
getActiveSubscriptions: getActiveSubscriptionsInternal,
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
package expo.modules.iap
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
import com.android.billingclient.api.BillingClient
|
|
5
|
-
import expo.modules.kotlin.Promise
|
|
6
|
-
|
|
7
|
-
data class BillingResponse(
|
|
8
|
-
val code: String,
|
|
9
|
-
val message: String,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
object PromiseUtils {
|
|
13
|
-
private val promises = HashMap<String, MutableList<Promise>>()
|
|
14
|
-
|
|
15
|
-
const val TAG = "PromiseUtils"
|
|
16
|
-
|
|
17
|
-
fun addPromiseForKey(
|
|
18
|
-
key: String,
|
|
19
|
-
promise: Promise,
|
|
20
|
-
) {
|
|
21
|
-
promises.getOrPut(key) { mutableListOf() }.add(promise)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
fun resolvePromisesForKey(
|
|
25
|
-
key: String,
|
|
26
|
-
value: Any?,
|
|
27
|
-
) {
|
|
28
|
-
promises[key]?.forEach { promise ->
|
|
29
|
-
promise.safeResolve(value)
|
|
30
|
-
}
|
|
31
|
-
promises.remove(key)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
fun rejectAllPendingPromises() {
|
|
35
|
-
promises.flatMap { it.value }.forEach { promise ->
|
|
36
|
-
promise.safeReject(IapErrorCode.E_CONNECTION_CLOSED, "Connection has been closed", null)
|
|
37
|
-
}
|
|
38
|
-
promises.clear()
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
fun rejectPromisesForKey(
|
|
42
|
-
key: String,
|
|
43
|
-
code: String,
|
|
44
|
-
message: String?,
|
|
45
|
-
err: Exception?,
|
|
46
|
-
) {
|
|
47
|
-
promises[key]?.forEach { promise ->
|
|
48
|
-
promise.safeReject(code, message, err)
|
|
49
|
-
}
|
|
50
|
-
promises.remove(key)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const val TAG = "IapPromises"
|
|
55
|
-
|
|
56
|
-
fun Promise.safeResolve(value: Any?) {
|
|
57
|
-
try {
|
|
58
|
-
this.resolve(value)
|
|
59
|
-
} catch (e: RuntimeException) {
|
|
60
|
-
Log.d(TAG, "Already consumed ${e.message}")
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fun Promise.safeReject(message: String) = this.safeReject(message, null, null)
|
|
65
|
-
|
|
66
|
-
fun Promise.safeReject(
|
|
67
|
-
code: String,
|
|
68
|
-
message: String?,
|
|
69
|
-
) = this.safeReject(code, message, null)
|
|
70
|
-
|
|
71
|
-
fun Promise.safeReject(
|
|
72
|
-
code: String,
|
|
73
|
-
throwable: Throwable?,
|
|
74
|
-
) = this.safeReject(code, null, throwable)
|
|
75
|
-
|
|
76
|
-
fun Promise.safeReject(
|
|
77
|
-
code: String,
|
|
78
|
-
message: String?,
|
|
79
|
-
throwable: Throwable?,
|
|
80
|
-
) {
|
|
81
|
-
try {
|
|
82
|
-
this.reject(code, message, throwable)
|
|
83
|
-
} catch (e: RuntimeException) {
|
|
84
|
-
Log.d(TAG, "Already consumed ${e.message}")
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
object PlayUtils {
|
|
89
|
-
const val TAG = "PlayUtils"
|
|
90
|
-
|
|
91
|
-
fun rejectPromiseWithBillingError(
|
|
92
|
-
promise: Promise,
|
|
93
|
-
responseCode: Int,
|
|
94
|
-
) {
|
|
95
|
-
val errorData = getBillingResponseData(responseCode)
|
|
96
|
-
promise.reject(errorData.code, errorData.message, null)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
fun getBillingResponseData(responseCode: Int): BillingResponse {
|
|
100
|
-
val errorData =
|
|
101
|
-
when (responseCode) {
|
|
102
|
-
BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED -> {
|
|
103
|
-
BillingResponse(
|
|
104
|
-
IapErrorCode.E_SERVICE_ERROR,
|
|
105
|
-
"This feature is not available on your device.",
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
BillingClient.BillingResponseCode.SERVICE_DISCONNECTED -> {
|
|
109
|
-
BillingResponse(
|
|
110
|
-
IapErrorCode.E_NETWORK_ERROR,
|
|
111
|
-
"The service is disconnected (check your internet connection.)",
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
BillingClient.BillingResponseCode.NETWORK_ERROR -> {
|
|
115
|
-
BillingResponse(
|
|
116
|
-
IapErrorCode.E_NETWORK_ERROR,
|
|
117
|
-
"You have a problem with network connection.",
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
BillingClient.BillingResponseCode.OK -> {
|
|
121
|
-
BillingResponse(
|
|
122
|
-
"OK",
|
|
123
|
-
"",
|
|
124
|
-
)
|
|
125
|
-
}
|
|
126
|
-
BillingClient.BillingResponseCode.USER_CANCELED -> {
|
|
127
|
-
BillingResponse(
|
|
128
|
-
IapErrorCode.E_USER_CANCELLED,
|
|
129
|
-
"Payment is cancelled.",
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE -> {
|
|
133
|
-
BillingResponse(
|
|
134
|
-
IapErrorCode.E_SERVICE_ERROR,
|
|
135
|
-
"The service is unreachable. This may be your internet connection, or the Play Store may be down.",
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
BillingClient.BillingResponseCode.BILLING_UNAVAILABLE -> {
|
|
139
|
-
BillingResponse(
|
|
140
|
-
IapErrorCode.E_SERVICE_ERROR,
|
|
141
|
-
"Billing is unavailable. This may be a problem with your device, or the Play Store may be down.",
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
BillingClient.BillingResponseCode.ITEM_UNAVAILABLE -> {
|
|
145
|
-
BillingResponse(
|
|
146
|
-
IapErrorCode.E_ITEM_UNAVAILABLE,
|
|
147
|
-
"That item is unavailable.",
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
BillingClient.BillingResponseCode.DEVELOPER_ERROR -> {
|
|
151
|
-
BillingResponse(
|
|
152
|
-
IapErrorCode.E_DEVELOPER_ERROR,
|
|
153
|
-
"Google is indicating that we have some issue connecting to payment.",
|
|
154
|
-
)
|
|
155
|
-
}
|
|
156
|
-
BillingClient.BillingResponseCode.ERROR -> {
|
|
157
|
-
BillingResponse(
|
|
158
|
-
IapErrorCode.E_UNKNOWN,
|
|
159
|
-
"An unknown or unexpected error has occurred. Please try again later.",
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED -> {
|
|
163
|
-
BillingResponse(
|
|
164
|
-
IapErrorCode.E_ALREADY_OWNED,
|
|
165
|
-
"You already own this item.",
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
else -> {
|
|
169
|
-
BillingResponse(
|
|
170
|
-
IapErrorCode.E_UNKNOWN,
|
|
171
|
-
"Purchase failed with code: $responseCode",
|
|
172
|
-
)
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
Log.e(TAG, "Error Code: $responseCode")
|
|
176
|
-
return errorData
|
|
177
|
-
}
|
|
178
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
package expo.modules.iap
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Error codes for IAP operations - centralized error code management
|
|
5
|
-
* Single source of truth for all error codes used across the module
|
|
6
|
-
*/
|
|
7
|
-
object IapErrorCode {
|
|
8
|
-
// Connection and initialization errors
|
|
9
|
-
const val E_NOT_PREPARED = "E_NOT_PREPARED"
|
|
10
|
-
const val E_INIT_CONNECTION = "E_INIT_CONNECTION"
|
|
11
|
-
const val E_SERVICE_DISCONNECTED = "E_SERVICE_DISCONNECTED"
|
|
12
|
-
const val E_ALREADY_PREPARED = "E_ALREADY_PREPARED"
|
|
13
|
-
const val E_CONNECTION_CLOSED = "E_CONNECTION_CLOSED"
|
|
14
|
-
|
|
15
|
-
// Product and purchase errors
|
|
16
|
-
const val E_QUERY_PRODUCT = "E_QUERY_PRODUCT"
|
|
17
|
-
const val E_SKU_NOT_FOUND = "E_SKU_NOT_FOUND"
|
|
18
|
-
const val E_SKU_OFFER_MISMATCH = "E_SKU_OFFER_MISMATCH"
|
|
19
|
-
const val E_PURCHASE_ERROR = "E_PURCHASE_ERROR"
|
|
20
|
-
const val E_USER_CANCELLED = "E_USER_CANCELLED"
|
|
21
|
-
const val E_PENDING = "E_PENDING"
|
|
22
|
-
|
|
23
|
-
// Service and developer errors
|
|
24
|
-
const val E_SERVICE_ERROR = "E_SERVICE_ERROR"
|
|
25
|
-
const val E_DEVELOPER_ERROR = "E_DEVELOPER_ERROR"
|
|
26
|
-
const val E_ITEM_UNAVAILABLE = "E_ITEM_UNAVAILABLE"
|
|
27
|
-
const val E_ALREADY_OWNED = "E_ALREADY_OWNED"
|
|
28
|
-
const val E_ITEM_NOT_OWNED = "E_ITEM_NOT_OWNED"
|
|
29
|
-
|
|
30
|
-
// Network and billing errors
|
|
31
|
-
const val E_NETWORK_ERROR = "E_NETWORK_ERROR"
|
|
32
|
-
const val E_BILLING_UNAVAILABLE = "E_BILLING_UNAVAILABLE"
|
|
33
|
-
const val E_FEATURE_NOT_SUPPORTED = "E_FEATURE_NOT_SUPPORTED"
|
|
34
|
-
const val E_BILLING_RESPONSE_JSON_PARSE_ERROR = "E_BILLING_RESPONSE_JSON_PARSE_ERROR"
|
|
35
|
-
|
|
36
|
-
// Activity and UI errors
|
|
37
|
-
const val E_ACTIVITY_UNAVAILABLE = "E_ACTIVITY_UNAVAILABLE"
|
|
38
|
-
|
|
39
|
-
// User and remote errors
|
|
40
|
-
const val E_USER_ERROR = "E_USER_ERROR"
|
|
41
|
-
const val E_REMOTE_ERROR = "E_REMOTE_ERROR"
|
|
42
|
-
const val E_NOT_ENDED = "E_NOT_ENDED"
|
|
43
|
-
|
|
44
|
-
// Unknown error
|
|
45
|
-
const val E_UNKNOWN = "E_UNKNOWN"
|
|
46
|
-
|
|
47
|
-
// Empty SKU list error
|
|
48
|
-
const val E_EMPTY_SKU_LIST = "E_EMPTY_SKU_LIST"
|
|
49
|
-
|
|
50
|
-
// Cached map for Constants export - initialized once at class loading time
|
|
51
|
-
// Using constants as keys to avoid duplication and ensure type safety
|
|
52
|
-
private val _cachedMap: Map<String, String> = mapOf(
|
|
53
|
-
E_NOT_PREPARED to E_NOT_PREPARED,
|
|
54
|
-
E_INIT_CONNECTION to E_INIT_CONNECTION,
|
|
55
|
-
E_QUERY_PRODUCT to E_QUERY_PRODUCT,
|
|
56
|
-
E_UNKNOWN to E_UNKNOWN,
|
|
57
|
-
E_SKU_OFFER_MISMATCH to E_SKU_OFFER_MISMATCH,
|
|
58
|
-
E_SKU_NOT_FOUND to E_SKU_NOT_FOUND,
|
|
59
|
-
E_USER_CANCELLED to E_USER_CANCELLED,
|
|
60
|
-
E_DEVELOPER_ERROR to E_DEVELOPER_ERROR,
|
|
61
|
-
E_ITEM_UNAVAILABLE to E_ITEM_UNAVAILABLE,
|
|
62
|
-
E_SERVICE_ERROR to E_SERVICE_ERROR,
|
|
63
|
-
E_PURCHASE_ERROR to E_PURCHASE_ERROR,
|
|
64
|
-
E_ACTIVITY_UNAVAILABLE to E_ACTIVITY_UNAVAILABLE,
|
|
65
|
-
E_ALREADY_PREPARED to E_ALREADY_PREPARED,
|
|
66
|
-
E_PENDING to E_PENDING,
|
|
67
|
-
E_NOT_ENDED to E_NOT_ENDED,
|
|
68
|
-
E_NETWORK_ERROR to E_NETWORK_ERROR,
|
|
69
|
-
E_ALREADY_OWNED to E_ALREADY_OWNED,
|
|
70
|
-
E_REMOTE_ERROR to E_REMOTE_ERROR,
|
|
71
|
-
E_USER_ERROR to E_USER_ERROR,
|
|
72
|
-
E_BILLING_RESPONSE_JSON_PARSE_ERROR to E_BILLING_RESPONSE_JSON_PARSE_ERROR,
|
|
73
|
-
E_CONNECTION_CLOSED to E_CONNECTION_CLOSED,
|
|
74
|
-
E_SERVICE_DISCONNECTED to E_SERVICE_DISCONNECTED,
|
|
75
|
-
E_BILLING_UNAVAILABLE to E_BILLING_UNAVAILABLE,
|
|
76
|
-
E_FEATURE_NOT_SUPPORTED to E_FEATURE_NOT_SUPPORTED,
|
|
77
|
-
E_ITEM_NOT_OWNED to E_ITEM_NOT_OWNED,
|
|
78
|
-
E_EMPTY_SKU_LIST to E_EMPTY_SKU_LIST
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
// Return cached map reference - no new allocations on repeated calls
|
|
82
|
-
fun toMap(): Map<String, String> = _cachedMap
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* IAP Event constants
|
|
87
|
-
*/
|
|
88
|
-
object OpenIapEvent {
|
|
89
|
-
const val PURCHASE_UPDATED = "purchase-updated"
|
|
90
|
-
const val PURCHASE_ERROR = "purchase-error"
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Other IAP-related constants (Promise keys, etc.)
|
|
95
|
-
*/
|
|
96
|
-
object IapConstants {
|
|
97
|
-
const val PROMISE_BUY_ITEM = "PROMISE_BUY_ITEM"
|
|
98
|
-
}
|