expo-iap 2.2.7 → 2.2.8-rc.2
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/build/ExpoIap.types.d.ts +1 -4
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js +0 -5
- package/build/ExpoIap.types.js.map +1 -1
- package/build/index.d.ts +13 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +66 -60
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts +0 -5
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +0 -3
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +0 -5
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +0 -3
- package/build/modules/ios.js.map +1 -1
- package/build/useIap.d.ts +4 -2
- package/build/useIap.d.ts.map +1 -1
- package/build/useIap.js +10 -10
- package/build/useIap.js.map +1 -1
- package/iap.md +206 -220
- package/package.json +1 -1
- package/src/ExpoIap.types.ts +1 -5
- package/src/index.ts +126 -111
- package/src/modules/android.ts +0 -6
- package/src/modules/ios.ts +0 -6
- package/src/useIap.ts +18 -13
package/src/ExpoIap.types.ts
CHANGED
|
@@ -34,11 +34,7 @@ export type ProductBase = {
|
|
|
34
34
|
// Define literal platform types for better type discrimination
|
|
35
35
|
export type IosPlatform = {platform: 'ios'};
|
|
36
36
|
export type AndroidPlatform = {platform: 'android'};
|
|
37
|
-
|
|
38
|
-
export enum ProductType {
|
|
39
|
-
InAppPurchase = 'inapp',
|
|
40
|
-
Subscription = 'subs',
|
|
41
|
-
}
|
|
37
|
+
export type ProductType = 'inapp' | 'subs';
|
|
42
38
|
|
|
43
39
|
// Common base purchase type
|
|
44
40
|
export type PurchaseBase = {
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,6 @@ import {Platform} from 'react-native';
|
|
|
5
5
|
import {
|
|
6
6
|
Product,
|
|
7
7
|
ProductPurchase,
|
|
8
|
-
ProductType,
|
|
9
8
|
Purchase,
|
|
10
9
|
PurchaseError,
|
|
11
10
|
PurchaseResult,
|
|
@@ -24,16 +23,13 @@ import {
|
|
|
24
23
|
RequestPurchaseIosProps,
|
|
25
24
|
RequestSubscriptionIosProps,
|
|
26
25
|
} from './types/ExpoIapIos.types';
|
|
27
|
-
import {isProductIos
|
|
28
|
-
import {
|
|
29
|
-
isProductAndroid,
|
|
30
|
-
isSubscriptionProductAndroid,
|
|
31
|
-
} from './modules/android';
|
|
26
|
+
import {isProductIos} from './modules/ios';
|
|
27
|
+
import {isProductAndroid} from './modules/android';
|
|
32
28
|
|
|
33
29
|
export * from './modules/android';
|
|
34
30
|
export * from './modules/ios';
|
|
35
31
|
|
|
36
|
-
// Get the native constant value
|
|
32
|
+
// Get the native constant value
|
|
37
33
|
export const PI = ExpoIapModule.PI;
|
|
38
34
|
|
|
39
35
|
export enum IapEvent {
|
|
@@ -81,11 +77,7 @@ export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
|
81
77
|
return items.filter((item: unknown) => isProductIos<Product>(item));
|
|
82
78
|
},
|
|
83
79
|
android: async () => {
|
|
84
|
-
const products = await ExpoIapModule.getItemsByType(
|
|
85
|
-
ProductType.InAppPurchase,
|
|
86
|
-
skus,
|
|
87
|
-
);
|
|
88
|
-
|
|
80
|
+
const products = await ExpoIapModule.getItemsByType('inapp', skus);
|
|
89
81
|
return products.filter((product: unknown) =>
|
|
90
82
|
isProductAndroid<Product>(product),
|
|
91
83
|
);
|
|
@@ -105,7 +97,7 @@ export const getSubscriptions = async (
|
|
|
105
97
|
ios: async () => {
|
|
106
98
|
const rawItems = await ExpoIapModule.getItems(skus);
|
|
107
99
|
return rawItems.filter((item: unknown) => {
|
|
108
|
-
if (!
|
|
100
|
+
if (!isProductIos(item)) return false;
|
|
109
101
|
return (
|
|
110
102
|
typeof item === 'object' &&
|
|
111
103
|
item !== null &&
|
|
@@ -118,7 +110,7 @@ export const getSubscriptions = async (
|
|
|
118
110
|
android: async () => {
|
|
119
111
|
const rawItems = await ExpoIapModule.getItemsByType('subs', skus);
|
|
120
112
|
return rawItems.filter((item: unknown) => {
|
|
121
|
-
if (!
|
|
113
|
+
if (!isProductAndroid(item)) return false;
|
|
122
114
|
return (
|
|
123
115
|
typeof item === 'object' &&
|
|
124
116
|
item !== null &&
|
|
@@ -152,12 +144,9 @@ export const getPurchaseHistory = ({
|
|
|
152
144
|
);
|
|
153
145
|
},
|
|
154
146
|
android: async () => {
|
|
155
|
-
const products = await ExpoIapModule.getPurchaseHistoryByType(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const subscriptions = await ExpoIapModule.getPurchaseHistoryByType(
|
|
159
|
-
ProductType.Subscription,
|
|
160
|
-
);
|
|
147
|
+
const products = await ExpoIapModule.getPurchaseHistoryByType('inapp');
|
|
148
|
+
const subscriptions =
|
|
149
|
+
await ExpoIapModule.getPurchaseHistoryByType('subs');
|
|
161
150
|
return products.concat(subscriptions);
|
|
162
151
|
},
|
|
163
152
|
}) || (() => Promise.resolve([]))
|
|
@@ -178,12 +167,9 @@ export const getAvailablePurchases = ({
|
|
|
178
167
|
onlyIncludeActiveItems,
|
|
179
168
|
),
|
|
180
169
|
android: async () => {
|
|
181
|
-
const products = await ExpoIapModule.getAvailableItemsByType(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const subscriptions = await ExpoIapModule.getAvailableItemsByType(
|
|
185
|
-
ProductType.Subscription,
|
|
186
|
-
);
|
|
170
|
+
const products = await ExpoIapModule.getAvailableItemsByType('inapp');
|
|
171
|
+
const subscriptions =
|
|
172
|
+
await ExpoIapModule.getAvailableItemsByType('subs');
|
|
187
173
|
return products.concat(subscriptions);
|
|
188
174
|
},
|
|
189
175
|
}) || (() => Promise.resolve([]))
|
|
@@ -192,9 +178,7 @@ export const getAvailablePurchases = ({
|
|
|
192
178
|
const offerToRecordIos = (
|
|
193
179
|
offer: PaymentDiscount | undefined,
|
|
194
180
|
): Record<keyof PaymentDiscount, string> | undefined => {
|
|
195
|
-
if (!offer)
|
|
196
|
-
return undefined;
|
|
197
|
-
}
|
|
181
|
+
if (!offer) return undefined;
|
|
198
182
|
return {
|
|
199
183
|
identifier: offer.identifier,
|
|
200
184
|
keyIdentifier: offer.keyIdentifier,
|
|
@@ -204,44 +188,77 @@ const offerToRecordIos = (
|
|
|
204
188
|
};
|
|
205
189
|
};
|
|
206
190
|
|
|
191
|
+
// Define discriminated union with explicit type parameter
|
|
192
|
+
type PurchaseRequest =
|
|
193
|
+
| {
|
|
194
|
+
request: RequestPurchaseIosProps | RequestPurchaseAndroidProps;
|
|
195
|
+
type?: 'inapp';
|
|
196
|
+
}
|
|
197
|
+
| {
|
|
198
|
+
request: RequestSubscriptionAndroidProps | RequestSubscriptionIosProps;
|
|
199
|
+
type: 'subs';
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Type guards for request objects
|
|
203
|
+
const isIosRequest = (
|
|
204
|
+
request: any,
|
|
205
|
+
): request is RequestPurchaseIosProps | RequestSubscriptionIosProps =>
|
|
206
|
+
'sku' in request && typeof request.sku === 'string';
|
|
207
|
+
|
|
207
208
|
export const requestPurchase = (
|
|
208
|
-
|
|
209
|
-
): Promise<
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
209
|
+
requestObj: PurchaseRequest,
|
|
210
|
+
): Promise<
|
|
211
|
+
| ProductPurchase
|
|
212
|
+
| SubscriptionPurchase
|
|
213
|
+
| ProductPurchase[]
|
|
214
|
+
| SubscriptionPurchase[]
|
|
215
|
+
| void
|
|
216
|
+
> => {
|
|
217
|
+
const {request, type = 'inapp'} = requestObj;
|
|
218
|
+
|
|
219
|
+
if (Platform.OS === 'ios') {
|
|
220
|
+
if (!isIosRequest(request)) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
'Invalid request for iOS. The `sku` property is required and must be a string.',
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const {
|
|
227
|
+
sku,
|
|
228
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
229
|
+
appAccountToken,
|
|
230
|
+
quantity,
|
|
231
|
+
withOffer,
|
|
232
|
+
} = request;
|
|
233
|
+
|
|
234
|
+
return (async () => {
|
|
235
|
+
const offer = offerToRecordIos(withOffer);
|
|
236
|
+
const purchase = await ExpoIapModule.buyProduct(
|
|
237
|
+
sku,
|
|
238
|
+
andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
239
|
+
appAccountToken,
|
|
240
|
+
quantity ?? -1,
|
|
241
|
+
offer,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
return type === 'inapp'
|
|
245
|
+
? (purchase as ProductPurchase)
|
|
246
|
+
: (purchase as SubscriptionPurchase);
|
|
247
|
+
})();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (Platform.OS === 'android') {
|
|
251
|
+
if (type === 'inapp') {
|
|
252
|
+
const {
|
|
253
|
+
skus,
|
|
254
|
+
obfuscatedAccountIdAndroid,
|
|
255
|
+
obfuscatedProfileIdAndroid,
|
|
256
|
+
isOfferPersonalized,
|
|
257
|
+
} = request as RequestPurchaseAndroidProps;
|
|
258
|
+
|
|
259
|
+
return (async () => {
|
|
243
260
|
return ExpoIapModule.buyItemByType({
|
|
244
|
-
type:
|
|
261
|
+
type: 'inapp',
|
|
245
262
|
skuArr: skus,
|
|
246
263
|
purchaseToken: undefined,
|
|
247
264
|
replacementMode: -1,
|
|
@@ -249,60 +266,58 @@ export const requestPurchase = (
|
|
|
249
266
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
250
267
|
offerTokenArr: [],
|
|
251
268
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
252
|
-
})
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
)();
|
|
269
|
+
}) as Promise<ProductPurchase[]>;
|
|
270
|
+
})();
|
|
271
|
+
}
|
|
256
272
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
appAccountToken,
|
|
270
|
-
quantity,
|
|
271
|
-
withOffer,
|
|
272
|
-
} = request as RequestSubscriptionIosProps;
|
|
273
|
-
const offer = offerToRecordIos(withOffer);
|
|
274
|
-
const purchase = await ExpoIapModule.buyProduct(
|
|
275
|
-
sku,
|
|
276
|
-
andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
277
|
-
appAccountToken,
|
|
278
|
-
quantity ?? -1,
|
|
279
|
-
offer,
|
|
280
|
-
);
|
|
281
|
-
return Promise.resolve(purchase as SubscriptionPurchase);
|
|
282
|
-
},
|
|
283
|
-
android: async () => {
|
|
284
|
-
const {
|
|
285
|
-
skus,
|
|
286
|
-
isOfferPersonalized,
|
|
287
|
-
obfuscatedAccountIdAndroid,
|
|
288
|
-
obfuscatedProfileIdAndroid,
|
|
289
|
-
subscriptionOffers,
|
|
290
|
-
replacementModeAndroid,
|
|
291
|
-
purchaseTokenAndroid,
|
|
292
|
-
} = request as RequestSubscriptionAndroidProps;
|
|
273
|
+
if (type === 'subs') {
|
|
274
|
+
const {
|
|
275
|
+
skus,
|
|
276
|
+
obfuscatedAccountIdAndroid,
|
|
277
|
+
obfuscatedProfileIdAndroid,
|
|
278
|
+
isOfferPersonalized,
|
|
279
|
+
subscriptionOffers = [],
|
|
280
|
+
replacementModeAndroid = -1,
|
|
281
|
+
purchaseTokenAndroid,
|
|
282
|
+
} = request as RequestSubscriptionAndroidProps;
|
|
283
|
+
|
|
284
|
+
return (async () => {
|
|
293
285
|
return ExpoIapModule.buyItemByType({
|
|
294
|
-
type:
|
|
295
|
-
skuArr: skus
|
|
286
|
+
type: 'subs',
|
|
287
|
+
skuArr: skus,
|
|
296
288
|
purchaseToken: purchaseTokenAndroid,
|
|
297
289
|
replacementMode: replacementModeAndroid,
|
|
298
290
|
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
299
291
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
300
292
|
offerTokenArr: subscriptionOffers.map((so) => so.offerToken),
|
|
301
293
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
302
|
-
})
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
294
|
+
}) as Promise<SubscriptionPurchase[]>;
|
|
295
|
+
})();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
throw new Error(
|
|
299
|
+
"Invalid request for Android: Expected a 'RequestPurchaseAndroidProps' object with a valid 'skus' array or a 'RequestSubscriptionAndroidProps' object with 'skus' and 'subscriptionOffers'.",
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return Promise.resolve(); // Fallback for unsupported platforms
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0+.
|
|
308
|
+
*/
|
|
309
|
+
export const requestSubscription = async (
|
|
310
|
+
request: RequestSubscriptionProps,
|
|
311
|
+
): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> => {
|
|
312
|
+
console.warn(
|
|
313
|
+
"`requestSubscription` is deprecated. Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0+.",
|
|
314
|
+
);
|
|
315
|
+
return (await requestPurchase({request, type: 'subs'})) as
|
|
316
|
+
| SubscriptionPurchase
|
|
317
|
+
| SubscriptionPurchase[]
|
|
318
|
+
| null
|
|
319
|
+
| void;
|
|
320
|
+
};
|
|
306
321
|
|
|
307
322
|
export const finishTransaction = ({
|
|
308
323
|
purchase,
|
package/src/modules/android.ts
CHANGED
|
@@ -15,12 +15,6 @@ export function isProductAndroid<T extends {platform?: string}>(
|
|
|
15
15
|
);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function isSubscriptionProductAndroid<T extends {platform?: string}>(
|
|
19
|
-
item: unknown,
|
|
20
|
-
): item is T & {platform: 'android'} {
|
|
21
|
-
return isProductAndroid(item);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
18
|
/**
|
|
25
19
|
* Deep link to subscriptions screen on Android.
|
|
26
20
|
* @param {string} sku The product's SKU (on Android)
|
package/src/modules/ios.ts
CHANGED
|
@@ -32,12 +32,6 @@ export function isProductIos<T extends {platform?: string}>(
|
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function isSubscriptionProductIos<T extends {platform?: string}>(
|
|
36
|
-
item: unknown,
|
|
37
|
-
): item is T & {platform: 'ios'} {
|
|
38
|
-
return isProductIos(item);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
35
|
// Functions
|
|
42
36
|
/**
|
|
43
37
|
* Sync state with Appstore (iOS only)
|
package/src/useIap.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getPurchaseHistory,
|
|
10
10
|
finishTransaction as finishTransactionInternal,
|
|
11
11
|
getSubscriptions,
|
|
12
|
+
requestPurchase as requestPurchaseInternal,
|
|
12
13
|
} from './';
|
|
13
14
|
import {useCallback, useEffect, useState, useRef} from 'react';
|
|
14
15
|
import {
|
|
@@ -40,10 +41,11 @@ type IAP_STATUS = {
|
|
|
40
41
|
purchase: Purchase;
|
|
41
42
|
isConsumable?: boolean;
|
|
42
43
|
}) => Promise<string | boolean | PurchaseResult | void>;
|
|
43
|
-
getAvailablePurchases: () => Promise<void>;
|
|
44
|
-
getPurchaseHistories: () => Promise<void>;
|
|
44
|
+
getAvailablePurchases: (skus: string[]) => Promise<void>;
|
|
45
|
+
getPurchaseHistories: (skus: string[]) => Promise<void>;
|
|
45
46
|
getProducts: (skus: string[]) => Promise<void>;
|
|
46
47
|
getSubscriptions: (skus: string[]) => Promise<void>;
|
|
48
|
+
requestPurchase: typeof requestPurchaseInternal;
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
export function useIAP(): IAP_STATUS {
|
|
@@ -63,29 +65,31 @@ export function useIAP(): IAP_STATUS {
|
|
|
63
65
|
const [currentPurchaseError, setCurrentPurchaseError] =
|
|
64
66
|
useState<PurchaseError>();
|
|
65
67
|
|
|
66
|
-
// 구독을 훅 인스턴스별로 관리하기 위한 ref
|
|
67
68
|
const subscriptionsRef = useRef<{
|
|
68
69
|
purchaseUpdate?: Subscription;
|
|
69
70
|
purchaseError?: Subscription;
|
|
70
71
|
promotedProductsIos?: Subscription;
|
|
71
72
|
}>({});
|
|
72
73
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const getProductsInternal = useCallback(
|
|
75
|
+
async (skus: string[]): Promise<void> => {
|
|
76
|
+
setProducts(await getProducts(skus));
|
|
77
|
+
},
|
|
78
|
+
[],
|
|
79
|
+
);
|
|
76
80
|
|
|
77
|
-
const
|
|
81
|
+
const getSubscriptionsInternal = useCallback(
|
|
78
82
|
async (skus: string[]): Promise<void> => {
|
|
79
83
|
setSubscriptions(await getSubscriptions(skus));
|
|
80
84
|
},
|
|
81
85
|
[],
|
|
82
86
|
);
|
|
83
87
|
|
|
84
|
-
const
|
|
88
|
+
const getAvailablePurchasesInternal = useCallback(async (): Promise<void> => {
|
|
85
89
|
setAvailablePurchases(await getAvailablePurchases());
|
|
86
90
|
}, []);
|
|
87
91
|
|
|
88
|
-
const
|
|
92
|
+
const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {
|
|
89
93
|
setPurchaseHistories(await getPurchaseHistory());
|
|
90
94
|
}, []);
|
|
91
95
|
|
|
@@ -172,9 +176,10 @@ export function useIAP(): IAP_STATUS {
|
|
|
172
176
|
availablePurchases,
|
|
173
177
|
currentPurchase,
|
|
174
178
|
currentPurchaseError,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
getAvailablePurchases: getAvailablePurchasesInternal,
|
|
180
|
+
getPurchaseHistories: getPurchaseHistoriesInternal,
|
|
181
|
+
getProducts: getProductsInternal,
|
|
182
|
+
getSubscriptions: getSubscriptionsInternal,
|
|
183
|
+
requestPurchase: requestPurchaseInternal,
|
|
179
184
|
};
|
|
180
185
|
}
|