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/commonjs/iap.js
CHANGED
|
@@ -7,8 +7,6 @@ exports.validateReceiptIos = exports.validateReceiptAndroid = exports.validateRe
|
|
|
7
7
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
|
|
10
|
-
var _apple = require("./types/apple");
|
|
11
|
-
|
|
12
10
|
var _internal = require("./internal");
|
|
13
11
|
|
|
14
12
|
var _types = require("./types");
|
|
@@ -18,8 +16,8 @@ const {
|
|
|
18
16
|
RNIapModule,
|
|
19
17
|
RNIapAmazonModule
|
|
20
18
|
} = _reactNative.NativeModules;
|
|
21
|
-
const ANDROID_ITEM_TYPE_SUBSCRIPTION =
|
|
22
|
-
const ANDROID_ITEM_TYPE_IAP =
|
|
19
|
+
const ANDROID_ITEM_TYPE_SUBSCRIPTION = _types.ProductType.subs;
|
|
20
|
+
const ANDROID_ITEM_TYPE_IAP = _types.ProductType.inapp;
|
|
23
21
|
|
|
24
22
|
const getInstallSourceAndroid = () => {
|
|
25
23
|
return RNIapModule ? _types.InstallSourceAndroid.GOOGLE_PLAY : _types.InstallSourceAndroid.AMAZON;
|
|
@@ -65,7 +63,21 @@ const getNativeModule = () => {
|
|
|
65
63
|
};
|
|
66
64
|
/**
|
|
67
65
|
* Init module for purchase flow. Required on Android. In ios it will check whether user canMakePayment.
|
|
68
|
-
*
|
|
66
|
+
* ## Usage
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import React, {useEffect} from 'react';
|
|
70
|
+
import {View} from 'react-native';
|
|
71
|
+
import {initConnection} from 'react-native-iap';
|
|
72
|
+
|
|
73
|
+
const App = () => {
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
void initConnection();
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
return <View />;
|
|
79
|
+
};
|
|
80
|
+
```
|
|
69
81
|
*/
|
|
70
82
|
|
|
71
83
|
|
|
@@ -73,7 +85,23 @@ exports.getNativeModule = getNativeModule;
|
|
|
73
85
|
|
|
74
86
|
const initConnection = () => getNativeModule().initConnection();
|
|
75
87
|
/**
|
|
76
|
-
*
|
|
88
|
+
* Disconnects from native SDK
|
|
89
|
+
* Usage
|
|
90
|
+
* ```tsx
|
|
91
|
+
import React, {useEffect} from 'react';
|
|
92
|
+
import {View} from 'react-native';
|
|
93
|
+
import {endConnection} from 'react-native-iap';
|
|
94
|
+
|
|
95
|
+
const App = () => {
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
return () => {
|
|
98
|
+
void endConnection();
|
|
99
|
+
};
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
return <View />;
|
|
103
|
+
};
|
|
104
|
+
```
|
|
77
105
|
* @returns {Promise<void>}
|
|
78
106
|
*/
|
|
79
107
|
|
|
@@ -92,8 +120,47 @@ exports.endConnection = endConnection;
|
|
|
92
120
|
const flushFailedPurchasesCachedAsPendingAndroid = () => getAndroidModule().flushFailedPurchasesCachedAsPending();
|
|
93
121
|
/**
|
|
94
122
|
* Get a list of products (consumable and non-consumable items, but not subscriptions)
|
|
95
|
-
|
|
96
|
-
|
|
123
|
+
## Usage
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import React, {useState} from 'react';
|
|
127
|
+
import {Platform} from 'react-native';
|
|
128
|
+
import {getProducts, Product} from 'react-native-iap';
|
|
129
|
+
|
|
130
|
+
const skus = Platform.select({
|
|
131
|
+
ios: ['com.example.consumableIos'],
|
|
132
|
+
android: ['com.example.consumableAndroid'],
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const App = () => {
|
|
136
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
137
|
+
|
|
138
|
+
const handleProducts = async () => {
|
|
139
|
+
const items = await getProducts({skus});
|
|
140
|
+
|
|
141
|
+
setProducts(items);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
void handleProducts();
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<>
|
|
150
|
+
{products.map((product) => (
|
|
151
|
+
<Text key={product.productId}>{product.productId}</Text>
|
|
152
|
+
))}
|
|
153
|
+
</>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Just a few things to keep in mind:
|
|
159
|
+
|
|
160
|
+
- You can get your products in `componentDidMount`, `useEffect` or another appropriate area of your app.
|
|
161
|
+
- 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.
|
|
162
|
+
- 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.
|
|
163
|
+
|
|
97
164
|
*/
|
|
98
165
|
|
|
99
166
|
|
|
@@ -112,12 +179,28 @@ const getProducts = _ref => {
|
|
|
112
179
|
const products = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_IAP, skus);
|
|
113
180
|
return (0, _internal.fillProductsWithAdditionalData)(products);
|
|
114
181
|
}
|
|
115
|
-
}) || Promise.
|
|
182
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform'))))();
|
|
116
183
|
};
|
|
117
184
|
/**
|
|
118
185
|
* Get a list of subscriptions
|
|
119
|
-
*
|
|
120
|
-
|
|
186
|
+
* ## Usage
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import React, {useCallback} from 'react';
|
|
190
|
+
import {View} from 'react-native';
|
|
191
|
+
import {getSubscriptions} from 'react-native-iap';
|
|
192
|
+
|
|
193
|
+
const App = () => {
|
|
194
|
+
const subscriptions = useCallback(
|
|
195
|
+
async () =>
|
|
196
|
+
await getSubscriptions(['com.example.product1', 'com.example.product2']),
|
|
197
|
+
[],
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return <View />;
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
121
204
|
*/
|
|
122
205
|
|
|
123
206
|
|
|
@@ -136,11 +219,30 @@ const getSubscriptions = _ref2 => {
|
|
|
136
219
|
const subscriptions = await getAndroidModule().getItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION, skus);
|
|
137
220
|
return (0, _internal.fillProductsWithAdditionalData)(subscriptions);
|
|
138
221
|
}
|
|
139
|
-
}) || Promise.
|
|
222
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform'))))();
|
|
140
223
|
};
|
|
141
224
|
/**
|
|
142
225
|
* Gets an inventory of purchases made by the user regardless of consumption status
|
|
143
|
-
*
|
|
226
|
+
* ## Usage
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
import React, {useCallback} from 'react';
|
|
230
|
+
import {View} from 'react-native';
|
|
231
|
+
import {getPurchaseHistory} from 'react-native-iap';
|
|
232
|
+
|
|
233
|
+
const App = () => {
|
|
234
|
+
const history = useCallback(
|
|
235
|
+
async () =>
|
|
236
|
+
await getPurchaseHistory([
|
|
237
|
+
'com.example.product1',
|
|
238
|
+
'com.example.product2',
|
|
239
|
+
]),
|
|
240
|
+
[],
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
return <View />;
|
|
244
|
+
};
|
|
245
|
+
```
|
|
144
246
|
*/
|
|
145
247
|
|
|
146
248
|
|
|
@@ -155,14 +257,88 @@ const getPurchaseHistory = () => (_reactNative.Platform.select({
|
|
|
155
257
|
return await RNIapAmazonModule.getAvailableItems();
|
|
156
258
|
}
|
|
157
259
|
|
|
158
|
-
const products = await
|
|
159
|
-
const subscriptions = await
|
|
260
|
+
const products = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_IAP);
|
|
261
|
+
const subscriptions = await RNIapModule.getPurchaseHistoryByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
|
|
160
262
|
return products.concat(subscriptions);
|
|
161
263
|
}
|
|
162
|
-
}) || Promise.resolve)();
|
|
264
|
+
}) || (() => Promise.resolve([])))();
|
|
163
265
|
/**
|
|
164
266
|
* Get all purchases made by the user (either non-consumable, or haven't been consumed yet)
|
|
165
|
-
*
|
|
267
|
+
* ## Usage
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import React, {useCallback} from 'react';
|
|
271
|
+
import {View} from 'react-native';
|
|
272
|
+
import {getAvailablePurchases} from 'react-native-iap';
|
|
273
|
+
|
|
274
|
+
const App = () => {
|
|
275
|
+
const availablePurchases = useCallback(
|
|
276
|
+
async () => await getAvailablePurchases(),
|
|
277
|
+
[],
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return <View />;
|
|
281
|
+
};
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Restoring purchases
|
|
285
|
+
|
|
286
|
+
You can use `getAvailablePurchases()` to do what's commonly understood as "restoring" purchases.
|
|
287
|
+
|
|
288
|
+
:::note
|
|
289
|
+
For debugging you may want to consume all items, you have then to iterate over the purchases returned by `getAvailablePurchases()`.
|
|
290
|
+
:::
|
|
291
|
+
|
|
292
|
+
:::warning
|
|
293
|
+
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.
|
|
294
|
+
:::
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import React from 'react';
|
|
298
|
+
import {Button} from 'react-native';
|
|
299
|
+
import {getAvailablePurchases,finishTransaction} from 'react-native-iap';
|
|
300
|
+
|
|
301
|
+
const App = () => {
|
|
302
|
+
handleRestore = async () => {
|
|
303
|
+
try {
|
|
304
|
+
const purchases = await getAvailablePurchases();
|
|
305
|
+
const newState = {premium: false, ads: true};
|
|
306
|
+
let titles = [];
|
|
307
|
+
|
|
308
|
+
await Promise.all(purchases.map(async purchase => {
|
|
309
|
+
switch (purchase.productId) {
|
|
310
|
+
case 'com.example.premium':
|
|
311
|
+
newState.premium = true;
|
|
312
|
+
titles.push('Premium Version');
|
|
313
|
+
break;
|
|
314
|
+
|
|
315
|
+
case 'com.example.no_ads':
|
|
316
|
+
newState.ads = false;
|
|
317
|
+
titles.push('No Ads');
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 'com.example.coins100':
|
|
321
|
+
await finishTransaction(purchase.purchaseToken);
|
|
322
|
+
CoinStore.addCoins(100);
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
Alert.alert(
|
|
327
|
+
'Restore Successful',
|
|
328
|
+
`You successfully restored the following purchases: ${titles.join(', ')}`,
|
|
329
|
+
);
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.warn(error);
|
|
332
|
+
Alert.alert(error.message);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<Button title="Restore purchases" onPress={handleRestore} />
|
|
338
|
+
)
|
|
339
|
+
};
|
|
340
|
+
```
|
|
341
|
+
*
|
|
166
342
|
*/
|
|
167
343
|
|
|
168
344
|
|
|
@@ -177,21 +353,77 @@ const getAvailablePurchases = () => (_reactNative.Platform.select({
|
|
|
177
353
|
return await RNIapAmazonModule.getAvailableItems();
|
|
178
354
|
}
|
|
179
355
|
|
|
180
|
-
const products = await
|
|
181
|
-
const subscriptions = await
|
|
356
|
+
const products = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_IAP);
|
|
357
|
+
const subscriptions = await RNIapModule.getAvailableItemsByType(ANDROID_ITEM_TYPE_SUBSCRIPTION);
|
|
182
358
|
return products.concat(subscriptions);
|
|
183
359
|
}
|
|
184
|
-
}) || Promise.resolve)();
|
|
360
|
+
}) || (() => Promise.resolve([])))();
|
|
185
361
|
/**
|
|
186
362
|
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
363
|
+
* Request a purchase for a product (consumables or non-consumables).
|
|
364
|
+
|
|
365
|
+
The response will be received through the `PurchaseUpdatedListener`.
|
|
366
|
+
|
|
367
|
+
:::note
|
|
368
|
+
`andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
|
|
369
|
+
always keeping at false, and verifying the transaction receipts on the server-side.
|
|
370
|
+
:::
|
|
371
|
+
|
|
372
|
+
## Signature
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
requestPurchase(
|
|
376
|
+
The product's sku/ID
|
|
377
|
+
sku,
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
* You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
|
|
381
|
+
* @default false
|
|
382
|
+
|
|
383
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
384
|
+
|
|
385
|
+
/** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
|
|
386
|
+
obfuscatedAccountIdAndroid,
|
|
387
|
+
|
|
388
|
+
Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
|
|
389
|
+
obfuscatedProfileIdAndroid,
|
|
390
|
+
|
|
391
|
+
The purchaser's user ID
|
|
392
|
+
applicationUsername,
|
|
393
|
+
): Promise<ProductPurchase>;
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Usage
|
|
397
|
+
|
|
398
|
+
```tsx
|
|
399
|
+
import React, {useCallback} from 'react';
|
|
400
|
+
import {Button} from 'react-native';
|
|
401
|
+
import {requestPurchase, Product, Sku, getProducts} from 'react-native-iap';
|
|
402
|
+
|
|
403
|
+
const App = () => {
|
|
404
|
+
const products = useCallback(
|
|
405
|
+
async () => getProducts(['com.example.product']),
|
|
406
|
+
[],
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const handlePurchase = async (sku: Sku) => {
|
|
410
|
+
await requestPurchase({sku});
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
return (
|
|
414
|
+
<>
|
|
415
|
+
{products.map((product) => (
|
|
416
|
+
<Button
|
|
417
|
+
key={product.productId}
|
|
418
|
+
title="Buy product"
|
|
419
|
+
onPress={() => handlePurchase(product.productId)}
|
|
420
|
+
/>
|
|
421
|
+
))}
|
|
422
|
+
</>
|
|
423
|
+
);
|
|
424
|
+
};
|
|
425
|
+
```
|
|
426
|
+
|
|
195
427
|
*/
|
|
196
428
|
|
|
197
429
|
|
|
@@ -209,6 +441,10 @@ const requestPurchase = _ref3 => {
|
|
|
209
441
|
} = _ref3;
|
|
210
442
|
return (_reactNative.Platform.select({
|
|
211
443
|
ios: async () => {
|
|
444
|
+
if (!sku) {
|
|
445
|
+
return Promise.reject(new Error('sku is required for iOS purchase'));
|
|
446
|
+
}
|
|
447
|
+
|
|
212
448
|
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
213
449
|
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.');
|
|
214
450
|
}
|
|
@@ -217,24 +453,97 @@ const requestPurchase = _ref3 => {
|
|
|
217
453
|
},
|
|
218
454
|
android: async () => {
|
|
219
455
|
if (_internal.isAmazon) {
|
|
456
|
+
if (!sku) {
|
|
457
|
+
return Promise.reject(new Error('sku is required for Amazon purchase'));
|
|
458
|
+
}
|
|
459
|
+
|
|
220
460
|
return RNIapAmazonModule.buyItemByType(sku);
|
|
221
461
|
} else {
|
|
222
|
-
|
|
462
|
+
if (!(sku !== null && sku !== void 0 && sku.length) && !sku) {
|
|
463
|
+
return Promise.reject(new Error('skus is required for Android purchase'));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return getAndroidModule().buyItemByType(ANDROID_ITEM_TYPE_IAP, skus !== null && skus !== void 0 && skus.length ? skus : [sku], undefined, -1, obfuscatedAccountIdAndroid, obfuscatedProfileIdAndroid, [], isOfferPersonalized ?? false);
|
|
223
467
|
}
|
|
224
468
|
}
|
|
225
469
|
}) || Promise.resolve)();
|
|
226
470
|
};
|
|
227
471
|
/**
|
|
228
472
|
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
|
|
229
|
-
*
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
473
|
+
* Request a purchase for a subscription.
|
|
474
|
+
|
|
475
|
+
The response will be received through the `PurchaseUpdatedListener`.
|
|
476
|
+
|
|
477
|
+
:::note
|
|
478
|
+
`andDangerouslyFinishTransactionAutomatically` defaults to false. We recommend
|
|
479
|
+
always keeping at false, and verifying the transaction receipts on the server-side.
|
|
480
|
+
:::
|
|
481
|
+
|
|
482
|
+
## Signature
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
requestSubscription(
|
|
486
|
+
The product's sku/ID
|
|
487
|
+
sku,
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
* You should set this to false and call finishTransaction manually when you have delivered the purchased goods to the user.
|
|
491
|
+
* @default false
|
|
492
|
+
|
|
493
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
494
|
+
|
|
495
|
+
purchaseToken that the user is upgrading or downgrading from (Android).
|
|
496
|
+
purchaseTokenAndroid,
|
|
497
|
+
|
|
498
|
+
UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, IMMEDIATE_WITH_TIME_PRORATION, IMMEDIATE_AND_CHARGE_PRORATED_PRICE, IMMEDIATE_WITHOUT_PRORATION, DEFERRED
|
|
499
|
+
prorationModeAndroid = -1,
|
|
500
|
+
|
|
501
|
+
/** Specifies an optional obfuscated string that is uniquely associated with the user's account in your app.
|
|
502
|
+
obfuscatedAccountIdAndroid,
|
|
503
|
+
|
|
504
|
+
Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app.
|
|
505
|
+
obfuscatedProfileIdAndroid,
|
|
506
|
+
|
|
507
|
+
The purchaser's user ID
|
|
508
|
+
applicationUsername,
|
|
509
|
+
): Promise<SubscriptionPurchase>
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## Usage
|
|
513
|
+
|
|
514
|
+
```tsx
|
|
515
|
+
import React, {useCallback} from 'react';
|
|
516
|
+
import {Button} from 'react-native';
|
|
517
|
+
import {
|
|
518
|
+
requestSubscription,
|
|
519
|
+
Product,
|
|
520
|
+
Sku,
|
|
521
|
+
getSubscriptions,
|
|
522
|
+
} from 'react-native-iap';
|
|
523
|
+
|
|
524
|
+
const App = () => {
|
|
525
|
+
const subscriptions = useCallback(
|
|
526
|
+
async () => getSubscriptions(['com.example.subscription']),
|
|
527
|
+
[],
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
const handlePurchase = async (sku: Sku) => {
|
|
531
|
+
await requestSubscription({sku});
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
return (
|
|
535
|
+
<>
|
|
536
|
+
{subscriptions.map((subscription) => (
|
|
537
|
+
<Button
|
|
538
|
+
key={subscription.productId}
|
|
539
|
+
title="Buy subscription"
|
|
540
|
+
onPress={() => handlePurchase(subscription.productId)}
|
|
541
|
+
/>
|
|
542
|
+
))}
|
|
543
|
+
</>
|
|
544
|
+
);
|
|
545
|
+
};
|
|
546
|
+
```
|
|
238
547
|
*/
|
|
239
548
|
|
|
240
549
|
|
|
@@ -254,6 +563,10 @@ const requestSubscription = _ref4 => {
|
|
|
254
563
|
} = _ref4;
|
|
255
564
|
return (_reactNative.Platform.select({
|
|
256
565
|
ios: async () => {
|
|
566
|
+
if (!sku) {
|
|
567
|
+
return Promise.reject(new Error('sku is required for iOS subscription'));
|
|
568
|
+
}
|
|
569
|
+
|
|
257
570
|
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
258
571
|
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.');
|
|
259
572
|
}
|
|
@@ -262,6 +575,10 @@ const requestSubscription = _ref4 => {
|
|
|
262
575
|
},
|
|
263
576
|
android: async () => {
|
|
264
577
|
if (_internal.isAmazon) {
|
|
578
|
+
if (!sku) {
|
|
579
|
+
return Promise.reject(new Error('sku is required for Amazon purchase'));
|
|
580
|
+
}
|
|
581
|
+
|
|
265
582
|
return RNIapAmazonModule.buyItemByType(sku);
|
|
266
583
|
} else {
|
|
267
584
|
if (!(subscriptionOffers !== null && subscriptionOffers !== void 0 && subscriptionOffers.length)) {
|
|
@@ -271,7 +588,7 @@ const requestSubscription = _ref4 => {
|
|
|
271
588
|
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);
|
|
272
589
|
}
|
|
273
590
|
}
|
|
274
|
-
}) || Promise.resolve)();
|
|
591
|
+
}) || (() => Promise.resolve(null)))();
|
|
275
592
|
};
|
|
276
593
|
/**
|
|
277
594
|
* Request a purchase for product. This will be received in `PurchaseUpdatedListener`.
|
|
@@ -296,10 +613,22 @@ const requestPurchaseWithQuantityIOS = _ref5 => {
|
|
|
296
613
|
* Call this after you have persisted the purchased state to your server or local data in your app.
|
|
297
614
|
* `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.**
|
|
298
615
|
* Android: it will consume purchase for consumables and acknowledge purchase for non-consumables.
|
|
299
|
-
*
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
616
|
+
*
|
|
617
|
+
```tsx
|
|
618
|
+
import React from 'react';
|
|
619
|
+
import {Button} from 'react-native';
|
|
620
|
+
import {finishTransaction} from 'react-native-iap';
|
|
621
|
+
|
|
622
|
+
const App = () => {
|
|
623
|
+
const handlePurchase = async () => {
|
|
624
|
+
// ... handle the purchase request
|
|
625
|
+
|
|
626
|
+
const result = finishTransaction(purchase);
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
return <Button title="Buy product" onPress={handlePurchase} />;
|
|
630
|
+
};
|
|
631
|
+
```
|
|
303
632
|
*/
|
|
304
633
|
|
|
305
634
|
|
|
@@ -313,22 +642,28 @@ const finishTransaction = _ref6 => {
|
|
|
313
642
|
} = _ref6;
|
|
314
643
|
return (_reactNative.Platform.select({
|
|
315
644
|
ios: async () => {
|
|
316
|
-
|
|
645
|
+
const transactionId = purchase.transactionId;
|
|
646
|
+
|
|
647
|
+
if (!transactionId) {
|
|
648
|
+
return Promise.reject(new Error('transactionId required to finish iOS transaction'));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return getIosModule().finishTransaction(transactionId);
|
|
317
652
|
},
|
|
318
653
|
android: async () => {
|
|
319
|
-
if (purchase) {
|
|
654
|
+
if (purchase !== null && purchase !== void 0 && purchase.purchaseToken) {
|
|
320
655
|
if (isConsumable) {
|
|
321
656
|
return getAndroidModule().consumeProduct(purchase.purchaseToken, developerPayloadAndroid);
|
|
322
657
|
} else if (purchase.userIdAmazon || !purchase.isAcknowledgedAndroid && purchase.purchaseStateAndroid === _types.PurchaseStateAndroid.PURCHASED) {
|
|
323
658
|
return getAndroidModule().acknowledgePurchase(purchase.purchaseToken, developerPayloadAndroid);
|
|
324
659
|
} else {
|
|
325
|
-
|
|
660
|
+
return Promise.reject(new Error('purchase is not suitable to be purchased'));
|
|
326
661
|
}
|
|
327
|
-
} else {
|
|
328
|
-
throw new Error('purchase is not assigned');
|
|
329
662
|
}
|
|
663
|
+
|
|
664
|
+
return Promise.reject(new Error('purchase is not suitable to be purchased'));
|
|
330
665
|
}
|
|
331
|
-
}) || Promise.
|
|
666
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform'))))();
|
|
332
667
|
};
|
|
333
668
|
/**
|
|
334
669
|
* Clear Transaction (iOS only)
|
|
@@ -406,6 +741,7 @@ exports.getPromotedProductIOS = getPromotedProductIOS;
|
|
|
406
741
|
const buyPromotedProductIOS = () => getIosModule().buyPromotedProduct();
|
|
407
742
|
|
|
408
743
|
exports.buyPromotedProductIOS = buyPromotedProductIOS;
|
|
744
|
+
const TEST_RECEIPT = 21007;
|
|
409
745
|
|
|
410
746
|
const requestAgnosticReceiptValidationIos = async receiptBody => {
|
|
411
747
|
const response = await (0, _internal.enhancedFetch)('https://buy.itunes.apple.com/verifyReceipt', {
|
|
@@ -414,7 +750,7 @@ const requestAgnosticReceiptValidationIos = async receiptBody => {
|
|
|
414
750
|
}); // Best practice is to check for test receipt and check sandbox instead
|
|
415
751
|
// https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
|
|
416
752
|
|
|
417
|
-
if (response && response.status ===
|
|
753
|
+
if (response && response.status === TEST_RECEIPT) {
|
|
418
754
|
const testResponse = await (0, _internal.enhancedFetch)('https://sandbox.itunes.apple.com/verifyReceipt', {
|
|
419
755
|
method: 'POST',
|
|
420
756
|
body: receiptBody
|
|
@@ -470,7 +806,10 @@ const validateReceiptIos = async _ref10 => {
|
|
|
470
806
|
}
|
|
471
807
|
|
|
472
808
|
const url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
|
|
473
|
-
return await (0, _internal.enhancedFetch)(url
|
|
809
|
+
return await (0, _internal.enhancedFetch)(url, {
|
|
810
|
+
method: 'POST',
|
|
811
|
+
body: receiptBody
|
|
812
|
+
});
|
|
474
813
|
};
|
|
475
814
|
/**
|
|
476
815
|
* Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including
|