expo-iap 1.0.3 → 2.0.0-rc.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/.eslintrc.js +9 -0
- package/.prettierrc.js +9 -0
- package/.swiftlint.yml +10 -0
- package/README.md +28 -21
- package/android/build.gradle +34 -72
- package/android/src/main/AndroidManifest.xml +1 -4
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +535 -0
- package/android/src/main/java/expo/modules/iap/MissingCurrentActivityException.kt +6 -0
- package/android/src/main/java/expo/modules/iap/PlayUtils.kt +124 -0
- package/build/ExpoIap.types.d.ts +89 -0
- package/build/ExpoIap.types.d.ts.map +1 -0
- package/build/ExpoIap.types.js +59 -0
- package/build/ExpoIap.types.js.map +1 -0
- package/build/ExpoIapModule.d.ts +3 -0
- package/build/ExpoIapModule.d.ts.map +1 -0
- package/build/ExpoIapModule.js +5 -0
- package/build/ExpoIapModule.js.map +1 -0
- package/build/index.d.ts +38 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +202 -0
- package/build/index.js.map +1 -0
- package/build/modules/android.d.ts +40 -0
- package/build/modules/android.d.ts.map +1 -0
- package/build/modules/android.js +54 -0
- package/build/modules/android.js.map +1 -0
- package/build/modules/ios.d.ts +41 -0
- package/build/modules/ios.d.ts.map +1 -0
- package/build/modules/ios.js +44 -0
- package/build/modules/ios.js.map +1 -0
- package/build/types/ExpoIapAndroid.types.d.ts +113 -0
- package/build/types/ExpoIapAndroid.types.d.ts.map +1 -0
- package/build/types/ExpoIapAndroid.types.js +23 -0
- package/build/types/ExpoIapAndroid.types.js.map +1 -0
- package/build/types/ExpoIapIos.types.d.ts +122 -0
- package/build/types/ExpoIapIos.types.d.ts.map +1 -0
- package/build/types/ExpoIapIos.types.js +2 -0
- package/build/types/ExpoIapIos.types.js.map +1 -0
- package/bun.lockb +0 -0
- package/expo-module.config.json +4 -8
- package/ios/ExpoIap.podspec +27 -0
- package/ios/ExpoIapModule.swift +498 -0
- package/ios/ProductStore.swift +27 -0
- package/ios/Types.swift +54 -0
- package/package.json +33 -62
- package/src/ExpoIap.types.ts +125 -0
- package/src/ExpoIapModule.ts +5 -0
- package/src/index.ts +397 -0
- package/src/modules/android.ts +89 -0
- package/src/modules/ios.ts +81 -0
- package/src/types/ExpoIapAndroid.types.ts +123 -0
- package/src/types/ExpoIapIos.types.ts +141 -0
- package/tsconfig.json +9 -0
- package/.editorconfig +0 -10
- package/.flowconfig +0 -11
- package/.monolinterrc +0 -3
- package/.yarn/install-state.gz +0 -0
- package/.yarn/releases/yarn-3.1.1.cjs +0 -768
- package/.yarnrc.yml +0 -3
- package/LICENSE +0 -21
- package/RNIap.podspec +0 -18
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/android/gradle.properties +0 -2
- package/android/gradlew +0 -160
- package/android/gradlew.bat +0 -90
- package/android/libs/in-app-purchasing-2.0.76.jar +0 -0
- package/android/src/amazon/AndroidManifest.xml +0 -12
- package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonListener.kt +0 -356
- package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonModule.kt +0 -128
- package/android/src/amazon/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -20
- package/android/src/main/java/com/dooboolab/RNIap/DoobooUtils.kt +0 -180
- package/android/src/play/java/com/dooboolab/RNIap/PlayUtils.kt +0 -77
- package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +0 -698
- package/android/src/play/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -20
- package/babel.config.js +0 -10
- package/index.d.ts +0 -3
- package/index.js +0 -3
- package/index.js.flow +0 -9
- package/ios/RNIap.xcodeproj/project.pbxproj +0 -370
- package/ios/RNIap.xcodeproj/xcshareddata/xcschemes/RNIap.xcscheme +0 -80
- package/ios/RNIapIos.m +0 -60
- package/ios/RNIapIos.swift +0 -932
- package/ios/RNIapQueue.swift +0 -35
- package/jest.config.js +0 -194
- package/src/__test__/iap.test.d.ts +0 -1
- package/src/__test__/iap.test.js +0 -59
- package/src/hooks/useIAP.d.ts +0 -21
- package/src/hooks/useIAP.js +0 -140
- package/src/hooks/withIAPContext.d.ts +0 -21
- package/src/hooks/withIAPContext.js +0 -142
- package/src/iap.d.ts +0 -197
- package/src/iap.js +0 -625
- package/src/index.d.ts +0 -4
- package/src/index.js +0 -4
- package/src/types/amazon.d.ts +0 -23
- package/src/types/amazon.js +0 -1
- package/src/types/android.d.ts +0 -47
- package/src/types/android.js +0 -22
- package/src/types/apple.d.ts +0 -424
- package/src/types/apple.js +0 -165
- package/src/types/index.d.ts +0 -117
- package/src/types/index.js +0 -40
- package/test/mocks/react-native-modules.js +0 -14
package/package.json
CHANGED
|
@@ -1,76 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-iap",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"types": "index.d.ts",
|
|
8
|
-
"postinstall": "dooboolab-welcome postinstall",
|
|
3
|
+
"version": "2.0.0-rc.1",
|
|
4
|
+
"description": "In App Purchase module in Expo",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
9
7
|
"scripts": {
|
|
10
|
-
"build": "
|
|
11
|
-
"
|
|
12
|
-
"lint": "eslint
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
},
|
|
21
|
-
"repository": {
|
|
22
|
-
"type": "git",
|
|
23
|
-
"url": "https://github.com/dooboolab/react-native-iap#expo"
|
|
8
|
+
"build": "expo-module build",
|
|
9
|
+
"clean": "expo-module clean",
|
|
10
|
+
"lint": "eslint --ext .ts,.tsx,.js,.jsx src",
|
|
11
|
+
"test": "expo-module test",
|
|
12
|
+
"prepare": "expo-module prepare",
|
|
13
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
14
|
+
"expo-module": "expo-module",
|
|
15
|
+
"open:ios": "xed example/ios",
|
|
16
|
+
"open:android": "open -a \"Android Studio\" example/android",
|
|
17
|
+
"lint:kotlin": "ktlint --format ./android"
|
|
24
18
|
},
|
|
25
19
|
"keywords": [
|
|
26
|
-
"expo",
|
|
27
20
|
"react-native",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
],
|
|
32
|
-
"author": "dooboolab",
|
|
33
|
-
"contributors": [
|
|
34
|
-
{
|
|
35
|
-
"name": "Andres Aguilar",
|
|
36
|
-
"url": "https://github.com/andresesfm"
|
|
37
|
-
}
|
|
21
|
+
"expo",
|
|
22
|
+
"expo-iap",
|
|
23
|
+
"ExpoIap"
|
|
38
24
|
],
|
|
25
|
+
"repository": "https://github.com/hyochan",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/hyochan/issues"
|
|
28
|
+
},
|
|
29
|
+
"author": "hyochan <dooboolab@gmail.com> (https://github.com/hyochan)",
|
|
39
30
|
"license": "MIT",
|
|
31
|
+
"homepage": "https://github.com/hyochan#readme",
|
|
32
|
+
"dependencies": {},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/react": "^18.0.25",
|
|
35
|
+
"expo-module-scripts": "^3.5.2",
|
|
36
|
+
"expo-modules-core": "^1.12.19",
|
|
37
|
+
"eslint": "8.57.0",
|
|
38
|
+
"eslint-config-expo": "^7.1.2",
|
|
39
|
+
"eslint-config-prettier": "^9.1.0",
|
|
40
|
+
"eslint-plugin-prettier": "^5.1.3"
|
|
41
|
+
},
|
|
40
42
|
"peerDependencies": {
|
|
41
43
|
"expo": "*",
|
|
42
44
|
"react": "*",
|
|
43
45
|
"react-native": "*"
|
|
44
|
-
},
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"dooboolab-welcome": "1.3.2"
|
|
47
|
-
},
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"@babel/core": "7.16.0",
|
|
50
|
-
"@babel/plugin-proposal-class-properties": "7.16.0",
|
|
51
|
-
"@babel/plugin-proposal-private-methods": "7.16.0",
|
|
52
|
-
"@babel/preset-env": "7.16.4",
|
|
53
|
-
"@babel/preset-react": "7.16.0",
|
|
54
|
-
"@babel/preset-typescript": "7.16.0",
|
|
55
|
-
"@dooboo/eslint-config": "1.2.2",
|
|
56
|
-
"@testing-library/jest-native": "4.0.4",
|
|
57
|
-
"@testing-library/react-native": "8.0.0",
|
|
58
|
-
"@types/eslint": "8.2.0",
|
|
59
|
-
"@types/jest": "27.0.3",
|
|
60
|
-
"@types/react": "17.0.37",
|
|
61
|
-
"@types/react-native": "0.66.6",
|
|
62
|
-
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
|
63
|
-
"babel-core": "7.0.0-bridge.0",
|
|
64
|
-
"babel-jest": "27.4.2",
|
|
65
|
-
"eslint": "8.3.0",
|
|
66
|
-
"flow-bin": "0.166.1",
|
|
67
|
-
"flowgen": "1.16.0",
|
|
68
|
-
"jest": "27.4.3",
|
|
69
|
-
"metro-react-native-babel-preset": "0.66.2",
|
|
70
|
-
"monolinter": "1.0.4",
|
|
71
|
-
"prettier": "2.5.0",
|
|
72
|
-
"react-native": "0.65.1",
|
|
73
|
-
"ts-jest": "27.0.7",
|
|
74
|
-
"typescript": "4.5.2"
|
|
75
46
|
}
|
|
76
47
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductAndroid,
|
|
3
|
+
RequestPurchaseAndroidProps,
|
|
4
|
+
RequestSubscriptionAndroidProps,
|
|
5
|
+
SubscriptionProductAndroid,
|
|
6
|
+
} from './types/ExpoIapAndroid.types';
|
|
7
|
+
import {
|
|
8
|
+
ProductIos,
|
|
9
|
+
RequestPurchaseIosProps,
|
|
10
|
+
RequestSubscriptionIosProps,
|
|
11
|
+
SubscriptionProductIos,
|
|
12
|
+
} from './types/ExpoIapIos.types';
|
|
13
|
+
export type ChangeEventPayload = {
|
|
14
|
+
value: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type Product = ProductAndroid | ProductIos;
|
|
18
|
+
export enum ProductType {
|
|
19
|
+
InAppPurchase = 'inapp',
|
|
20
|
+
Subscription = 'subs',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type SubscriptionProduct =
|
|
24
|
+
| SubscriptionProductAndroid
|
|
25
|
+
| SubscriptionProductIos;
|
|
26
|
+
|
|
27
|
+
export type RequestPurchaseProps =
|
|
28
|
+
| RequestPurchaseIosProps
|
|
29
|
+
| RequestPurchaseAndroidProps;
|
|
30
|
+
|
|
31
|
+
enum PurchaseStateAndroid {
|
|
32
|
+
UNSPECIFIED_STATE = 0,
|
|
33
|
+
PURCHASED = 1,
|
|
34
|
+
PENDING = 2,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type ProductPurchase = {
|
|
38
|
+
productId: string;
|
|
39
|
+
transactionId?: string;
|
|
40
|
+
transactionDate: number;
|
|
41
|
+
transactionReceipt: string;
|
|
42
|
+
purchaseToken?: string;
|
|
43
|
+
//iOS
|
|
44
|
+
quantityIOS?: number;
|
|
45
|
+
originalTransactionDateIOS?: number;
|
|
46
|
+
originalTransactionIdentifierIOS?: string;
|
|
47
|
+
verificationResultIOS?: string;
|
|
48
|
+
appAccountToken?: string;
|
|
49
|
+
//Android
|
|
50
|
+
productIds?: string[];
|
|
51
|
+
dataAndroid?: string;
|
|
52
|
+
signatureAndroid?: string;
|
|
53
|
+
autoRenewingAndroid?: boolean;
|
|
54
|
+
purchaseStateAndroid?: PurchaseStateAndroid;
|
|
55
|
+
isAcknowledgedAndroid?: boolean;
|
|
56
|
+
packageNameAndroid?: string;
|
|
57
|
+
developerPayloadAndroid?: string;
|
|
58
|
+
obfuscatedAccountIdAndroid?: string;
|
|
59
|
+
obfuscatedProfileIdAndroid?: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type RequestSubscriptionProps =
|
|
63
|
+
| RequestSubscriptionAndroidProps
|
|
64
|
+
| RequestSubscriptionIosProps;
|
|
65
|
+
|
|
66
|
+
enum TransactionReason {
|
|
67
|
+
PURCHASE = 'PURCHASE',
|
|
68
|
+
RENEWAL = 'RENEWAL',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type SubscriptionPurchase = {
|
|
72
|
+
autoRenewingAndroid?: boolean;
|
|
73
|
+
originalTransactionDateIOS?: number;
|
|
74
|
+
originalTransactionIdentifierIOS?: string;
|
|
75
|
+
verificationResultIOS?: string;
|
|
76
|
+
transactionReasonIOS?: TransactionReason | string;
|
|
77
|
+
} & ProductPurchase;
|
|
78
|
+
|
|
79
|
+
export type Purchase = ProductPurchase | SubscriptionPurchase;
|
|
80
|
+
|
|
81
|
+
export type PurchaseResult = {
|
|
82
|
+
responseCode?: number;
|
|
83
|
+
debugMessage?: string;
|
|
84
|
+
code?: string;
|
|
85
|
+
message?: string;
|
|
86
|
+
purchaseToken?: string;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export enum ErrorCode {
|
|
90
|
+
E_UNKNOWN = 'E_UNKNOWN',
|
|
91
|
+
E_USER_CANCELLED = 'E_USER_CANCELLED',
|
|
92
|
+
E_USER_ERROR = 'E_USER_ERROR',
|
|
93
|
+
E_ITEM_UNAVAILABLE = 'E_ITEM_UNAVAILABLE',
|
|
94
|
+
E_REMOTE_ERROR = 'E_REMOTE_ERROR',
|
|
95
|
+
E_NETWORK_ERROR = 'E_NETWORK_ERROR',
|
|
96
|
+
E_SERVICE_ERROR = 'E_SERVICE_ERROR',
|
|
97
|
+
E_RECEIPT_FAILED = 'E_RECEIPT_FAILED',
|
|
98
|
+
E_RECEIPT_FINISHED_FAILED = 'E_RECEIPT_FINISHED_FAILED',
|
|
99
|
+
E_NOT_PREPARED = 'E_NOT_PREPARED',
|
|
100
|
+
E_NOT_ENDED = 'E_NOT_ENDED',
|
|
101
|
+
E_ALREADY_OWNED = 'E_ALREADY_OWNED',
|
|
102
|
+
E_DEVELOPER_ERROR = 'E_DEVELOPER_ERROR',
|
|
103
|
+
E_BILLING_RESPONSE_JSON_PARSE_ERROR = 'E_BILLING_RESPONSE_JSON_PARSE_ERROR',
|
|
104
|
+
E_DEFERRED_PAYMENT = 'E_DEFERRED_PAYMENT',
|
|
105
|
+
E_INTERRUPTED = 'E_INTERRUPTED',
|
|
106
|
+
E_IAP_NOT_AVAILABLE = 'E_IAP_NOT_AVAILABLE',
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class PurchaseError implements Error {
|
|
110
|
+
constructor(
|
|
111
|
+
public name: string,
|
|
112
|
+
public message: string,
|
|
113
|
+
public responseCode?: number,
|
|
114
|
+
public debugMessage?: string,
|
|
115
|
+
public code?: ErrorCode,
|
|
116
|
+
public productId?: string,
|
|
117
|
+
) {
|
|
118
|
+
this.name = '[react-native-iap]: PurchaseError';
|
|
119
|
+
this.message = message;
|
|
120
|
+
this.responseCode = responseCode;
|
|
121
|
+
this.debugMessage = debugMessage;
|
|
122
|
+
this.code = code;
|
|
123
|
+
this.productId = productId;
|
|
124
|
+
}
|
|
125
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// Import the native module. On web, it will be resolved to ExpoIap.web.ts
|
|
2
|
+
// and on native platforms to ExpoIap.ts
|
|
3
|
+
import {NativeModulesProxy, EventEmitter} from 'expo-modules-core';
|
|
4
|
+
import {Platform} from 'react-native';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Product,
|
|
8
|
+
ProductPurchase,
|
|
9
|
+
ProductType,
|
|
10
|
+
Purchase,
|
|
11
|
+
PurchaseError,
|
|
12
|
+
PurchaseResult,
|
|
13
|
+
RequestSubscriptionProps,
|
|
14
|
+
SubscriptionProduct,
|
|
15
|
+
SubscriptionPurchase,
|
|
16
|
+
} from './ExpoIap.types';
|
|
17
|
+
import ExpoIapModule from './ExpoIapModule';
|
|
18
|
+
import {RequestPurchaseAndroidProps, RequestSubscriptionAndroidProps} from './types/ExpoIapAndroid.types';
|
|
19
|
+
import {
|
|
20
|
+
PaymentDiscount,
|
|
21
|
+
ProductIos,
|
|
22
|
+
RequestPurchaseIosProps,
|
|
23
|
+
RequestSubscriptionIosProps,
|
|
24
|
+
SubscriptionProductIos,
|
|
25
|
+
TransactionSk2,
|
|
26
|
+
} from './types/ExpoIapIos.types';
|
|
27
|
+
import {isProductIos} from './modules/ios';
|
|
28
|
+
|
|
29
|
+
export * from './modules/android';
|
|
30
|
+
export * from './modules/ios';
|
|
31
|
+
|
|
32
|
+
// Get the native constant value.
|
|
33
|
+
export const PI = ExpoIapModule.PI;
|
|
34
|
+
|
|
35
|
+
export enum IapEvent {
|
|
36
|
+
PurchaseUpdated = 'purchase-updated',
|
|
37
|
+
PurchaseError = 'purchase-error',
|
|
38
|
+
TransactionIapUpdated = 'iap-transaction-updated',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function setValueAsync(value: string) {
|
|
42
|
+
return await ExpoIapModule.setValueAsync(value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const emitter = new EventEmitter(
|
|
46
|
+
ExpoIapModule ?? NativeModulesProxy.ExpoIap,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export const purchaseUpdatedListener = (
|
|
50
|
+
listener: (event: Purchase) => void,
|
|
51
|
+
) => {
|
|
52
|
+
const emitterSubscription = emitter.addListener(
|
|
53
|
+
IapEvent.PurchaseUpdated,
|
|
54
|
+
listener,
|
|
55
|
+
);
|
|
56
|
+
return emitterSubscription;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const purchaseErrorListener = (
|
|
60
|
+
listener: (error: PurchaseError) => void,
|
|
61
|
+
) => {
|
|
62
|
+
return emitter.addListener<PurchaseError>(IapEvent.PurchaseError, listener);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export function initConnection() {
|
|
66
|
+
return ExpoIapModule.initConnection();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
70
|
+
if (!skus?.length) {
|
|
71
|
+
return Promise.reject(new Error('"skus" is required'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Platform.select({
|
|
75
|
+
ios: async () => {
|
|
76
|
+
const items = (await ExpoIapModule.getItems(skus)) as ProductIos[];
|
|
77
|
+
return items.filter((item: ProductIos) => isProductIos(item));
|
|
78
|
+
},
|
|
79
|
+
android: async () => {
|
|
80
|
+
const products = await ExpoIapModule.getItemsByType(
|
|
81
|
+
ProductType.InAppPurchase,
|
|
82
|
+
skus,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return products;
|
|
86
|
+
},
|
|
87
|
+
default: () => Promise.reject(new Error('Unsupported Platform')),
|
|
88
|
+
})();
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const getSubscriptions = async (
|
|
92
|
+
skus: string[],
|
|
93
|
+
): Promise<SubscriptionProduct[]> => {
|
|
94
|
+
if (!skus?.length) {
|
|
95
|
+
return Promise.reject(new Error('"skus" is required'));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return Platform.select({
|
|
99
|
+
ios: async (): Promise<SubscriptionProductIos[]> => {
|
|
100
|
+
const items: SubscriptionProductIos[] = (
|
|
101
|
+
(await ExpoIapModule.getItems(skus)) as SubscriptionProductIos[]
|
|
102
|
+
).filter((item: SubscriptionProductIos) => skus.includes(item.id));
|
|
103
|
+
|
|
104
|
+
return items;
|
|
105
|
+
},
|
|
106
|
+
android: async () => {
|
|
107
|
+
return ExpoIapModule.getItemsByType('subs', skus);
|
|
108
|
+
},
|
|
109
|
+
default: () => Promise.reject(new Error('Unsupported Platform')),
|
|
110
|
+
})();
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export async function endConnection(): Promise<boolean> {
|
|
114
|
+
return ExpoIapModule.endConnection();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const getPurchaseHistory = ({
|
|
118
|
+
alsoPublishToEventListener = false,
|
|
119
|
+
automaticallyFinishRestoredTransactions = true,
|
|
120
|
+
onlyIncludeActiveItems = false,
|
|
121
|
+
}: {
|
|
122
|
+
alsoPublishToEventListener?: boolean;
|
|
123
|
+
automaticallyFinishRestoredTransactions?: boolean;
|
|
124
|
+
onlyIncludeActiveItems?: boolean;
|
|
125
|
+
} = {}): Promise<ProductPurchase[]> =>
|
|
126
|
+
(
|
|
127
|
+
Platform.select({
|
|
128
|
+
ios: async () => {
|
|
129
|
+
return ExpoIapModule.getAvailableItems(
|
|
130
|
+
alsoPublishToEventListener,
|
|
131
|
+
onlyIncludeActiveItems,
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
android: async () => {
|
|
135
|
+
const products = await ExpoIapModule.getPurchaseHistoryByType(
|
|
136
|
+
ProductType.InAppPurchase,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const subscriptions = await ExpoIapModule.getPurchaseHistoryByType(
|
|
140
|
+
ProductType.Subscription,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return products.concat(subscriptions);
|
|
144
|
+
},
|
|
145
|
+
}) || (() => Promise.resolve([]))
|
|
146
|
+
)();
|
|
147
|
+
|
|
148
|
+
export const getAvailablePurchases = ({
|
|
149
|
+
alsoPublishToEventListener = false,
|
|
150
|
+
automaticallyFinishRestoredTransactions = false,
|
|
151
|
+
onlyIncludeActiveItems = true,
|
|
152
|
+
}: {
|
|
153
|
+
alsoPublishToEventListener?: boolean;
|
|
154
|
+
automaticallyFinishRestoredTransactions?: boolean;
|
|
155
|
+
onlyIncludeActiveItems?: boolean;
|
|
156
|
+
} = {}): Promise<ProductPurchase[]> =>
|
|
157
|
+
(
|
|
158
|
+
Platform.select({
|
|
159
|
+
ios: () =>
|
|
160
|
+
ExpoIapModule.getAvailableItems(
|
|
161
|
+
alsoPublishToEventListener,
|
|
162
|
+
onlyIncludeActiveItems,
|
|
163
|
+
),
|
|
164
|
+
android: async () => {
|
|
165
|
+
const products = await ExpoIapModule.getAvailableItemsByType(
|
|
166
|
+
ProductType.InAppPurchase,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const subscriptions = await ExpoIapModule.getAvailableItemsByType(
|
|
170
|
+
ProductType.Subscription,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
return products.concat(subscriptions);
|
|
174
|
+
},
|
|
175
|
+
}) || (() => Promise.resolve([]))
|
|
176
|
+
)();
|
|
177
|
+
|
|
178
|
+
const offerToRecordIos = (
|
|
179
|
+
offer: PaymentDiscount | undefined,
|
|
180
|
+
): Record<keyof PaymentDiscount, string> | undefined => {
|
|
181
|
+
if (!offer) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
identifier: offer.identifier,
|
|
186
|
+
keyIdentifier: offer.keyIdentifier,
|
|
187
|
+
nonce: offer.nonce,
|
|
188
|
+
signature: offer.signature,
|
|
189
|
+
timestamp: offer.timestamp.toString(),
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const iosTransactionToPurchaseMap = ({
|
|
194
|
+
id,
|
|
195
|
+
originalPurchaseDate,
|
|
196
|
+
productID,
|
|
197
|
+
purchaseDate,
|
|
198
|
+
purchasedQuantity,
|
|
199
|
+
originalID,
|
|
200
|
+
verificationResult,
|
|
201
|
+
appAccountToken,
|
|
202
|
+
jsonRepresentation,
|
|
203
|
+
}: TransactionSk2): Purchase => {
|
|
204
|
+
let transactionReasonIOS;
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
if (jsonRepresentation) {
|
|
208
|
+
const transactionData = JSON.parse(jsonRepresentation);
|
|
209
|
+
transactionReasonIOS = transactionData.transactionReason;
|
|
210
|
+
}
|
|
211
|
+
} catch (e) {
|
|
212
|
+
console.log('SK2 Error parsing jsonRepresentation', e);
|
|
213
|
+
}
|
|
214
|
+
const purchase: Purchase = {
|
|
215
|
+
productId: productID,
|
|
216
|
+
transactionId: String(id),
|
|
217
|
+
transactionDate: purchaseDate, //??
|
|
218
|
+
transactionReceipt: '', // Not available
|
|
219
|
+
purchaseToken: '', //Not available
|
|
220
|
+
quantityIOS: purchasedQuantity,
|
|
221
|
+
originalTransactionDateIOS: originalPurchaseDate,
|
|
222
|
+
originalTransactionIdentifierIOS: originalID,
|
|
223
|
+
verificationResultIOS: verificationResult ?? '',
|
|
224
|
+
appAccountToken: appAccountToken ?? '',
|
|
225
|
+
transactionReasonIOS: transactionReasonIOS ?? '',
|
|
226
|
+
};
|
|
227
|
+
return purchase;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const requestPurchase = (
|
|
231
|
+
request: RequestPurchaseIosProps | RequestPurchaseAndroidProps,
|
|
232
|
+
): Promise<ProductPurchase | ProductPurchase[] | void> =>
|
|
233
|
+
(
|
|
234
|
+
Platform.select({
|
|
235
|
+
ios: async () => {
|
|
236
|
+
if (!('sku' in request)) {
|
|
237
|
+
throw new Error('sku is required for iOS purchase');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const {
|
|
241
|
+
sku,
|
|
242
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
243
|
+
appAccountToken,
|
|
244
|
+
quantity,
|
|
245
|
+
withOffer,
|
|
246
|
+
} = request;
|
|
247
|
+
|
|
248
|
+
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
249
|
+
console.warn(
|
|
250
|
+
'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.',
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const offer = offerToRecordIos(withOffer);
|
|
255
|
+
|
|
256
|
+
const result = await ExpoIapModule.buyProduct(
|
|
257
|
+
sku,
|
|
258
|
+
andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
259
|
+
appAccountToken,
|
|
260
|
+
quantity ?? -1,
|
|
261
|
+
offer,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const purchase = iosTransactionToPurchaseMap(result);
|
|
265
|
+
return Promise.resolve(purchase);
|
|
266
|
+
},
|
|
267
|
+
android: async () => {
|
|
268
|
+
if (!('skus' in request) || !request.skus.length) {
|
|
269
|
+
throw new Error('skus is required for Android purchase');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const {
|
|
273
|
+
skus,
|
|
274
|
+
obfuscatedAccountIdAndroid,
|
|
275
|
+
obfuscatedProfileIdAndroid,
|
|
276
|
+
isOfferPersonalized,
|
|
277
|
+
} = request;
|
|
278
|
+
|
|
279
|
+
return ExpoIapModule.buyItemByType({
|
|
280
|
+
type: ProductType.InAppPurchase,
|
|
281
|
+
skuArr: skus,
|
|
282
|
+
purchaseToken: undefined,
|
|
283
|
+
replacementMode: -1,
|
|
284
|
+
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
285
|
+
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
286
|
+
offerTokenArr: [],
|
|
287
|
+
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
}) || Promise.resolve
|
|
291
|
+
)();
|
|
292
|
+
|
|
293
|
+
export const requestSubscription = (
|
|
294
|
+
request: RequestSubscriptionProps,
|
|
295
|
+
): Promise<SubscriptionPurchase | SubscriptionPurchase[] | null | void> =>
|
|
296
|
+
(
|
|
297
|
+
Platform.select({
|
|
298
|
+
ios: async () => {
|
|
299
|
+
if (!('sku' in request)) {
|
|
300
|
+
throw new Error('sku is required for iOS subscriptions');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const {
|
|
304
|
+
sku,
|
|
305
|
+
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
306
|
+
appAccountToken,
|
|
307
|
+
quantity,
|
|
308
|
+
withOffer,
|
|
309
|
+
} = request as RequestSubscriptionIosProps;
|
|
310
|
+
|
|
311
|
+
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
312
|
+
console.warn(
|
|
313
|
+
'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.',
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const offer = offerToRecordIos(withOffer);
|
|
318
|
+
|
|
319
|
+
const purchase = iosTransactionToPurchaseMap(
|
|
320
|
+
await ExpoIapModule.buyProduct(
|
|
321
|
+
sku,
|
|
322
|
+
andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
323
|
+
appAccountToken,
|
|
324
|
+
quantity ?? -1,
|
|
325
|
+
offer,
|
|
326
|
+
),
|
|
327
|
+
);
|
|
328
|
+
return Promise.resolve(purchase);
|
|
329
|
+
},
|
|
330
|
+
android: async () => {
|
|
331
|
+
console.log('requestSubscription', request);
|
|
332
|
+
const {
|
|
333
|
+
skus,
|
|
334
|
+
isOfferPersonalized,
|
|
335
|
+
obfuscatedAccountIdAndroid,
|
|
336
|
+
obfuscatedProfileIdAndroid,
|
|
337
|
+
subscriptionOffers,
|
|
338
|
+
replacementModeAndroid,
|
|
339
|
+
purchaseTokenAndroid,
|
|
340
|
+
} = request as RequestSubscriptionAndroidProps;
|
|
341
|
+
|
|
342
|
+
return ExpoIapModule.buyItemByType({
|
|
343
|
+
type: ProductType.Subscription,
|
|
344
|
+
skuArr: skus.map((so) => so),
|
|
345
|
+
purchaseToken: purchaseTokenAndroid,
|
|
346
|
+
replacementMode: replacementModeAndroid,
|
|
347
|
+
obfuscatedAccountId: obfuscatedAccountIdAndroid,
|
|
348
|
+
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
349
|
+
offerTokenArr: subscriptionOffers.map((so) => so.offerToken),
|
|
350
|
+
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
351
|
+
});
|
|
352
|
+
},
|
|
353
|
+
}) || (() => Promise.resolve(null))
|
|
354
|
+
)();
|
|
355
|
+
|
|
356
|
+
export const finishTransaction = ({
|
|
357
|
+
purchase,
|
|
358
|
+
isConsumable,
|
|
359
|
+
developerPayloadAndroid,
|
|
360
|
+
}: {
|
|
361
|
+
purchase: Purchase;
|
|
362
|
+
isConsumable?: boolean;
|
|
363
|
+
developerPayloadAndroid?: string;
|
|
364
|
+
}): Promise<PurchaseResult | boolean> => {
|
|
365
|
+
return (
|
|
366
|
+
Platform.select({
|
|
367
|
+
ios: async () => {
|
|
368
|
+
const transactionId = purchase.transactionId;
|
|
369
|
+
|
|
370
|
+
if (!transactionId) {
|
|
371
|
+
return Promise.reject(
|
|
372
|
+
new Error('transactionId required to finish iOS transaction'),
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
await ExpoIapModule.finishTransaction(transactionId);
|
|
376
|
+
return Promise.resolve(true);
|
|
377
|
+
},
|
|
378
|
+
android: async () => {
|
|
379
|
+
if (purchase?.purchaseToken) {
|
|
380
|
+
if (!isConsumable) {
|
|
381
|
+
return Promise.reject(
|
|
382
|
+
new Error('purchase is not suitable to be purchased'),
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return ExpoIapModule.consumeProduct(
|
|
387
|
+
purchase.purchaseToken,
|
|
388
|
+
developerPayloadAndroid,
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
return Promise.reject(
|
|
392
|
+
new Error('purchase is not suitable to be purchased'),
|
|
393
|
+
);
|
|
394
|
+
},
|
|
395
|
+
}) || (() => Promise.reject(new Error('Unsupported Platform')))
|
|
396
|
+
)();
|
|
397
|
+
};
|