react-native-iap 11.0.0-beta.1 → 11.0.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 +0 -1
- package/ios/IapSerializationUtils.swift +30 -0
- package/ios/RNIapIosSk2.m +5 -0
- package/ios/RNIapIosSk2.swift +63 -49
- package/lib/commonjs/eventEmitter.js +4 -4
- package/lib/commonjs/eventEmitter.js.map +1 -1
- package/lib/commonjs/hooks/withIAPContext.js +1 -1
- package/lib/commonjs/hooks/withIAPContext.js.map +1 -1
- package/lib/commonjs/iap.js +50 -297
- package/lib/commonjs/iap.js.map +1 -1
- package/lib/commonjs/internal/platform.js +75 -2
- package/lib/commonjs/internal/platform.js.map +1 -1
- package/lib/commonjs/modules/amazon.js +28 -1
- package/lib/commonjs/modules/amazon.js.map +1 -1
- package/lib/commonjs/modules/android.js +73 -1
- package/lib/commonjs/modules/android.js.map +1 -1
- package/lib/commonjs/modules/ios.js +126 -0
- package/lib/commonjs/modules/ios.js.map +1 -1
- package/lib/commonjs/modules/iosSk2.js +47 -0
- package/lib/commonjs/modules/iosSk2.js.map +1 -1
- package/lib/commonjs/types/appleSk2.js.map +1 -1
- package/lib/module/eventEmitter.js +2 -2
- package/lib/module/eventEmitter.js.map +1 -1
- package/lib/module/hooks/withIAPContext.js +2 -2
- package/lib/module/hooks/withIAPContext.js.map +1 -1
- package/lib/module/iap.js +8 -223
- package/lib/module/iap.js.map +1 -1
- package/lib/module/internal/platform.js +48 -1
- package/lib/module/internal/platform.js.map +1 -1
- package/lib/module/modules/amazon.js +23 -0
- package/lib/module/modules/amazon.js.map +1 -1
- package/lib/module/modules/android.js +59 -1
- package/lib/module/modules/android.js.map +1 -1
- package/lib/module/modules/ios.js +102 -1
- package/lib/module/modules/ios.js.map +1 -1
- package/lib/module/modules/iosSk2.js +30 -1
- package/lib/module/modules/iosSk2.js.map +1 -1
- package/lib/module/types/appleSk2.js.map +1 -1
- package/lib/typescript/iap.d.ts +7 -120
- package/lib/typescript/internal/platform.d.ts +9 -0
- package/lib/typescript/modules/amazon.d.ts +17 -0
- package/lib/typescript/modules/android.d.ts +38 -1
- package/lib/typescript/modules/ios.d.ts +56 -0
- package/lib/typescript/modules/iosSk2.d.ts +27 -1
- package/lib/typescript/types/appleSk2.d.ts +4 -0
- package/package.json +1 -1
- package/src/eventEmitter.ts +4 -3
- package/src/hooks/withIAPContext.tsx +2 -2
- package/src/iap.ts +20 -297
- package/src/internal/platform.ts +78 -1
- package/src/modules/amazon.ts +29 -1
- package/src/modules/android.ts +83 -2
- package/src/modules/ios.ts +121 -0
- package/src/modules/iosSk2.ts +45 -1
- package/src/types/appleSk2.ts +12 -0
package/src/iap.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {ResponseBody as ReceiptValidationResponse} from '@jeremybarbet/apple-api-types';
|
|
1
|
+
import {NativeModules, Platform} from 'react-native';
|
|
3
2
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import * as IapAmazon from './modules/amazon';
|
|
4
|
+
import * as IapAndroid from './modules/android';
|
|
5
|
+
import * as IapIos from './modules/ios';
|
|
6
|
+
import * as IapIosSk2 from './modules/iosSk2';
|
|
6
7
|
import {
|
|
7
8
|
offerSk2Map,
|
|
8
9
|
ProductSk2,
|
|
@@ -10,10 +11,13 @@ import {
|
|
|
10
11
|
subscriptionSk2Map,
|
|
11
12
|
} from './types/appleSk2';
|
|
12
13
|
import {
|
|
13
|
-
|
|
14
|
+
enableStorekit2,
|
|
14
15
|
fillProductsWithAdditionalData,
|
|
16
|
+
getAndroidModule,
|
|
17
|
+
getIosModule,
|
|
18
|
+
getNativeModule,
|
|
15
19
|
isAmazon,
|
|
16
|
-
|
|
20
|
+
isIosStorekit2,
|
|
17
21
|
} from './internal';
|
|
18
22
|
import {
|
|
19
23
|
Product,
|
|
@@ -22,94 +26,24 @@ import {
|
|
|
22
26
|
PurchaseResult,
|
|
23
27
|
RequestPurchase,
|
|
24
28
|
RequestSubscription,
|
|
25
|
-
Sku,
|
|
26
29
|
Subscription,
|
|
27
30
|
SubscriptionPurchase,
|
|
28
31
|
} from './types';
|
|
29
|
-
import {
|
|
32
|
+
import {PurchaseStateAndroid} from './types';
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
IapAndroid,
|
|
36
|
+
IapAmazon,
|
|
37
|
+
IapIos,
|
|
38
|
+
IapIosSk2,
|
|
39
|
+
isIosStorekit2,
|
|
40
|
+
enableStorekit2,
|
|
41
|
+
};
|
|
30
42
|
|
|
31
43
|
const {RNIapIos, RNIapIosSk2, RNIapModule, RNIapAmazonModule} = NativeModules;
|
|
32
44
|
const ANDROID_ITEM_TYPE_SUBSCRIPTION = ProductType.subs;
|
|
33
45
|
const ANDROID_ITEM_TYPE_IAP = ProductType.inapp;
|
|
34
46
|
|
|
35
|
-
export const getInstallSourceAndroid = (): InstallSourceAndroid => {
|
|
36
|
-
return RNIapModule
|
|
37
|
-
? InstallSourceAndroid.GOOGLE_PLAY
|
|
38
|
-
: InstallSourceAndroid.AMAZON;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
let androidNativeModule = RNIapModule;
|
|
42
|
-
|
|
43
|
-
let iosNativeModule: typeof RNIapIos | typeof RNIapIosSk2 = RNIapIos;
|
|
44
|
-
|
|
45
|
-
export const isIosStorekit2 = () => iosNativeModule === RNIapIosSk2;
|
|
46
|
-
|
|
47
|
-
export const isStorekit2Avaiable = (): boolean => !!RNIapIosSk2;
|
|
48
|
-
|
|
49
|
-
export const enableStorekit2 = () => {
|
|
50
|
-
if (RNIapIosSk2) {
|
|
51
|
-
iosNativeModule = RNIapIosSk2;
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
console.warn('Storekit 2 is not available on this device');
|
|
55
|
-
|
|
56
|
-
return false;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const setAndroidNativeModule = (
|
|
60
|
-
nativeModule: typeof RNIapModule,
|
|
61
|
-
): void => {
|
|
62
|
-
androidNativeModule = nativeModule;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const setIosNativeModule = (
|
|
66
|
-
nativeModule: typeof RNIapIos | typeof RNIapIosSk2,
|
|
67
|
-
): void => {
|
|
68
|
-
iosNativeModule = nativeModule;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const checkNativeAndroidAvailable = (): void => {
|
|
72
|
-
if (!RNIapModule && !RNIapAmazonModule) {
|
|
73
|
-
throw new Error('IAP_NOT_AVAILABLE');
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const getAndroidModule = ():
|
|
78
|
-
| typeof RNIapModule
|
|
79
|
-
| typeof RNIapAmazonModule => {
|
|
80
|
-
checkNativeAndroidAvailable();
|
|
81
|
-
|
|
82
|
-
return androidNativeModule
|
|
83
|
-
? androidNativeModule
|
|
84
|
-
: RNIapModule
|
|
85
|
-
? RNIapModule
|
|
86
|
-
: RNIapAmazonModule;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const checkNativeIOSAvailable = (): void => {
|
|
90
|
-
if (!RNIapIos && !RNIapIosSk2) {
|
|
91
|
-
throw new Error('IAP_NOT_AVAILABLE');
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export const getIosModule = (): typeof RNIapIos | typeof RNIapIosSk2 => {
|
|
96
|
-
checkNativeIOSAvailable();
|
|
97
|
-
|
|
98
|
-
return iosNativeModule
|
|
99
|
-
? iosNativeModule
|
|
100
|
-
: RNIapIosSk2
|
|
101
|
-
? RNIapIosSk2
|
|
102
|
-
: RNIapIos;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export const getNativeModule = ():
|
|
106
|
-
| typeof RNIapModule
|
|
107
|
-
| typeof RNIapAmazonModule
|
|
108
|
-
| typeof RNIapIos
|
|
109
|
-
| typeof RNIapIosSk2 => {
|
|
110
|
-
return isAndroid ? getAndroidModule() : getIosModule();
|
|
111
|
-
};
|
|
112
|
-
|
|
113
47
|
/**
|
|
114
48
|
* Init module for purchase flow. Required on Android. In ios it will check whether user canMakePayment.
|
|
115
49
|
* ## Usage
|
|
@@ -811,214 +745,3 @@ export const finishTransaction = ({
|
|
|
811
745
|
}) || (() => Promise.reject(new Error('Unsupported Platform')))
|
|
812
746
|
)();
|
|
813
747
|
};
|
|
814
|
-
|
|
815
|
-
/**
|
|
816
|
-
* Clear Transaction (iOS only)
|
|
817
|
-
* Finish remaining transactions. Related to issue #257 and #801
|
|
818
|
-
* link : https://github.com/dooboolab/react-native-iap/issues/257
|
|
819
|
-
* https://github.com/dooboolab/react-native-iap/issues/801
|
|
820
|
-
* @returns {Promise<void>}
|
|
821
|
-
*/
|
|
822
|
-
export const clearTransactionIOS = (): Promise<void> =>
|
|
823
|
-
getIosModule().clearTransaction();
|
|
824
|
-
|
|
825
|
-
/**
|
|
826
|
-
* Clear valid Products (iOS only)
|
|
827
|
-
* Remove all products which are validated by Apple server.
|
|
828
|
-
* @returns {void}
|
|
829
|
-
*/
|
|
830
|
-
export const clearProductsIOS = (): Promise<void> =>
|
|
831
|
-
getIosModule().clearProducts();
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Acknowledge a product (on Android.) No-op on iOS.
|
|
835
|
-
* @param {string} token The product's token (on Android)
|
|
836
|
-
* @returns {Promise<PurchaseResult | void>}
|
|
837
|
-
*/
|
|
838
|
-
export const acknowledgePurchaseAndroid = ({
|
|
839
|
-
token,
|
|
840
|
-
developerPayload,
|
|
841
|
-
}: {
|
|
842
|
-
token: string;
|
|
843
|
-
developerPayload?: string;
|
|
844
|
-
}): Promise<PurchaseResult | boolean | void> => {
|
|
845
|
-
return getAndroidModule().acknowledgePurchase(token, developerPayload);
|
|
846
|
-
};
|
|
847
|
-
|
|
848
|
-
/**
|
|
849
|
-
* Deep link to subscriptions screen on Android. No-op on iOS.
|
|
850
|
-
* @param {string} sku The product's SKU (on Android)
|
|
851
|
-
* @returns {Promise<void>}
|
|
852
|
-
*/
|
|
853
|
-
export const deepLinkToSubscriptionsAndroid = async ({
|
|
854
|
-
sku,
|
|
855
|
-
}: {
|
|
856
|
-
sku: Sku;
|
|
857
|
-
}): Promise<void> => {
|
|
858
|
-
checkNativeAndroidAvailable();
|
|
859
|
-
|
|
860
|
-
return Linking.openURL(
|
|
861
|
-
`https://play.google.com/store/account/subscriptions?package=${await RNIapModule.getPackageName()}&sku=${sku}`,
|
|
862
|
-
);
|
|
863
|
-
};
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* Should Add Store Payment (iOS only)
|
|
867
|
-
* Indicates the the App Store purchase should continue from the app instead of the App Store.
|
|
868
|
-
* @returns {Promise<Product | null>} promoted product
|
|
869
|
-
*/
|
|
870
|
-
export const getPromotedProductIOS = (): Promise<Product | null> => {
|
|
871
|
-
if (!isIosStorekit2) {
|
|
872
|
-
return getIosModule().promotedProduct();
|
|
873
|
-
} else {
|
|
874
|
-
return Promise.reject('Only available on SK1');
|
|
875
|
-
}
|
|
876
|
-
};
|
|
877
|
-
|
|
878
|
-
/**
|
|
879
|
-
* Buy the currently selected promoted product (iOS only)
|
|
880
|
-
* Initiates the payment process for a promoted product. Should only be called in response to the `iap-promoted-product` event.
|
|
881
|
-
* @returns {Promise<void>}
|
|
882
|
-
*/
|
|
883
|
-
export const buyPromotedProductIOS = (): Promise<void> =>
|
|
884
|
-
getIosModule().buyPromotedProduct();
|
|
885
|
-
|
|
886
|
-
const TEST_RECEIPT = 21007;
|
|
887
|
-
const requestAgnosticReceiptValidationIos = async (
|
|
888
|
-
receiptBody: Record<string, unknown>,
|
|
889
|
-
): Promise<ReceiptValidationResponse | false> => {
|
|
890
|
-
const response = await enhancedFetch<ReceiptValidationResponse>(
|
|
891
|
-
'https://buy.itunes.apple.com/verifyReceipt',
|
|
892
|
-
{
|
|
893
|
-
method: 'POST',
|
|
894
|
-
body: receiptBody,
|
|
895
|
-
},
|
|
896
|
-
);
|
|
897
|
-
|
|
898
|
-
// Best practice is to check for test receipt and check sandbox instead
|
|
899
|
-
// https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
|
|
900
|
-
if (response && response.status === TEST_RECEIPT) {
|
|
901
|
-
const testResponse = await enhancedFetch<ReceiptValidationResponse>(
|
|
902
|
-
'https://sandbox.itunes.apple.com/verifyReceipt',
|
|
903
|
-
{
|
|
904
|
-
method: 'POST',
|
|
905
|
-
body: receiptBody,
|
|
906
|
-
},
|
|
907
|
-
);
|
|
908
|
-
|
|
909
|
-
return testResponse;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
return response;
|
|
913
|
-
};
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
* Validate receipt for iOS.
|
|
917
|
-
* @param {object} receiptBody the receipt body to send to apple server.
|
|
918
|
-
* @param {boolean} isTest whether this is in test environment which is sandbox.
|
|
919
|
-
* @returns {Promise<Apple.ReceiptValidationResponse | false>}
|
|
920
|
-
*/
|
|
921
|
-
export const validateReceiptIos = async ({
|
|
922
|
-
receiptBody,
|
|
923
|
-
isTest,
|
|
924
|
-
}: {
|
|
925
|
-
receiptBody: Record<string, unknown>;
|
|
926
|
-
isTest?: boolean;
|
|
927
|
-
}): Promise<ReceiptValidationResponse | false> => {
|
|
928
|
-
if (isTest == null) {
|
|
929
|
-
return await requestAgnosticReceiptValidationIos(receiptBody);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
const url = isTest
|
|
933
|
-
? 'https://sandbox.itunes.apple.com/verifyReceipt'
|
|
934
|
-
: 'https://buy.itunes.apple.com/verifyReceipt';
|
|
935
|
-
|
|
936
|
-
return await enhancedFetch<ReceiptValidationResponse>(url);
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
/**
|
|
940
|
-
* Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including
|
|
941
|
-
* your access token in the binary you ship to users is potentially dangerous.
|
|
942
|
-
* Use server side validation instead for your production builds
|
|
943
|
-
* @param {string} packageName package name of your app.
|
|
944
|
-
* @param {string} productId product id for your in app product.
|
|
945
|
-
* @param {string} productToken token for your purchase.
|
|
946
|
-
* @param {string} accessToken accessToken from googleApis.
|
|
947
|
-
* @param {boolean} isSub whether this is subscription or inapp. `true` for subscription.
|
|
948
|
-
* @returns {Promise<object>}
|
|
949
|
-
*/
|
|
950
|
-
export const validateReceiptAndroid = async ({
|
|
951
|
-
packageName,
|
|
952
|
-
productId,
|
|
953
|
-
productToken,
|
|
954
|
-
accessToken,
|
|
955
|
-
isSub,
|
|
956
|
-
}: {
|
|
957
|
-
packageName: string;
|
|
958
|
-
productId: string;
|
|
959
|
-
productToken: string;
|
|
960
|
-
accessToken: string;
|
|
961
|
-
isSub?: boolean;
|
|
962
|
-
}): Promise<Android.ReceiptType> => {
|
|
963
|
-
const type = isSub ? 'subscriptions' : 'products';
|
|
964
|
-
|
|
965
|
-
const url =
|
|
966
|
-
'https://androidpublisher.googleapis.com/androidpublisher/v3/applications' +
|
|
967
|
-
`/${packageName}/purchases/${type}/${productId}` +
|
|
968
|
-
`/tokens/${productToken}?access_token=${accessToken}`;
|
|
969
|
-
|
|
970
|
-
return await enhancedFetch<Android.ReceiptType>(url);
|
|
971
|
-
};
|
|
972
|
-
|
|
973
|
-
/**
|
|
974
|
-
* Validate receipt for Amazon. NOTE: This method is here for debugging purposes only. Including
|
|
975
|
-
* your developer secret in the binary you ship to users is potentially dangerous.
|
|
976
|
-
* Use server side validation instead for your production builds
|
|
977
|
-
* @param {string} developerSecret: from the Amazon developer console.
|
|
978
|
-
* @param {string} userId who purchased the item.
|
|
979
|
-
* @param {string} receiptId long obfuscated string returned when purchasing the item
|
|
980
|
-
* @param {boolean} useSandbox Defaults to true, use sandbox environment or production.
|
|
981
|
-
* @returns {Promise<object>}
|
|
982
|
-
*/
|
|
983
|
-
export const validateReceiptAmazon = async ({
|
|
984
|
-
developerSecret,
|
|
985
|
-
userId,
|
|
986
|
-
receiptId,
|
|
987
|
-
useSandbox = true,
|
|
988
|
-
}: {
|
|
989
|
-
developerSecret: string;
|
|
990
|
-
userId: string;
|
|
991
|
-
receiptId: string;
|
|
992
|
-
useSandbox: boolean;
|
|
993
|
-
}): Promise<Amazon.ReceiptType> => {
|
|
994
|
-
const sandBoxUrl = useSandbox ? 'sandbox/' : '';
|
|
995
|
-
const url = `https://appstore-sdk.amazon.com/${sandBoxUrl}version/1.0/verifyReceiptId/developer/${developerSecret}/user/${userId}/receiptId/${receiptId}`;
|
|
996
|
-
|
|
997
|
-
return await enhancedFetch<Amazon.ReceiptType>(url);
|
|
998
|
-
};
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* Get the current receipt base64 encoded in IOS.
|
|
1002
|
-
* @param {forceRefresh?:boolean}
|
|
1003
|
-
* @returns {Promise<ProductPurchase[]>}
|
|
1004
|
-
*/
|
|
1005
|
-
export const getPendingPurchasesIOS = async (): Promise<ProductPurchase[]> =>
|
|
1006
|
-
getIosModule().getPendingTransactions();
|
|
1007
|
-
|
|
1008
|
-
/**
|
|
1009
|
-
* Get the current receipt base64 encoded in IOS.
|
|
1010
|
-
* @param {forceRefresh?:boolean}
|
|
1011
|
-
* @returns {Promise<string>}
|
|
1012
|
-
*/
|
|
1013
|
-
export const getReceiptIOS = async ({
|
|
1014
|
-
forceRefresh,
|
|
1015
|
-
}: {
|
|
1016
|
-
forceRefresh?: boolean;
|
|
1017
|
-
}): Promise<string> => getIosModule().requestReceipt(forceRefresh ?? false);
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
* Launches a modal to register the redeem offer code in IOS.
|
|
1021
|
-
* @returns {Promise<null>}
|
|
1022
|
-
*/
|
|
1023
|
-
export const presentCodeRedemptionSheetIOS = async (): Promise<null> =>
|
|
1024
|
-
getIosModule().presentCodeRedemptionSheet();
|
package/src/internal/platform.ts
CHANGED
|
@@ -1,7 +1,84 @@
|
|
|
1
1
|
import {NativeModules, Platform} from 'react-native';
|
|
2
2
|
|
|
3
|
-
const {RNIapAmazonModule} = NativeModules;
|
|
3
|
+
const {RNIapIos, RNIapIosSk2, RNIapModule, RNIapAmazonModule} = NativeModules;
|
|
4
4
|
|
|
5
5
|
export const isIos = Platform.OS === 'ios';
|
|
6
6
|
export const isAndroid = Platform.OS === 'android';
|
|
7
7
|
export const isAmazon = isAndroid && !!RNIapAmazonModule;
|
|
8
|
+
|
|
9
|
+
// Android
|
|
10
|
+
|
|
11
|
+
let androidNativeModule = RNIapModule;
|
|
12
|
+
|
|
13
|
+
export const setAndroidNativeModule = (
|
|
14
|
+
nativeModule: typeof RNIapModule,
|
|
15
|
+
): void => {
|
|
16
|
+
androidNativeModule = nativeModule;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const checkNativeAndroidAvailable = (): void => {
|
|
20
|
+
if (!RNIapModule && !RNIapAmazonModule) {
|
|
21
|
+
throw new Error('IAP_NOT_AVAILABLE');
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getAndroidModule = ():
|
|
26
|
+
| typeof RNIapModule
|
|
27
|
+
| typeof RNIapAmazonModule => {
|
|
28
|
+
checkNativeAndroidAvailable();
|
|
29
|
+
|
|
30
|
+
return androidNativeModule
|
|
31
|
+
? androidNativeModule
|
|
32
|
+
: RNIapModule
|
|
33
|
+
? RNIapModule
|
|
34
|
+
: RNIapAmazonModule;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const getNativeModule = ():
|
|
38
|
+
| typeof RNIapModule
|
|
39
|
+
| typeof RNIapAmazonModule
|
|
40
|
+
| typeof RNIapIos
|
|
41
|
+
| typeof RNIapIosSk2 => {
|
|
42
|
+
return isAndroid ? getAndroidModule() : getIosModule();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// iOS
|
|
46
|
+
|
|
47
|
+
let iosNativeModule: typeof RNIapIos | typeof RNIapIosSk2 = RNIapIos;
|
|
48
|
+
|
|
49
|
+
export const isIosStorekit2 = () =>
|
|
50
|
+
!!iosNativeModule && iosNativeModule === RNIapIosSk2;
|
|
51
|
+
|
|
52
|
+
export const isStorekit2Avaiable = (): boolean => !!RNIapIosSk2;
|
|
53
|
+
|
|
54
|
+
export const setIosNativeModule = (
|
|
55
|
+
nativeModule: typeof RNIapIos | typeof RNIapIosSk2,
|
|
56
|
+
): void => {
|
|
57
|
+
iosNativeModule = nativeModule;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const enableStorekit2 = () => {
|
|
61
|
+
if (RNIapIosSk2) {
|
|
62
|
+
iosNativeModule = RNIapIosSk2;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
console.warn('Storekit 2 is not available on this device');
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const checkNativeIOSAvailable = (): void => {
|
|
71
|
+
if (!RNIapIos && !RNIapIosSk2) {
|
|
72
|
+
throw new Error('IAP_NOT_AVAILABLE');
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const getIosModule = (): typeof RNIapIos | typeof RNIapIosSk2 => {
|
|
77
|
+
checkNativeIOSAvailable();
|
|
78
|
+
|
|
79
|
+
return iosNativeModule
|
|
80
|
+
? iosNativeModule
|
|
81
|
+
: RNIapIosSk2
|
|
82
|
+
? RNIapIosSk2
|
|
83
|
+
: RNIapIos;
|
|
84
|
+
};
|
package/src/modules/amazon.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {NativeModules} from 'react-native';
|
|
2
2
|
|
|
3
|
+
import {enhancedFetch} from '../internal';
|
|
3
4
|
import type {Product, Purchase, Sku} from '../types';
|
|
4
5
|
import type {UserDataAmazon} from '../types/amazon';
|
|
6
|
+
import type * as Amazon from '../types/amazon';
|
|
5
7
|
|
|
6
8
|
import type {NativeModuleProps} from './common';
|
|
7
|
-
|
|
8
9
|
// ----------
|
|
9
10
|
|
|
10
11
|
type GetUser = () => Promise<UserDataAmazon>;
|
|
@@ -38,3 +39,30 @@ export interface AmazonModuleProps extends NativeModuleProps {
|
|
|
38
39
|
|
|
39
40
|
export const AmazonModule =
|
|
40
41
|
NativeModules.RNIapAmazonModule as AmazonModuleProps;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate receipt for Amazon. NOTE: This method is here for debugging purposes only. Including
|
|
45
|
+
* your developer secret in the binary you ship to users is potentially dangerous.
|
|
46
|
+
* Use server side validation instead for your production builds
|
|
47
|
+
* @param {string} developerSecret: from the Amazon developer console.
|
|
48
|
+
* @param {string} userId who purchased the item.
|
|
49
|
+
* @param {string} receiptId long obfuscated string returned when purchasing the item
|
|
50
|
+
* @param {boolean} useSandbox Defaults to true, use sandbox environment or production.
|
|
51
|
+
* @returns {Promise<object>}
|
|
52
|
+
*/
|
|
53
|
+
export const validateReceiptAmazon = async ({
|
|
54
|
+
developerSecret,
|
|
55
|
+
userId,
|
|
56
|
+
receiptId,
|
|
57
|
+
useSandbox = true,
|
|
58
|
+
}: {
|
|
59
|
+
developerSecret: string;
|
|
60
|
+
userId: string;
|
|
61
|
+
receiptId: string;
|
|
62
|
+
useSandbox: boolean;
|
|
63
|
+
}): Promise<Amazon.ReceiptType> => {
|
|
64
|
+
const sandBoxUrl = useSandbox ? 'sandbox/' : '';
|
|
65
|
+
const url = `https://appstore-sdk.amazon.com/${sandBoxUrl}version/1.0/verifyReceiptId/developer/${developerSecret}/user/${userId}/receiptId/${receiptId}`;
|
|
66
|
+
|
|
67
|
+
return await enhancedFetch<Amazon.ReceiptType>(url);
|
|
68
|
+
};
|
package/src/modules/android.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {NativeModules} from 'react-native';
|
|
1
|
+
import {Linking, NativeModules} from 'react-native';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
checkNativeAndroidAvailable,
|
|
5
|
+
enhancedFetch,
|
|
6
|
+
getAndroidModule,
|
|
7
|
+
} from '../internal';
|
|
8
|
+
import {
|
|
9
|
+
InstallSourceAndroid,
|
|
4
10
|
Product,
|
|
5
11
|
ProductType,
|
|
6
12
|
ProrationModesAndroid,
|
|
@@ -8,9 +14,12 @@ import type {
|
|
|
8
14
|
PurchaseResult,
|
|
9
15
|
Sku,
|
|
10
16
|
} from '../types';
|
|
17
|
+
import type * as Android from '../types/android';
|
|
11
18
|
|
|
12
19
|
import type {NativeModuleProps} from './common';
|
|
13
20
|
|
|
21
|
+
const {RNIapModule} = NativeModules;
|
|
22
|
+
|
|
14
23
|
type FlushFailedPurchasesCachedAsPending = () => Promise<boolean>;
|
|
15
24
|
|
|
16
25
|
type GetItemsByType = <T = Product>(
|
|
@@ -63,3 +72,75 @@ export interface AndroidModuleProps extends NativeModuleProps {
|
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
export const AndroidModule = NativeModules.RNIapModule as AndroidModuleProps;
|
|
75
|
+
|
|
76
|
+
export const getInstallSourceAndroid = (): InstallSourceAndroid => {
|
|
77
|
+
return RNIapModule
|
|
78
|
+
? InstallSourceAndroid.GOOGLE_PLAY
|
|
79
|
+
: InstallSourceAndroid.AMAZON;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Deep link to subscriptions screen on Android. No-op on iOS.
|
|
84
|
+
* @param {string} sku The product's SKU (on Android)
|
|
85
|
+
* @returns {Promise<void>}
|
|
86
|
+
*/
|
|
87
|
+
export const deepLinkToSubscriptionsAndroid = async ({
|
|
88
|
+
sku,
|
|
89
|
+
}: {
|
|
90
|
+
sku: Sku;
|
|
91
|
+
}): Promise<void> => {
|
|
92
|
+
checkNativeAndroidAvailable();
|
|
93
|
+
|
|
94
|
+
return Linking.openURL(
|
|
95
|
+
`https://play.google.com/store/account/subscriptions?package=${await RNIapModule.getPackageName()}&sku=${sku}`,
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validate receipt for Android. NOTE: This method is here for debugging purposes only. Including
|
|
101
|
+
* your access token in the binary you ship to users is potentially dangerous.
|
|
102
|
+
* Use server side validation instead for your production builds
|
|
103
|
+
* @param {string} packageName package name of your app.
|
|
104
|
+
* @param {string} productId product id for your in app product.
|
|
105
|
+
* @param {string} productToken token for your purchase.
|
|
106
|
+
* @param {string} accessToken accessToken from googleApis.
|
|
107
|
+
* @param {boolean} isSub whether this is subscription or inapp. `true` for subscription.
|
|
108
|
+
* @returns {Promise<object>}
|
|
109
|
+
*/
|
|
110
|
+
export const validateReceiptAndroid = async ({
|
|
111
|
+
packageName,
|
|
112
|
+
productId,
|
|
113
|
+
productToken,
|
|
114
|
+
accessToken,
|
|
115
|
+
isSub,
|
|
116
|
+
}: {
|
|
117
|
+
packageName: string;
|
|
118
|
+
productId: string;
|
|
119
|
+
productToken: string;
|
|
120
|
+
accessToken: string;
|
|
121
|
+
isSub?: boolean;
|
|
122
|
+
}): Promise<Android.ReceiptType> => {
|
|
123
|
+
const type = isSub ? 'subscriptions' : 'products';
|
|
124
|
+
|
|
125
|
+
const url =
|
|
126
|
+
'https://androidpublisher.googleapis.com/androidpublisher/v3/applications' +
|
|
127
|
+
`/${packageName}/purchases/${type}/${productId}` +
|
|
128
|
+
`/tokens/${productToken}?access_token=${accessToken}`;
|
|
129
|
+
|
|
130
|
+
return await enhancedFetch<Android.ReceiptType>(url);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Acknowledge a product (on Android.) No-op on iOS.
|
|
135
|
+
* @param {string} token The product's token (on Android)
|
|
136
|
+
* @returns {Promise<PurchaseResult | void>}
|
|
137
|
+
*/
|
|
138
|
+
export const acknowledgePurchaseAndroid = ({
|
|
139
|
+
token,
|
|
140
|
+
developerPayload,
|
|
141
|
+
}: {
|
|
142
|
+
token: string;
|
|
143
|
+
developerPayload?: string;
|
|
144
|
+
}): Promise<PurchaseResult | boolean | void> => {
|
|
145
|
+
return getAndroidModule().acknowledgePurchase(token, developerPayload);
|
|
146
|
+
};
|
package/src/modules/ios.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type {ResponseBody as ReceiptValidationResponse} from '@jeremybarbet/apple-api-types';
|
|
2
|
+
|
|
3
|
+
import {enhancedFetch, getIosModule, isIosStorekit2} from '../internal';
|
|
1
4
|
import type {
|
|
2
5
|
Product,
|
|
3
6
|
ProductPurchase,
|
|
@@ -45,3 +48,121 @@ export interface IosModuleProps extends NativeModuleProps {
|
|
|
45
48
|
getPendingTransactions: getPendingTransactions;
|
|
46
49
|
presentCodeRedemptionSheet: presentCodeRedemptionSheet;
|
|
47
50
|
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the current receipt base64 encoded in IOS.
|
|
54
|
+
* @param {forceRefresh?:boolean}
|
|
55
|
+
* @returns {Promise<ProductPurchase[]>}
|
|
56
|
+
*/
|
|
57
|
+
export const getPendingPurchasesIOS = async (): Promise<ProductPurchase[]> =>
|
|
58
|
+
getIosModule().getPendingTransactions();
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the current receipt base64 encoded in IOS.
|
|
62
|
+
* @param {forceRefresh?:boolean}
|
|
63
|
+
* @returns {Promise<string>}
|
|
64
|
+
*/
|
|
65
|
+
export const getReceiptIOS = async ({
|
|
66
|
+
forceRefresh,
|
|
67
|
+
}: {
|
|
68
|
+
forceRefresh?: boolean;
|
|
69
|
+
}): Promise<string> => getIosModule().requestReceipt(forceRefresh ?? false);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Launches a modal to register the redeem offer code in IOS.
|
|
73
|
+
* @returns {Promise<null>}
|
|
74
|
+
*/
|
|
75
|
+
export const presentCodeRedemptionSheetIOS = async (): Promise<null> =>
|
|
76
|
+
getIosModule().presentCodeRedemptionSheet();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Should Add Store Payment (iOS only)
|
|
80
|
+
* Indicates the the App Store purchase should continue from the app instead of the App Store.
|
|
81
|
+
* @returns {Promise<Product | null>} promoted product
|
|
82
|
+
*/
|
|
83
|
+
export const getPromotedProductIOS = (): Promise<Product | null> => {
|
|
84
|
+
if (!isIosStorekit2()) {
|
|
85
|
+
return getIosModule().promotedProduct();
|
|
86
|
+
} else {
|
|
87
|
+
return Promise.reject('Only available on SK1');
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Buy the currently selected promoted product (iOS only)
|
|
93
|
+
* Initiates the payment process for a promoted product. Should only be called in response to the `iap-promoted-product` event.
|
|
94
|
+
* @returns {Promise<void>}
|
|
95
|
+
*/
|
|
96
|
+
export const buyPromotedProductIOS = (): Promise<void> =>
|
|
97
|
+
getIosModule().buyPromotedProduct();
|
|
98
|
+
|
|
99
|
+
const TEST_RECEIPT = 21007;
|
|
100
|
+
const requestAgnosticReceiptValidationIos = async (
|
|
101
|
+
receiptBody: Record<string, unknown>,
|
|
102
|
+
): Promise<ReceiptValidationResponse | false> => {
|
|
103
|
+
const response = await enhancedFetch<ReceiptValidationResponse>(
|
|
104
|
+
'https://buy.itunes.apple.com/verifyReceipt',
|
|
105
|
+
{
|
|
106
|
+
method: 'POST',
|
|
107
|
+
body: receiptBody,
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Best practice is to check for test receipt and check sandbox instead
|
|
112
|
+
// https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
|
|
113
|
+
if (response && response.status === TEST_RECEIPT) {
|
|
114
|
+
const testResponse = await enhancedFetch<ReceiptValidationResponse>(
|
|
115
|
+
'https://sandbox.itunes.apple.com/verifyReceipt',
|
|
116
|
+
{
|
|
117
|
+
method: 'POST',
|
|
118
|
+
body: receiptBody,
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
return testResponse;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return response;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Validate receipt for iOS.
|
|
130
|
+
* @param {object} receiptBody the receipt body to send to apple server.
|
|
131
|
+
* @param {boolean} isTest whether this is in test environment which is sandbox.
|
|
132
|
+
* @returns {Promise<Apple.ReceiptValidationResponse | false>}
|
|
133
|
+
*/
|
|
134
|
+
export const validateReceiptIos = async ({
|
|
135
|
+
receiptBody,
|
|
136
|
+
isTest,
|
|
137
|
+
}: {
|
|
138
|
+
receiptBody: Record<string, unknown>;
|
|
139
|
+
isTest?: boolean;
|
|
140
|
+
}): Promise<ReceiptValidationResponse | false> => {
|
|
141
|
+
if (isTest == null) {
|
|
142
|
+
return await requestAgnosticReceiptValidationIos(receiptBody);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const url = isTest
|
|
146
|
+
? 'https://sandbox.itunes.apple.com/verifyReceipt'
|
|
147
|
+
: 'https://buy.itunes.apple.com/verifyReceipt';
|
|
148
|
+
|
|
149
|
+
return await enhancedFetch<ReceiptValidationResponse>(url);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Clear Transaction (iOS only)
|
|
154
|
+
* Finish remaining transactions. Related to issue #257 and #801
|
|
155
|
+
* link : https://github.com/dooboolab/react-native-iap/issues/257
|
|
156
|
+
* https://github.com/dooboolab/react-native-iap/issues/801
|
|
157
|
+
* @returns {Promise<void>}
|
|
158
|
+
*/
|
|
159
|
+
export const clearTransactionIOS = (): Promise<void> =>
|
|
160
|
+
getIosModule().clearTransaction();
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Clear valid Products (iOS only)
|
|
164
|
+
* Remove all products which are validated by Apple server.
|
|
165
|
+
* @returns {void}
|
|
166
|
+
*/
|
|
167
|
+
export const clearProductsIOS = (): Promise<void> =>
|
|
168
|
+
getIosModule().clearProducts();
|