expo-iap 2.1.0 → 2.2.0-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/README.md +8 -27
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +10 -9
- package/build/ExpoIap.types.d.ts +31 -40
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js +0 -11
- package/build/ExpoIap.types.js.map +1 -1
- package/build/index.d.ts +2 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +32 -46
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts +12 -4
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +8 -4
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +15 -7
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +9 -5
- package/build/modules/ios.js.map +1 -1
- package/build/types/ExpoIapAndroid.types.d.ts +31 -31
- package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
- package/build/types/ExpoIapAndroid.types.js +6 -0
- package/build/types/ExpoIapAndroid.types.js.map +1 -1
- package/build/types/ExpoIapIos.types.d.ts +31 -34
- package/build/types/ExpoIapIos.types.d.ts.map +1 -1
- package/build/types/ExpoIapIos.types.js.map +1 -1
- package/build/useIap.d.ts +1 -2
- package/build/useIap.d.ts.map +1 -1
- package/build/useIap.js +3 -3
- package/build/useIap.js.map +1 -1
- package/bun.lockb +0 -0
- package/iap.md +161 -0
- package/ios/ExpoIapModule.swift +116 -30
- package/package.json +5 -5
- package/plugin/build/withIAP.js +29 -2
- package/plugin/src/withIAP.ts +41 -4
- package/src/ExpoIap.types.ts +48 -48
- package/src/index.ts +48 -96
- package/src/modules/android.ts +16 -12
- package/src/modules/ios.ts +20 -17
- package/src/types/ExpoIapAndroid.types.ts +37 -33
- package/src/types/ExpoIapIos.types.ts +36 -40
- package/src/useIap.ts +5 -8
package/src/index.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// and on native platforms to ExpoIap.ts
|
|
3
3
|
import {NativeModulesProxy, EventEmitter} from 'expo-modules-core';
|
|
4
4
|
import {Platform} from 'react-native';
|
|
5
|
-
|
|
6
5
|
import {
|
|
7
6
|
Product,
|
|
8
7
|
ProductPurchase,
|
|
@@ -21,13 +20,14 @@ import {
|
|
|
21
20
|
} from './types/ExpoIapAndroid.types';
|
|
22
21
|
import {
|
|
23
22
|
PaymentDiscount,
|
|
24
|
-
ProductIos,
|
|
25
23
|
RequestPurchaseIosProps,
|
|
26
24
|
RequestSubscriptionIosProps,
|
|
27
|
-
SubscriptionProductIos,
|
|
28
|
-
TransactionSk2,
|
|
29
25
|
} from './types/ExpoIapIos.types';
|
|
30
|
-
import {isProductIos} from './modules/ios';
|
|
26
|
+
import {isProductIos, isSubscriptionProductIos} from './modules/ios';
|
|
27
|
+
import {
|
|
28
|
+
isProductAndroid,
|
|
29
|
+
isSubscriptionProductAndroid,
|
|
30
|
+
} from './modules/android';
|
|
31
31
|
|
|
32
32
|
export * from './modules/android';
|
|
33
33
|
export * from './modules/ios';
|
|
@@ -70,22 +70,25 @@ export function initConnection() {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
73
|
+
console.log('getProducts', skus);
|
|
73
74
|
if (!skus?.length) {
|
|
74
75
|
return Promise.reject(new Error('"skus" is required'));
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
return Platform.select({
|
|
78
79
|
ios: async () => {
|
|
79
|
-
const items =
|
|
80
|
-
|
|
80
|
+
const items = await ExpoIapModule.getItems(skus);
|
|
81
|
+
console.log('items', items);
|
|
82
|
+
return items.filter((item: unknown) => isProductIos<Product>(item));
|
|
81
83
|
},
|
|
82
84
|
android: async () => {
|
|
83
85
|
const products = await ExpoIapModule.getItemsByType(
|
|
84
86
|
ProductType.InAppPurchase,
|
|
85
87
|
skus,
|
|
86
88
|
);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
return products.filter((product: unknown) =>
|
|
90
|
+
isProductAndroid<Product>(product),
|
|
91
|
+
);
|
|
89
92
|
},
|
|
90
93
|
default: () => Promise.reject(new Error('Unsupported Platform')),
|
|
91
94
|
})();
|
|
@@ -99,15 +102,33 @@ export const getSubscriptions = async (
|
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
return Platform.select({
|
|
102
|
-
ios: async ()
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
ios: async () => {
|
|
106
|
+
const rawItems = await ExpoIapModule.getItems(skus);
|
|
107
|
+
|
|
108
|
+
return rawItems.filter((item: unknown) => {
|
|
109
|
+
if (!isSubscriptionProductIos(item)) return false;
|
|
110
|
+
return (
|
|
111
|
+
typeof item === 'object' &&
|
|
112
|
+
item !== null &&
|
|
113
|
+
'id' in item &&
|
|
114
|
+
typeof item.id === 'string' &&
|
|
115
|
+
skus.includes(item.id)
|
|
116
|
+
);
|
|
117
|
+
}) as SubscriptionProduct[];
|
|
108
118
|
},
|
|
109
119
|
android: async () => {
|
|
110
|
-
|
|
120
|
+
const rawItems = await ExpoIapModule.getItemsByType('subs', skus);
|
|
121
|
+
|
|
122
|
+
return rawItems.filter((item: unknown) => {
|
|
123
|
+
if (!isSubscriptionProductAndroid(item)) return false;
|
|
124
|
+
return (
|
|
125
|
+
typeof item === 'object' &&
|
|
126
|
+
item !== null &&
|
|
127
|
+
'id' in item &&
|
|
128
|
+
typeof item.id === 'string' &&
|
|
129
|
+
skus.includes(item.id)
|
|
130
|
+
);
|
|
131
|
+
}) as SubscriptionProduct[];
|
|
111
132
|
},
|
|
112
133
|
default: () => Promise.reject(new Error('Unsupported Platform')),
|
|
113
134
|
})();
|
|
@@ -119,11 +140,9 @@ export async function endConnection(): Promise<boolean> {
|
|
|
119
140
|
|
|
120
141
|
export const getPurchaseHistory = ({
|
|
121
142
|
alsoPublishToEventListener = false,
|
|
122
|
-
automaticallyFinishRestoredTransactions = true,
|
|
123
143
|
onlyIncludeActiveItems = false,
|
|
124
144
|
}: {
|
|
125
145
|
alsoPublishToEventListener?: boolean;
|
|
126
|
-
automaticallyFinishRestoredTransactions?: boolean;
|
|
127
146
|
onlyIncludeActiveItems?: boolean;
|
|
128
147
|
} = {}): Promise<ProductPurchase[]> =>
|
|
129
148
|
(
|
|
@@ -150,11 +169,9 @@ export const getPurchaseHistory = ({
|
|
|
150
169
|
|
|
151
170
|
export const getAvailablePurchases = ({
|
|
152
171
|
alsoPublishToEventListener = false,
|
|
153
|
-
automaticallyFinishRestoredTransactions = false,
|
|
154
172
|
onlyIncludeActiveItems = true,
|
|
155
173
|
}: {
|
|
156
174
|
alsoPublishToEventListener?: boolean;
|
|
157
|
-
automaticallyFinishRestoredTransactions?: boolean;
|
|
158
175
|
onlyIncludeActiveItems?: boolean;
|
|
159
176
|
} = {}): Promise<ProductPurchase[]> =>
|
|
160
177
|
(
|
|
@@ -193,43 +210,6 @@ const offerToRecordIos = (
|
|
|
193
210
|
};
|
|
194
211
|
};
|
|
195
212
|
|
|
196
|
-
const iosTransactionToPurchaseMap = ({
|
|
197
|
-
id,
|
|
198
|
-
originalPurchaseDate,
|
|
199
|
-
productID,
|
|
200
|
-
purchaseDate,
|
|
201
|
-
purchasedQuantity,
|
|
202
|
-
originalID,
|
|
203
|
-
verificationResult,
|
|
204
|
-
appAccountToken,
|
|
205
|
-
jsonRepresentation,
|
|
206
|
-
}: TransactionSk2): Purchase => {
|
|
207
|
-
let transactionReasonIOS;
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
if (jsonRepresentation) {
|
|
211
|
-
const transactionData = JSON.parse(jsonRepresentation);
|
|
212
|
-
transactionReasonIOS = transactionData.transactionReason;
|
|
213
|
-
}
|
|
214
|
-
} catch (e) {
|
|
215
|
-
console.log('SK2 Error parsing jsonRepresentation', e);
|
|
216
|
-
}
|
|
217
|
-
const purchase: Purchase = {
|
|
218
|
-
productId: productID,
|
|
219
|
-
transactionId: String(id),
|
|
220
|
-
transactionDate: purchaseDate, //??
|
|
221
|
-
transactionReceipt: '', // Not available
|
|
222
|
-
purchaseToken: '', //Not available
|
|
223
|
-
quantityIOS: purchasedQuantity,
|
|
224
|
-
originalTransactionDateIOS: originalPurchaseDate,
|
|
225
|
-
originalTransactionIdentifierIOS: originalID,
|
|
226
|
-
verificationResultIOS: verificationResult ?? '',
|
|
227
|
-
appAccountToken: appAccountToken ?? '',
|
|
228
|
-
transactionReasonIOS: transactionReasonIOS ?? '',
|
|
229
|
-
};
|
|
230
|
-
return purchase;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
213
|
export const requestPurchase = (
|
|
234
214
|
request: RequestPurchaseIosProps | RequestPurchaseAndroidProps,
|
|
235
215
|
): Promise<ProductPurchase | ProductPurchase[] | void> =>
|
|
@@ -240,31 +220,17 @@ export const requestPurchase = (
|
|
|
240
220
|
throw new Error('sku is required for iOS purchase');
|
|
241
221
|
}
|
|
242
222
|
|
|
243
|
-
const {
|
|
244
|
-
sku,
|
|
245
|
-
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
246
|
-
appAccountToken,
|
|
247
|
-
quantity,
|
|
248
|
-
withOffer,
|
|
249
|
-
} = request;
|
|
250
|
-
|
|
251
|
-
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
252
|
-
console.warn(
|
|
253
|
-
'You are dangerously allowing expo-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.',
|
|
254
|
-
);
|
|
255
|
-
}
|
|
223
|
+
const {sku, appAccountToken, quantity, withOffer} = request;
|
|
256
224
|
|
|
257
225
|
const offer = offerToRecordIos(withOffer);
|
|
258
226
|
|
|
259
|
-
const
|
|
227
|
+
const purchase = await ExpoIapModule.buyProduct(
|
|
260
228
|
sku,
|
|
261
|
-
andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
262
229
|
appAccountToken,
|
|
263
230
|
quantity ?? -1,
|
|
264
231
|
offer,
|
|
265
232
|
);
|
|
266
233
|
|
|
267
|
-
const purchase = iosTransactionToPurchaseMap(result);
|
|
268
234
|
return Promise.resolve(purchase);
|
|
269
235
|
},
|
|
270
236
|
android: async () => {
|
|
@@ -303,35 +269,21 @@ export const requestSubscription = (
|
|
|
303
269
|
throw new Error('sku is required for iOS subscriptions');
|
|
304
270
|
}
|
|
305
271
|
|
|
306
|
-
const {
|
|
307
|
-
|
|
308
|
-
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
309
|
-
appAccountToken,
|
|
310
|
-
quantity,
|
|
311
|
-
withOffer,
|
|
312
|
-
} = request as RequestSubscriptionIosProps;
|
|
313
|
-
|
|
314
|
-
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
315
|
-
console.warn(
|
|
316
|
-
'You are dangerously allowing expo-iap to finish your transaction automatically. You should set andDangerouslyFinishTransactionAutomatically to false when calling requestPurchase and call finishTransaction manually when you have delivered the purchased goods to the user. It defaults to true to provide backwards compatibility. Will default to false in version 4.0.0.',
|
|
317
|
-
);
|
|
318
|
-
}
|
|
272
|
+
const {sku, appAccountToken, quantity, withOffer} =
|
|
273
|
+
request as RequestSubscriptionIosProps;
|
|
319
274
|
|
|
320
275
|
const offer = offerToRecordIos(withOffer);
|
|
321
276
|
|
|
322
|
-
const purchase =
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
quantity ?? -1,
|
|
328
|
-
offer,
|
|
329
|
-
),
|
|
277
|
+
const purchase = await ExpoIapModule.buyProduct(
|
|
278
|
+
sku,
|
|
279
|
+
appAccountToken,
|
|
280
|
+
quantity ?? -1,
|
|
281
|
+
offer,
|
|
330
282
|
);
|
|
331
|
-
|
|
283
|
+
|
|
284
|
+
return Promise.resolve(purchase as SubscriptionPurchase);
|
|
332
285
|
},
|
|
333
286
|
android: async () => {
|
|
334
|
-
console.log('requestSubscription', request);
|
|
335
287
|
const {
|
|
336
288
|
skus,
|
|
337
289
|
isOfferPersonalized,
|
package/src/modules/android.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import {Linking} from 'react-native';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
ProductAndroid,
|
|
5
|
-
ReceiptAndroid,
|
|
6
|
-
SubscriptionProductAndroid,
|
|
7
|
-
} from '../types/ExpoIapAndroid.types';
|
|
2
|
+
import {PurchaseResult} from '../ExpoIap.types';
|
|
3
|
+
import {ReceiptAndroid} from '../types/ExpoIapAndroid.types';
|
|
8
4
|
import ExpoIapModule from '../ExpoIapModule';
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
// Type guards
|
|
7
|
+
export function isProductAndroid<T extends {platform?: string}>(
|
|
8
|
+
item: unknown,
|
|
9
|
+
): item is T & {platform: 'android'} {
|
|
10
|
+
return (
|
|
11
|
+
item != null &&
|
|
12
|
+
typeof item === 'object' &&
|
|
13
|
+
'platform' in item &&
|
|
14
|
+
item.platform === 'android'
|
|
15
|
+
);
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
export function isSubscriptionProductAndroid(
|
|
15
|
-
|
|
16
|
-
):
|
|
17
|
-
return (
|
|
18
|
+
export function isSubscriptionProductAndroid<T extends {platform?: string}>(
|
|
19
|
+
item: unknown,
|
|
20
|
+
): item is T & {platform: 'android'} {
|
|
21
|
+
return isProductAndroid(item);
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
/**
|
package/src/modules/ios.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import {Platform} from 'react-native';
|
|
2
2
|
import {emitter, IapEvent} from '..';
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
ProductIos,
|
|
6
|
-
ProductStatusIos,
|
|
7
|
-
SubscriptionProductIos,
|
|
8
|
-
TransactionSk2,
|
|
9
|
-
} from '../types/ExpoIapIos.types';
|
|
3
|
+
import {ProductPurchase, PurchaseError} from '../ExpoIap.types';
|
|
4
|
+
import type {ProductStatusIos} from '../types/ExpoIapIos.types';
|
|
10
5
|
import ExpoIapModule from '../ExpoIapModule';
|
|
11
6
|
|
|
12
7
|
export type TransactionEvent = {
|
|
13
|
-
transaction?:
|
|
8
|
+
transaction?: ProductPurchase;
|
|
14
9
|
error?: PurchaseError;
|
|
15
10
|
};
|
|
16
11
|
|
|
@@ -25,17 +20,25 @@ export const transactionUpdatedIos = (
|
|
|
25
20
|
return emitter.addListener(IapEvent.TransactionIapUpdated, listener);
|
|
26
21
|
};
|
|
27
22
|
|
|
28
|
-
//
|
|
29
|
-
export function isProductIos
|
|
30
|
-
|
|
23
|
+
// Type guards
|
|
24
|
+
export function isProductIos<T extends {platform?: string}>(
|
|
25
|
+
item: unknown,
|
|
26
|
+
): item is T & {platform: 'ios'} {
|
|
27
|
+
return (
|
|
28
|
+
item != null &&
|
|
29
|
+
typeof item === 'object' &&
|
|
30
|
+
'platform' in item &&
|
|
31
|
+
item.platform === 'ios'
|
|
32
|
+
);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
export function isSubscriptionProductIos(
|
|
34
|
-
|
|
35
|
-
):
|
|
36
|
-
return (
|
|
35
|
+
export function isSubscriptionProductIos<T extends {platform?: string}>(
|
|
36
|
+
item: unknown,
|
|
37
|
+
): item is T & {platform: 'ios'} {
|
|
38
|
+
return isProductIos(item);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// Functions
|
|
39
42
|
/**
|
|
40
43
|
* Sync state with Appstore (iOS only)
|
|
41
44
|
* https://developer.apple.com/documentation/storekit/appstore/3791906-sync
|
|
@@ -58,13 +61,13 @@ export const subscriptionStatus = (sku: string): Promise<ProductStatusIos[]> =>
|
|
|
58
61
|
/**
|
|
59
62
|
*
|
|
60
63
|
*/
|
|
61
|
-
export const currentEntitlement = (sku: string): Promise<
|
|
64
|
+
export const currentEntitlement = (sku: string): Promise<ProductPurchase> =>
|
|
62
65
|
ExpoIapModule.currentEntitlement(sku);
|
|
63
66
|
|
|
64
67
|
/**
|
|
65
68
|
*
|
|
66
69
|
*/
|
|
67
|
-
export const latestTransaction = (sku: string): Promise<
|
|
70
|
+
export const latestTransaction = (sku: string): Promise<ProductPurchase> =>
|
|
68
71
|
ExpoIapModule.latestTransaction(sku);
|
|
69
72
|
|
|
70
73
|
/**
|
|
@@ -1,63 +1,48 @@
|
|
|
1
|
+
import {PurchaseBase, ProductBase} from '../ExpoIap.types';
|
|
2
|
+
|
|
1
3
|
type OneTimePurchaseOfferDetails = {
|
|
2
4
|
priceCurrencyCode: string;
|
|
3
5
|
formattedPrice: string;
|
|
4
6
|
priceAmountMicros: string;
|
|
5
7
|
};
|
|
6
8
|
|
|
7
|
-
type
|
|
8
|
-
basePlanId: string;
|
|
9
|
-
offerId: string;
|
|
10
|
-
offerToken: string;
|
|
11
|
-
offerTags: string[];
|
|
12
|
-
pricingPhases: PricingPhases;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type PricingPhases = {
|
|
16
|
-
pricingPhaseList: PricingPhase[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
type PricingPhase = {
|
|
9
|
+
type PricingPhaseAndroid = {
|
|
20
10
|
formattedPrice: string;
|
|
21
11
|
priceCurrencyCode: string;
|
|
12
|
+
// P1W, P1M, P1Y
|
|
22
13
|
billingPeriod: string;
|
|
23
14
|
billingCycleCount: number;
|
|
24
15
|
priceAmountMicros: string;
|
|
25
16
|
recurrenceMode: number;
|
|
26
17
|
};
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
type PricingPhasesAndroid = {
|
|
20
|
+
pricingPhaseList: PricingPhaseAndroid[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type SubscriptionOfferDetail = {
|
|
24
|
+
basePlanId: string;
|
|
25
|
+
offerId: string;
|
|
26
|
+
offerToken: string;
|
|
27
|
+
offerTags: string[];
|
|
28
|
+
pricingPhases: PricingPhasesAndroid;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ProductAndroid = ProductBase & {
|
|
33
32
|
name: string;
|
|
34
33
|
oneTimePurchaseOfferDetails?: OneTimePurchaseOfferDetails;
|
|
35
34
|
subscriptionOfferDetails?: SubscriptionOfferDetail[];
|
|
36
35
|
};
|
|
37
36
|
|
|
38
|
-
type PricingPhaseAndroid = {
|
|
39
|
-
formattedPrice: string;
|
|
40
|
-
priceCurrencyCode: string;
|
|
41
|
-
// P1W, P1M, P1Y
|
|
42
|
-
billingPeriod: string;
|
|
43
|
-
billingCycleCount: number;
|
|
44
|
-
priceAmountMicros: string;
|
|
45
|
-
recurrenceMode: number;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
37
|
type SubscriptionOfferAndroid = {
|
|
49
38
|
basePlanId: string;
|
|
50
39
|
offerId: string | null;
|
|
51
40
|
offerToken: string;
|
|
52
|
-
pricingPhases:
|
|
41
|
+
pricingPhases: PricingPhasesAndroid;
|
|
53
42
|
offerTags: string[];
|
|
54
43
|
};
|
|
55
44
|
|
|
56
45
|
export type SubscriptionProductAndroid = ProductAndroid & {
|
|
57
|
-
name: string;
|
|
58
|
-
title: string;
|
|
59
|
-
description: string;
|
|
60
|
-
productId: string;
|
|
61
46
|
subscriptionOfferDetails: SubscriptionOfferAndroid[];
|
|
62
47
|
};
|
|
63
48
|
|
|
@@ -121,3 +106,22 @@ export enum FeatureTypeAndroid {
|
|
|
121
106
|
/** Subscriptions update/replace. */
|
|
122
107
|
SUBSCRIPTIONS_UPDATE = 'SUBSCRIPTIONS_UPDATE',
|
|
123
108
|
}
|
|
109
|
+
|
|
110
|
+
export enum PurchaseStateAndroid {
|
|
111
|
+
UNSPECIFIED_STATE = 0,
|
|
112
|
+
PURCHASED = 1,
|
|
113
|
+
PENDING = 2,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type ProductPurchaseAndroid = PurchaseBase & {
|
|
117
|
+
ids?: string[];
|
|
118
|
+
dataAndroid?: string;
|
|
119
|
+
signatureAndroid?: string;
|
|
120
|
+
autoRenewingAndroid?: boolean;
|
|
121
|
+
purchaseStateAndroid?: PurchaseStateAndroid;
|
|
122
|
+
isAcknowledgedAndroid?: boolean;
|
|
123
|
+
packageNameAndroid?: string;
|
|
124
|
+
developerPayloadAndroid?: string;
|
|
125
|
+
obfuscatedAccountIdAndroid?: string;
|
|
126
|
+
obfuscatedProfileIdAndroid?: string;
|
|
127
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {PurchaseBase, ProductBase} from '../ExpoIap.types';
|
|
2
|
+
|
|
1
3
|
type SubscriptionIosPeriod = 'DAY' | 'WEEK' | 'MONTH' | 'YEAR' | '';
|
|
2
4
|
type PaymentMode = '' | 'FREETRIAL' | 'PAYASYOUGO' | 'PAYUPFRONT';
|
|
3
5
|
|
|
@@ -18,22 +20,18 @@ type SubscriptionInfo = {
|
|
|
18
20
|
subscriptionPeriod: SubscriptionIosPeriod;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
|
-
export type ProductIos = {
|
|
22
|
-
currency: string;
|
|
23
|
-
description: string;
|
|
23
|
+
export type ProductIos = ProductBase & {
|
|
24
24
|
displayName: string;
|
|
25
25
|
displayPrice: string;
|
|
26
|
-
|
|
27
|
-
type: 'autoRenewable' | 'consumable' | 'nonConsumable' | 'nonRenewable';
|
|
26
|
+
type: 'autoRenewable' | 'consumable' | 'nonConsumable' | 'nonRenewable'; // Maps to 'type' in base Product
|
|
28
27
|
isFamilyShareable: boolean;
|
|
29
28
|
jsonRepresentation: string;
|
|
30
|
-
price: number;
|
|
31
29
|
subscription: SubscriptionInfo;
|
|
32
30
|
introductoryPriceNumberOfPeriodsIOS?: string;
|
|
33
31
|
introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;
|
|
34
32
|
};
|
|
35
33
|
|
|
36
|
-
type Discount = {
|
|
34
|
+
export type Discount = {
|
|
37
35
|
identifier: string;
|
|
38
36
|
type: string;
|
|
39
37
|
numberOfPeriods: string;
|
|
@@ -59,22 +57,18 @@ export type PaymentDiscount = {
|
|
|
59
57
|
* A string used to uniquely identify a discount offer for a product.
|
|
60
58
|
*/
|
|
61
59
|
identifier: string;
|
|
62
|
-
|
|
63
60
|
/**
|
|
64
61
|
* A string that identifies the key used to generate the signature.
|
|
65
62
|
*/
|
|
66
63
|
keyIdentifier: string;
|
|
67
|
-
|
|
68
64
|
/**
|
|
69
65
|
* A universally unique ID (UUID) value that you define.
|
|
70
66
|
*/
|
|
71
67
|
nonce: string;
|
|
72
|
-
|
|
73
68
|
/**
|
|
74
69
|
* A UTF-8 string representing the properties of a specific discount offer, cryptographically signed.
|
|
75
70
|
*/
|
|
76
71
|
signature: string;
|
|
77
|
-
|
|
78
72
|
/**
|
|
79
73
|
* The date and time of the signature's creation in milliseconds, formatted in Unix epoch time.
|
|
80
74
|
*/
|
|
@@ -83,7 +77,6 @@ export type PaymentDiscount = {
|
|
|
83
77
|
|
|
84
78
|
export type RequestPurchaseIosProps = {
|
|
85
79
|
sku: string;
|
|
86
|
-
andDangerouslyFinishTransactionAutomaticallyIOS?: boolean;
|
|
87
80
|
/**
|
|
88
81
|
* UUID representing user account
|
|
89
82
|
*/
|
|
@@ -94,34 +87,6 @@ export type RequestPurchaseIosProps = {
|
|
|
94
87
|
|
|
95
88
|
export type RequestSubscriptionIosProps = RequestPurchaseIosProps;
|
|
96
89
|
|
|
97
|
-
export type TransactionSk2 = {
|
|
98
|
-
appAccountToken: string;
|
|
99
|
-
appBundleID: string;
|
|
100
|
-
debugDescription: string;
|
|
101
|
-
deviceVerification: string;
|
|
102
|
-
deviceVerificationNonce: string;
|
|
103
|
-
expirationDate: number;
|
|
104
|
-
environment?: 'Production' | 'Sandbox' | 'Xcode'; // Could be undefined in some cases on iOS 15, but it's stable since iOS 16
|
|
105
|
-
id: number;
|
|
106
|
-
isUpgraded: boolean;
|
|
107
|
-
jsonRepresentation: string;
|
|
108
|
-
offerID: string;
|
|
109
|
-
offerType: string;
|
|
110
|
-
originalID: string;
|
|
111
|
-
originalPurchaseDate: number;
|
|
112
|
-
ownershipType: string;
|
|
113
|
-
productID: string;
|
|
114
|
-
productType: string;
|
|
115
|
-
purchaseDate: number;
|
|
116
|
-
purchasedQuantity: number;
|
|
117
|
-
revocationDate: number;
|
|
118
|
-
revocationReason: string;
|
|
119
|
-
signedDate: number;
|
|
120
|
-
subscriptionGroupID: number;
|
|
121
|
-
webOrderLineItemID: number;
|
|
122
|
-
verificationResult?: string;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
90
|
type SubscriptionStatus =
|
|
126
91
|
| 'expired'
|
|
127
92
|
| 'inBillingRetryPeriod'
|
|
@@ -139,3 +104,34 @@ export type ProductStatusIos = {
|
|
|
139
104
|
state: SubscriptionStatus;
|
|
140
105
|
renewalInfo?: RenewalInfo;
|
|
141
106
|
};
|
|
107
|
+
|
|
108
|
+
export type ProductPurchaseIos = PurchaseBase & {
|
|
109
|
+
// iOS basic fields
|
|
110
|
+
quantityIos?: number;
|
|
111
|
+
originalTransactionDateIos?: number;
|
|
112
|
+
originalTransactionIdentifierIos?: string;
|
|
113
|
+
verificationResultIos?: string;
|
|
114
|
+
appAccountToken?: string;
|
|
115
|
+
// iOS additional fields from StoreKit 2
|
|
116
|
+
expirationDateIos?: number;
|
|
117
|
+
webOrderLineItemIdIos?: number;
|
|
118
|
+
environmentIos?: string;
|
|
119
|
+
storefrontCountryCodeIos?: string;
|
|
120
|
+
appBundleIdIos?: string;
|
|
121
|
+
productTypeIos?: string;
|
|
122
|
+
subscriptionGroupIdIos?: string;
|
|
123
|
+
isUpgradedIos?: boolean;
|
|
124
|
+
ownershipTypeIos?: string;
|
|
125
|
+
reasonIos?: string;
|
|
126
|
+
reasonStringRepresentationIos?: string;
|
|
127
|
+
transactionReasonIos?: 'PURCHASE' | 'RENEWAL' | string;
|
|
128
|
+
revocationDateIos?: number;
|
|
129
|
+
revocationReasonIos?: string;
|
|
130
|
+
offerIos?: {
|
|
131
|
+
id: string;
|
|
132
|
+
type: string;
|
|
133
|
+
paymentMode: string;
|
|
134
|
+
};
|
|
135
|
+
priceIos?: number;
|
|
136
|
+
currencyIos?: string;
|
|
137
|
+
};
|
package/src/useIap.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
getPurchaseHistory,
|
|
10
10
|
getSubscriptions,
|
|
11
11
|
} from './';
|
|
12
|
-
|
|
13
12
|
import {useCallback, useEffect, useState} from 'react';
|
|
14
13
|
import {
|
|
15
14
|
Product,
|
|
@@ -21,13 +20,12 @@ import {
|
|
|
21
20
|
SubscriptionPurchase,
|
|
22
21
|
} from './ExpoIap.types';
|
|
23
22
|
import {TransactionEvent} from './modules/ios';
|
|
24
|
-
import {TransactionSk2} from './types/ExpoIapIos.types';
|
|
25
23
|
import {Subscription} from 'expo-modules-core';
|
|
26
24
|
|
|
27
25
|
type IAP_STATUS = {
|
|
28
26
|
connected: boolean;
|
|
29
27
|
products: Product[];
|
|
30
|
-
promotedProductsIOS:
|
|
28
|
+
promotedProductsIOS: ProductPurchase[];
|
|
31
29
|
subscriptions: SubscriptionProduct[];
|
|
32
30
|
purchaseHistories: ProductPurchase[];
|
|
33
31
|
availablePurchases: ProductPurchase[];
|
|
@@ -56,7 +54,7 @@ export function useIAP(): IAP_STATUS {
|
|
|
56
54
|
const [connected, setConnected] = useState<boolean>(false);
|
|
57
55
|
const [products, setProducts] = useState<Product[]>([]);
|
|
58
56
|
const [promotedProductsIOS, setPromotedProductsIOS] = useState<
|
|
59
|
-
|
|
57
|
+
ProductPurchase[]
|
|
60
58
|
>([]);
|
|
61
59
|
const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);
|
|
62
60
|
const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(
|
|
@@ -66,7 +64,6 @@ export function useIAP(): IAP_STATUS {
|
|
|
66
64
|
ProductPurchase[]
|
|
67
65
|
>([]);
|
|
68
66
|
const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();
|
|
69
|
-
|
|
70
67
|
const [currentPurchaseError, setCurrentPurchaseError] =
|
|
71
68
|
useState<PurchaseError>();
|
|
72
69
|
|
|
@@ -108,17 +105,17 @@ export function useIAP(): IAP_STATUS {
|
|
|
108
105
|
} catch (err) {
|
|
109
106
|
throw err;
|
|
110
107
|
} finally {
|
|
111
|
-
if (purchase.
|
|
108
|
+
if (purchase.id === currentPurchase?.id) {
|
|
112
109
|
setCurrentPurchase(undefined);
|
|
113
110
|
}
|
|
114
111
|
|
|
115
|
-
if (purchase.
|
|
112
|
+
if (purchase.id === currentPurchaseError?.productId) { // Note that PurchaseError still uses productId
|
|
116
113
|
setCurrentPurchaseError(undefined);
|
|
117
114
|
}
|
|
118
115
|
}
|
|
119
116
|
},
|
|
120
117
|
[
|
|
121
|
-
currentPurchase?.
|
|
118
|
+
currentPurchase?.id,
|
|
122
119
|
currentPurchaseError?.productId,
|
|
123
120
|
setCurrentPurchase,
|
|
124
121
|
setCurrentPurchaseError,
|