react-native-iap 10.0.7 → 10.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonModule.kt +1 -2
- package/lib/commonjs/hooks/index.js +19 -0
- package/lib/commonjs/hooks/index.js.map +1 -0
- package/lib/commonjs/hooks/useIAP.js +8 -4
- package/lib/commonjs/hooks/useIAP.js.map +1 -1
- package/lib/commonjs/iap.js +390 -51
- package/lib/commonjs/iap.js.map +1 -1
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/modules/amazon.js +12 -0
- package/lib/commonjs/modules/amazon.js.map +1 -0
- package/lib/commonjs/modules/android.js +12 -0
- package/lib/commonjs/modules/android.js.map +1 -0
- package/lib/commonjs/modules/common.js +2 -0
- package/lib/commonjs/modules/common.js.map +1 -0
- package/lib/commonjs/modules/index.js +58 -0
- package/lib/commonjs/modules/index.js.map +1 -0
- package/lib/commonjs/modules/ios.js +6 -0
- package/lib/commonjs/modules/ios.js.map +1 -0
- package/lib/commonjs/types/amazon.js.map +1 -1
- package/lib/commonjs/types/apple.js +0 -163
- package/lib/commonjs/types/apple.js.map +1 -1
- package/lib/commonjs/types/index.js +11 -1
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/module/hooks/index.js +2 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/useIAP.js +6 -4
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/iap.js +391 -50
- package/lib/module/iap.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/modules/amazon.js +3 -0
- package/lib/module/modules/amazon.js.map +1 -0
- package/lib/module/modules/android.js +3 -0
- package/lib/module/modules/android.js.map +1 -0
- package/lib/module/modules/common.js +2 -0
- package/lib/module/modules/common.js.map +1 -0
- package/lib/module/modules/index.js +5 -0
- package/lib/module/modules/index.js.map +1 -0
- package/lib/module/modules/ios.js +2 -0
- package/lib/module/modules/ios.js.map +1 -0
- package/lib/module/types/amazon.js.map +1 -1
- package/lib/module/types/apple.js +0 -151
- package/lib/module/types/apple.js.map +1 -1
- package/lib/module/types/index.js +9 -0
- package/lib/module/types/index.js.map +1 -1
- package/lib/typescript/hooks/index.d.ts +1 -0
- package/lib/typescript/hooks/useIAP.d.ts +6 -3
- package/lib/typescript/iap.d.ts +356 -65
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/modules/amazon.d.ts +23 -0
- package/lib/typescript/modules/android.d.ts +24 -0
- package/lib/typescript/modules/common.d.ts +13 -0
- package/lib/typescript/modules/index.d.ts +4 -0
- package/lib/typescript/modules/ios.d.ts +32 -0
- package/lib/typescript/types/amazon.d.ts +8 -0
- package/lib/typescript/types/apple.d.ts +0 -399
- package/lib/typescript/types/index.d.ts +51 -1
- package/package.json +7 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useIAP.ts +11 -5
- package/src/iap.ts +420 -91
- package/src/index.ts +1 -0
- package/src/modules/amazon.ts +40 -0
- package/src/modules/android.ts +65 -0
- package/src/modules/common.ts +16 -0
- package/src/modules/index.ts +4 -0
- package/src/modules/ios.ts +57 -0
- package/src/types/amazon.ts +9 -0
- package/src/types/apple.ts +0 -438
- package/src/types/index.ts +71 -4
package/lib/module/iap.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Linking, NativeModules, Platform } from 'react-native';
|
|
2
|
-
import { ReceiptValidationStatus } from './types/apple';
|
|
3
2
|
import { enhancedFetch, fillProductsWithAdditionalData, isAmazon, isAndroid } from './internal';
|
|
3
|
+
import { ProductType } from './types';
|
|
4
4
|
import { InstallSourceAndroid, PurchaseStateAndroid } from './types';
|
|
5
5
|
const {
|
|
6
6
|
RNIapIos,
|
|
7
7
|
RNIapModule,
|
|
8
8
|
RNIapAmazonModule
|
|
9
9
|
} = NativeModules;
|
|
10
|
-
const ANDROID_ITEM_TYPE_SUBSCRIPTION =
|
|
11
|
-
const ANDROID_ITEM_TYPE_IAP =
|
|
10
|
+
const ANDROID_ITEM_TYPE_SUBSCRIPTION = ProductType.subs;
|
|
11
|
+
const ANDROID_ITEM_TYPE_IAP = ProductType.inapp;
|
|
12
12
|
export const getInstallSourceAndroid = () => {
|
|
13
13
|
return RNIapModule ? InstallSourceAndroid.GOOGLE_PLAY : InstallSourceAndroid.AMAZON;
|
|
14
14
|
};
|
|
@@ -43,12 +43,42 @@ export const getNativeModule = () => {
|
|
|
43
43
|
};
|
|
44
44
|
/**
|
|
45
45
|
* Init module for purchase flow. Required on Android. In ios it will check whether user canMakePayment.
|
|
46
|
-
*
|
|
46
|
+
* ## Usage
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import React, {useEffect} from 'react';
|
|
50
|
+
import {View} from 'react-native';
|
|
51
|
+
import {initConnection} from 'react-native-iap';
|
|
52
|
+
|
|
53
|
+
const App = () => {
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
void initConnection();
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
return <View />;
|
|
59
|
+
};
|
|
60
|
+
```
|
|
47
61
|
*/
|
|
48
62
|
|
|
49
63
|
export const initConnection = () => getNativeModule().initConnection();
|
|
50
64
|
/**
|
|
51
|
-
*
|
|
65
|
+
* Disconnects from native SDK
|
|
66
|
+
* Usage
|
|
67
|
+
* ```tsx
|
|
68
|
+
import React, {useEffect} from 'react';
|
|
69
|
+
import {View} from 'react-native';
|
|
70
|
+
import {endConnection} from 'react-native-iap';
|
|
71
|
+
|
|
72
|
+
const App = () => {
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
return () => {
|
|
75
|
+
void endConnection();
|
|
76
|
+
};
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
return <View />;
|
|
80
|
+
};
|
|
81
|
+
```
|
|
52
82
|
* @returns {Promise<void>}
|
|
53
83
|
*/
|
|
54
84
|
|
|
@@ -61,8 +91,47 @@ export const endConnection = () => getNativeModule().endConnection();
|
|
|
61
91
|
export const flushFailedPurchasesCachedAsPendingAndroid = () => getAndroidModule().flushFailedPurchasesCachedAsPending();
|
|
62
92
|
/**
|
|
63
93
|
* Get a list of products (consumable and non-consumable items, but not subscriptions)
|
|
64
|
-
|
|
65
|
-
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import React, {useState} from 'react';
|
|
98
|
+
import {Platform} from 'react-native';
|
|
99
|
+
import {getProducts, Product} from 'react-native-iap';
|
|
100
|
+
|
|
101
|
+
const skus = Platform.select({
|
|
102
|
+
ios: ['com.example.consumableIos'],
|
|
103
|
+
android: ['com.example.consumableAndroid'],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const App = () => {
|
|
107
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
108
|
+
|
|
109
|
+
const handleProducts = async () => {
|
|
110
|
+
const items = await getProducts({skus});
|
|
111
|
+
|
|
112
|
+
setProducts(items);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
void handleProducts();
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<>
|
|
121
|
+
{products.map((product) => (
|
|
122
|
+
<Text key={product.productId}>{product.productId}</Text>
|
|
123
|
+
))}
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Just a few things to keep in mind:
|
|
130
|
+
|
|
131
|
+
- You can get your products in `componentDidMount`, `useEffect` or another appropriate area of your app.
|
|
132
|
+
- Since a user may start your app with a bad or no internet connection, preparing/getting the items more than once may be a good idea.
|
|
133
|
+
- If the user has no IAPs available when the app starts first, you may want to check again when the user enters your IAP store.
|
|
134
|
+
|
|
66
135
|
*/
|
|
67
136
|
|
|
68
137
|
export const getProducts = _ref => {
|
|
@@ -78,12 +147,28 @@ export const getProducts = _ref => {
|
|
|
78
147
|
const products = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_IAP, skus);
|
|
79
148
|
return fillProductsWithAdditionalData(products);
|
|
80
149
|
}
|
|
81
|
-
}) || Promise.
|
|
150
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform'))))();
|
|
82
151
|
};
|
|
83
152
|
/**
|
|
84
153
|
* Get a list of subscriptions
|
|
85
|
-
*
|
|
86
|
-
|
|
154
|
+
* ## Usage
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import React, {useCallback} from 'react';
|
|
158
|
+
import {View} from 'react-native';
|
|
159
|
+
import {getSubscriptions} from 'react-native-iap';
|
|
160
|
+
|
|
161
|
+
const App = () => {
|
|
162
|
+
const subscriptions = useCallback(
|
|
163
|
+
async () =>
|
|
164
|
+
await getSubscriptions(['com.example.product1', 'com.example.product2']),
|
|
165
|
+
[],
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return <View />;
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
87
172
|
*/
|
|
88
173
|
|
|
89
174
|
export const getSubscriptions = _ref2 => {
|
|
@@ -99,11 +184,30 @@ export const getSubscriptions = _ref2 => {
|
|
|
99
184
|
const subscriptions = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, skus);
|
|
100
185
|
return fillProductsWithAdditionalData(subscriptions);
|
|
101
186
|
}
|
|
102
|
-
}) || Promise.
|
|
187
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform'))))();
|
|
103
188
|
};
|
|
104
189
|
/**
|
|
105
190
|
* Gets an inventory of purchases made by the user regardless of consumption status
|
|
106
|
-
*
|
|
191
|
+
* ## Usage
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
import React, {useCallback} from 'react';
|
|
195
|
+
import {View} from 'react-native';
|
|
196
|
+
import {getPurchaseHistory} from 'react-native-iap';
|
|
197
|
+
|
|
198
|
+
const App = () => {
|
|
199
|
+
const history = useCallback(
|
|
200
|
+
async () =>
|
|
201
|
+
await getPurchaseHistory([
|
|
202
|
+
'com.example.product1',
|
|
203
|
+
'com.example.product2',
|
|
204
|
+
]),
|
|
205
|
+
[],
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return <View />;
|
|
209
|
+
};
|
|
210
|
+
```
|
|
107
211
|
*/
|
|
108
212
|
|
|
109
213
|
export const getPurchaseHistory = () => (Platform.select({
|
|
@@ -115,14 +219,88 @@ export const getPurchaseHistory = () => (Platform.select({
|
|
|
115
219
|
return await RNIapAmazonModule.getAvailableItems();
|
|
116
220
|
}
|
|
117
221
|
|
|
118
|
-
const products = await
|
|
119
|
-
const subscriptions = await
|
|
222
|
+
const products = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_IAP);
|
|
223
|
+
const subscriptions = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
|
|
120
224
|
return products.concat(subscriptions);
|
|
121
225
|
}
|
|
122
|
-
}) || Promise.resolve)();
|
|
226
|
+
}) || (() => Promise.resolve([])))();
|
|
123
227
|
/**
|
|
124
228
|
* Get all purchases made by the user (either non-consumable, or haven't been consumed yet)
|
|
125
|
-
*
|
|
229
|
+
* ## Usage
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import React, {useCallback} from 'react';
|
|
233
|
+
import {View} from 'react-native';
|
|
234
|
+
import {getAvailablePurchases} from 'react-native-iap';
|
|
235
|
+
|
|
236
|
+
const App = () => {
|
|
237
|
+
const availablePurchases = useCallback(
|
|
238
|
+
async () => await getAvailablePurchases(),
|
|
239
|
+
[],
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return <View />;
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Restoring purchases
|
|
247
|
+
|
|
248
|
+
You can use `getAvailablePurchases()` to do what's commonly understood as "restoring" purchases.
|
|
249
|
+
|
|
250
|
+
:::note
|
|
251
|
+
For debugging you may want to consume all items, you have then to iterate over the purchases returned by `getAvailablePurchases()`.
|
|
252
|
+
:::
|
|
253
|
+
|
|
254
|
+
:::warning
|
|
255
|
+
Beware that if you consume an item without having recorded the purchase in your database the user may have paid for something without getting it delivered and you will have no way to recover the receipt to validate and restore their purchase.
|
|
256
|
+
:::
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
import React from 'react';
|
|
260
|
+
import {Button} from 'react-native';
|
|
261
|
+
import {getAvailablePurchases,finishTransaction} from 'react-native-iap';
|
|
262
|
+
|
|
263
|
+
const App = () => {
|
|
264
|
+
handleRestore = async () => {
|
|
265
|
+
try {
|
|
266
|
+
const purchases = await getAvailablePurchases();
|
|
267
|
+
const newState = {premium: false, ads: true};
|
|
268
|
+
let titles = [];
|
|
269
|
+
|
|
270
|
+
await Promise.all(purchases.map(async purchase => {
|
|
271
|
+
switch (purchase.productId) {
|
|
272
|
+
case 'com.example.premium':
|
|
273
|
+
newState.premium = true;
|
|
274
|
+
titles.push('Premium Version');
|
|
275
|
+
break;
|
|
276
|
+
|
|
277
|
+
case 'com.example.no_ads':
|
|
278
|
+
newState.ads = false;
|
|
279
|
+
titles.push('No Ads');
|
|
280
|
+
break;
|
|
281
|
+
|
|
282
|
+
case 'com.example.coins100':
|
|
283
|
+
await finishTransaction(purchase.purchaseToken);
|
|
284
|
+
CoinStore.addCoins(100);
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
Alert.alert(
|
|
289
|
+
'Restore Successful',
|
|
290
|
+
`You successfully restored the following purchases: ${titles.join(', ')}`,
|
|
291
|
+
);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.warn(error);
|
|
294
|
+
Alert.alert(error.message);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<Button title="Restore purchases" onPress={handleRestore} />
|
|
300
|
+
)
|
|
301
|
+
};
|
|
302
|
+
```
|
|
303
|
+
*
|
|
126
304
|
*/
|
|
127
305
|
|
|
128
306
|
export const getAvailablePurchases = () => (Platform.select({
|
|
@@ -134,21 +312,77 @@ export const getAvailablePurchases = () => (Platform.select({
|
|
|
134
312
|
return await RNIapAmazonModule.getAvailableItems();
|
|
135
313
|
}
|
|
136
314
|
|
|
137
|
-
const products = await
|
|
138
|
-
const subscriptions = await
|
|
315
|
+
const products = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_IAP);
|
|
316
|
+
const subscriptions = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
|
|
139
317
|
return products.concat(subscriptions);
|
|
140
318
|
}
|
|
141
|
-
}) || Promise.resolve)();
|
|
319
|
+
}) || (() => Promise.resolve([])))();
|
|
142
320
|
/**
|
|
143
321
|
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
|
|
144
|
-
*
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
322
|
+
* Request a purchase for a product (consumables or non-consumables).
|
|
323
|
+
|
|
324
|
+
The response will be received through the `PurchaseUpdatedListener`.
|
|
325
|
+
|
|
326
|
+
:::note
|
|
327
|
+
`andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
|
|
328
|
+
always keeping at false, and verifying the transaction receipts on the server-side.
|
|
329
|
+
:::
|
|
330
|
+
|
|
331
|
+
## Signature
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
requestPurchase(
|
|
335
|
+
The product's sku/ID
|
|
336
|
+
sku,
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
* You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
|
|
340
|
+
* @default false
|
|
341
|
+
|
|
342
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
343
|
+
|
|
344
|
+
/** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
|
|
345
|
+
obfuscatedAccountIdAndroid,
|
|
346
|
+
|
|
347
|
+
Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
|
|
348
|
+
obfuscatedProfileIdAndroid,
|
|
349
|
+
|
|
350
|
+
The purchaser's user ID
|
|
351
|
+
applicationUsername,
|
|
352
|
+
): Promise<ProductPurchase>;
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Usage
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
import React, {useCallback} from 'react';
|
|
359
|
+
import {Button} from 'react-native';
|
|
360
|
+
import {requestPurchase, Product, Sku, getProducts} from 'react-native-iap';
|
|
361
|
+
|
|
362
|
+
const App = () => {
|
|
363
|
+
const products = useCallback(
|
|
364
|
+
async () => getProducts(['com.example.product']),
|
|
365
|
+
[],
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const handlePurchase = async (sku: Sku) => {
|
|
369
|
+
await requestPurchase({sku});
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
return (
|
|
373
|
+
<>
|
|
374
|
+
{products.map((product) => (
|
|
375
|
+
<Button
|
|
376
|
+
key={product.productId}
|
|
377
|
+
title="Buy product"
|
|
378
|
+
onPress={() => handlePurchase(product.productId)}
|
|
379
|
+
/>
|
|
380
|
+
))}
|
|
381
|
+
</>
|
|
382
|
+
);
|
|
383
|
+
};
|
|
384
|
+
```
|
|
385
|
+
|
|
152
386
|
*/
|
|
153
387
|
|
|
154
388
|
export const requestPurchase = _ref3 => {
|
|
@@ -163,6 +397,10 @@ export const requestPurchase = _ref3 => {
|
|
|
163
397
|
} = _ref3;
|
|
164
398
|
return (Platform.select({
|
|
165
399
|
ios: async () => {
|
|
400
|
+
if (!sku) {
|
|
401
|
+
return Promise.reject(new Error('sku is required for iOS purchase'));
|
|
402
|
+
}
|
|
403
|
+
|
|
166
404
|
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
167
405
|
console.warn('You are dangerously allowing react-native-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.');
|
|
168
406
|
}
|
|
@@ -171,24 +409,97 @@ export const requestPurchase = _ref3 => {
|
|
|
171
409
|
},
|
|
172
410
|
android: async () => {
|
|
173
411
|
if (isAmazon) {
|
|
412
|
+
if (!sku) {
|
|
413
|
+
return Promise.reject(new Error('sku is required for Amazon purchase'));
|
|
414
|
+
}
|
|
415
|
+
|
|
174
416
|
return RNIapAmazonModule.buyItemByType(sku);
|
|
175
417
|
} else {
|
|
176
|
-
|
|
418
|
+
if (!(sku !== null && sku !== void 0 && sku.length) && !sku) {
|
|
419
|
+
return Promise.reject(new Error('skus is required for Android purchase'));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], undefined, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, [], isOfferPersonalized ?? false);
|
|
177
423
|
}
|
|
178
424
|
}
|
|
179
425
|
}) || Promise.resolve)();
|
|
180
426
|
};
|
|
181
427
|
/**
|
|
182
428
|
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
|
|
183
|
-
*
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
429
|
+
* Request a purchase for a subscription.
|
|
430
|
+
|
|
431
|
+
The response will be received through the `PurchaseUpdatedListener`.
|
|
432
|
+
|
|
433
|
+
:::note
|
|
434
|
+
`andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
|
|
435
|
+
always keeping at false, and verifying the transaction receipts on the server-side.
|
|
436
|
+
:::
|
|
437
|
+
|
|
438
|
+
## Signature
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
requestSubscription(
|
|
442
|
+
The product's sku/ID
|
|
443
|
+
sku,
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
* You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
|
|
447
|
+
* @default false
|
|
448
|
+
|
|
449
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
450
|
+
|
|
451
|
+
purchaseToken that the user is upgrading or downgrading from (Android).
|
|
452
|
+
purchaseTokenAndroid,
|
|
453
|
+
|
|
454
|
+
UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
|
|
455
|
+
prorationModeAndroid = -1,
|
|
456
|
+
|
|
457
|
+
/** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
|
|
458
|
+
obfuscatedAccountIdAndroid,
|
|
459
|
+
|
|
460
|
+
Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
|
|
461
|
+
obfuscatedProfileIdAndroid,
|
|
462
|
+
|
|
463
|
+
The purchaser's user ID
|
|
464
|
+
applicationUsername,
|
|
465
|
+
): Promise<SubscriptionPurchase>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Usage
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
import React, {useCallback} from 'react';
|
|
472
|
+
import {Button} from 'react-native';
|
|
473
|
+
import {
|
|
474
|
+
requestSubscription,
|
|
475
|
+
Product,
|
|
476
|
+
Sku,
|
|
477
|
+
getSubscriptions,
|
|
478
|
+
} from 'react-native-iap';
|
|
479
|
+
|
|
480
|
+
const App = () => {
|
|
481
|
+
const subscriptions = useCallback(
|
|
482
|
+
async () => getSubscriptions(['com.example.subscription']),
|
|
483
|
+
[],
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
const handlePurchase = async (sku: Sku) => {
|
|
487
|
+
await requestSubscription({sku});
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<>
|
|
492
|
+
{subscriptions.map((subscription) => (
|
|
493
|
+
<Button
|
|
494
|
+
key={subscription.productId}
|
|
495
|
+
title="Buy subscription"
|
|
496
|
+
onPress={() => handlePurchase(subscription.productId)}
|
|
497
|
+
/>
|
|
498
|
+
))}
|
|
499
|
+
</>
|
|
500
|
+
);
|
|
501
|
+
};
|
|
502
|
+
```
|
|
192
503
|
*/
|
|
193
504
|
|
|
194
505
|
export const requestSubscription = _ref4 => {
|
|
@@ -205,6 +516,10 @@ export const requestSubscription = _ref4 => {
|
|
|
205
516
|
} = _ref4;
|
|
206
517
|
return (Platform.select({
|
|
207
518
|
ios: async () => {
|
|
519
|
+
if (!sku) {
|
|
520
|
+
return Promise.reject(new Error('sku is required for iOS subscription'));
|
|
521
|
+
}
|
|
522
|
+
|
|
208
523
|
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
209
524
|
console.warn('You are dangerously allowing react-native-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.');
|
|
210
525
|
}
|
|
@@ -213,6 +528,10 @@ export const requestSubscription = _ref4 => {
|
|
|
213
528
|
},
|
|
214
529
|
android: async () => {
|
|
215
530
|
if (isAmazon) {
|
|
531
|
+
if (!sku) {
|
|
532
|
+
return Promise.reject(new Error('sku is required for Amazon purchase'));
|
|
533
|
+
}
|
|
534
|
+
|
|
216
535
|
return RNIapAmazonModule.buyItemByType(sku);
|
|
217
536
|
} else {
|
|
218
537
|
if (!(subscriptionOffers !== null && subscriptionOffers !== void 0 && subscriptionOffers.length)) {
|
|
@@ -222,7 +541,7 @@ export const requestSubscription = _ref4 => {
|
|
|
222
541
|
return RNIapModule.buyItemByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, subscriptionOffers === null || subscriptionOffers === void 0 ? void 0 : subscriptionOffers.map(so => so.sku), purchaseTokenAndroid, prorationModeAndroid, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, subscriptionOffers === null || subscriptionOffers === void 0 ? void 0 : subscriptionOffers.map(so => so.offerToken), isOfferPersonalized ?? false);
|
|
223
542
|
}
|
|
224
543
|
}
|
|
225
|
-
}) || Promise.resolve)();
|
|
544
|
+
}) || (() => Promise.resolve(null)))();
|
|
226
545
|
};
|
|
227
546
|
/**
|
|
228
547
|
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
|
|
@@ -244,10 +563,22 @@ export const requestPurchaseWithQuantityIOS = _ref5 => {
|
|
|
244
563
|
* Call this after you have persisted the purchased state to your server or local data in your app.
|
|
245
564
|
* `react-native-iap` will continue to deliver the purchase updated events with the successful purchase until you finish the transaction. **Even after the app has relaunched.**
|
|
246
565
|
* Android: it will consume purchase for consumables and acknowledge purchase for non-consumables.
|
|
247
|
-
*
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
566
|
+
*
|
|
567
|
+
```tsx
|
|
568
|
+
import React from 'react';
|
|
569
|
+
import {Button} from 'react-native';
|
|
570
|
+
import {finishTransaction} from 'react-native-iap';
|
|
571
|
+
|
|
572
|
+
const App = () => {
|
|
573
|
+
const handlePurchase = async () => {
|
|
574
|
+
// ... handle the purchase request
|
|
575
|
+
|
|
576
|
+
const result = finishTransaction(purchase);
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
return <Button title="Buy product" onPress={handlePurchase} />;
|
|
580
|
+
};
|
|
581
|
+
```
|
|
251
582
|
*/
|
|
252
583
|
|
|
253
584
|
export const finishTransaction = _ref6 => {
|
|
@@ -258,22 +589,28 @@ export const finishTransaction = _ref6 => {
|
|
|
258
589
|
} = _ref6;
|
|
259
590
|
return (Platform.select({
|
|
260
591
|
ios: async () => {
|
|
261
|
-
|
|
592
|
+
const transactionId = purchase.transactionId;
|
|
593
|
+
|
|
594
|
+
if (!transactionId) {
|
|
595
|
+
return Promise.reject(new Error('transactionId required to finish iOS transaction'));
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return getIosModule().finishTransaction(transactionId);
|
|
262
599
|
},
|
|
263
600
|
android: async () => {
|
|
264
|
-
if (purchase) {
|
|
601
|
+
if (purchase !== null && purchase !== void 0 && purchase.purchaseToken) {
|
|
265
602
|
if (isConsumable) {
|
|
266
603
|
return getAndroidModule().consumeProduct(purchase.purchaseToken, developerPayloadAndroid);
|
|
267
604
|
} else if (purchase.userIdAmazon || !purchase.isAcknowledgedAndroid && purchase.purchaseStateAndroid === PurchaseStateAndroid.PURCHASED) {
|
|
268
605
|
return getAndroidModule().acknowledgePurchase(purchase.purchaseToken, developerPayloadAndroid);
|
|
269
606
|
} else {
|
|
270
|
-
|
|
607
|
+
return Promise.reject(new Error('purchase is not suitable to be purchased'));
|
|
271
608
|
}
|
|
272
|
-
} else {
|
|
273
|
-
throw new Error('purchase is not assigned');
|
|
274
609
|
}
|
|
610
|
+
|
|
611
|
+
return Promise.reject(new Error('purchase is not suitable to be purchased'));
|
|
275
612
|
}
|
|
276
|
-
}) || Promise.
|
|
613
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform'))))();
|
|
277
614
|
};
|
|
278
615
|
/**
|
|
279
616
|
* Clear Transaction (iOS only)
|
|
@@ -331,6 +668,7 @@ export const getPromotedProductIOS = () => getIosModule().promotedProduct();
|
|
|
331
668
|
*/
|
|
332
669
|
|
|
333
670
|
export const buyPromotedProductIOS = () => getIosModule().buyPromotedProduct();
|
|
671
|
+
const TEST_RECEIPT = 21007;
|
|
334
672
|
|
|
335
673
|
const requestAgnosticReceiptValidationIos = async receiptBody => {
|
|
336
674
|
const response = await enhancedFetch('https://buy.itunes.apple.com/verifyReceipt', {
|
|
@@ -339,7 +677,7 @@ const requestAgnosticReceiptValidationIos = async receiptBody => {
|
|
|
339
677
|
}); // Best practice is to check for test receipt and check sandbox instead
|
|
340
678
|
// https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
|
|
341
679
|
|
|
342
|
-
if (response && response.status ===
|
|
680
|
+
if (response && response.status === TEST_RECEIPT) {
|
|
343
681
|
const testResponse = await enhancedFetch('https://sandbox.itunes.apple.com/verifyReceipt', {
|
|
344
682
|
method: 'POST',
|
|
345
683
|
body: receiptBody
|
|
@@ -392,7 +730,10 @@ export const validateReceiptIos = async _ref10 => {
|
|
|
392
730
|
}
|
|
393
731
|
|
|
394
732
|
const url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
|
|
395
|
-
return await enhancedFetch(url
|
|
733
|
+
return await enhancedFetch(url, {
|
|
734
|
+
method: 'POST',
|
|
735
|
+
body: receiptBody
|
|
736
|
+
});
|
|
396
737
|
};
|
|
397
738
|
/**
|
|
398
739
|
* Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including
|