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.
Files changed (103) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.prettierrc.js +9 -0
  3. package/.swiftlint.yml +10 -0
  4. package/README.md +28 -21
  5. package/android/build.gradle +34 -72
  6. package/android/src/main/AndroidManifest.xml +1 -4
  7. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +535 -0
  8. package/android/src/main/java/expo/modules/iap/MissingCurrentActivityException.kt +6 -0
  9. package/android/src/main/java/expo/modules/iap/PlayUtils.kt +124 -0
  10. package/build/ExpoIap.types.d.ts +89 -0
  11. package/build/ExpoIap.types.d.ts.map +1 -0
  12. package/build/ExpoIap.types.js +59 -0
  13. package/build/ExpoIap.types.js.map +1 -0
  14. package/build/ExpoIapModule.d.ts +3 -0
  15. package/build/ExpoIapModule.d.ts.map +1 -0
  16. package/build/ExpoIapModule.js +5 -0
  17. package/build/ExpoIapModule.js.map +1 -0
  18. package/build/index.d.ts +38 -0
  19. package/build/index.d.ts.map +1 -0
  20. package/build/index.js +202 -0
  21. package/build/index.js.map +1 -0
  22. package/build/modules/android.d.ts +40 -0
  23. package/build/modules/android.d.ts.map +1 -0
  24. package/build/modules/android.js +54 -0
  25. package/build/modules/android.js.map +1 -0
  26. package/build/modules/ios.d.ts +41 -0
  27. package/build/modules/ios.d.ts.map +1 -0
  28. package/build/modules/ios.js +44 -0
  29. package/build/modules/ios.js.map +1 -0
  30. package/build/types/ExpoIapAndroid.types.d.ts +113 -0
  31. package/build/types/ExpoIapAndroid.types.d.ts.map +1 -0
  32. package/build/types/ExpoIapAndroid.types.js +23 -0
  33. package/build/types/ExpoIapAndroid.types.js.map +1 -0
  34. package/build/types/ExpoIapIos.types.d.ts +122 -0
  35. package/build/types/ExpoIapIos.types.d.ts.map +1 -0
  36. package/build/types/ExpoIapIos.types.js +2 -0
  37. package/build/types/ExpoIapIos.types.js.map +1 -0
  38. package/bun.lockb +0 -0
  39. package/expo-module.config.json +4 -8
  40. package/ios/ExpoIap.podspec +27 -0
  41. package/ios/ExpoIapModule.swift +498 -0
  42. package/ios/ProductStore.swift +27 -0
  43. package/ios/Types.swift +54 -0
  44. package/package.json +33 -62
  45. package/src/ExpoIap.types.ts +125 -0
  46. package/src/ExpoIapModule.ts +5 -0
  47. package/src/index.ts +397 -0
  48. package/src/modules/android.ts +89 -0
  49. package/src/modules/ios.ts +81 -0
  50. package/src/types/ExpoIapAndroid.types.ts +123 -0
  51. package/src/types/ExpoIapIos.types.ts +141 -0
  52. package/tsconfig.json +9 -0
  53. package/.editorconfig +0 -10
  54. package/.flowconfig +0 -11
  55. package/.monolinterrc +0 -3
  56. package/.yarn/install-state.gz +0 -0
  57. package/.yarn/releases/yarn-3.1.1.cjs +0 -768
  58. package/.yarnrc.yml +0 -3
  59. package/LICENSE +0 -21
  60. package/RNIap.podspec +0 -18
  61. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  62. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  63. package/android/gradle.properties +0 -2
  64. package/android/gradlew +0 -160
  65. package/android/gradlew.bat +0 -90
  66. package/android/libs/in-app-purchasing-2.0.76.jar +0 -0
  67. package/android/src/amazon/AndroidManifest.xml +0 -12
  68. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonListener.kt +0 -356
  69. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapAmazonModule.kt +0 -128
  70. package/android/src/amazon/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -20
  71. package/android/src/main/java/com/dooboolab/RNIap/DoobooUtils.kt +0 -180
  72. package/android/src/play/java/com/dooboolab/RNIap/PlayUtils.kt +0 -77
  73. package/android/src/play/java/com/dooboolab/RNIap/RNIapModule.kt +0 -698
  74. package/android/src/play/java/com/dooboolab/RNIap/RNIapPackage.kt +0 -20
  75. package/babel.config.js +0 -10
  76. package/index.d.ts +0 -3
  77. package/index.js +0 -3
  78. package/index.js.flow +0 -9
  79. package/ios/RNIap.xcodeproj/project.pbxproj +0 -370
  80. package/ios/RNIap.xcodeproj/xcshareddata/xcschemes/RNIap.xcscheme +0 -80
  81. package/ios/RNIapIos.m +0 -60
  82. package/ios/RNIapIos.swift +0 -932
  83. package/ios/RNIapQueue.swift +0 -35
  84. package/jest.config.js +0 -194
  85. package/src/__test__/iap.test.d.ts +0 -1
  86. package/src/__test__/iap.test.js +0 -59
  87. package/src/hooks/useIAP.d.ts +0 -21
  88. package/src/hooks/useIAP.js +0 -140
  89. package/src/hooks/withIAPContext.d.ts +0 -21
  90. package/src/hooks/withIAPContext.js +0 -142
  91. package/src/iap.d.ts +0 -197
  92. package/src/iap.js +0 -625
  93. package/src/index.d.ts +0 -4
  94. package/src/index.js +0 -4
  95. package/src/types/amazon.d.ts +0 -23
  96. package/src/types/amazon.js +0 -1
  97. package/src/types/android.d.ts +0 -47
  98. package/src/types/android.js +0 -22
  99. package/src/types/apple.d.ts +0 -424
  100. package/src/types/apple.js +0 -165
  101. package/src/types/index.d.ts +0 -117
  102. package/src/types/index.js +0 -40
  103. 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": "1.0.3",
4
- "packageManager": "yarn@3.1.1",
5
- "description": "React Native In App Purchase Module.",
6
- "main": "index.js",
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": "tsc && flowgen index.d.ts -o index.js.flow",
11
- "flow": "flow",
12
- "lint": "eslint src --ext .ts,.tsx",
13
- "lint:android": "cd android ",
14
- "test": "jest",
15
- "test:flow": "flow",
16
- "test:typescript": "tsc --noEmit",
17
- "tsc:sync": "npx typesync",
18
- "tsc": "tsc",
19
- "validate": "yarn lint && yarn test:typescript && yarn test:flow && yarn test"
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
- "in app purchase",
29
- "in-app-purchase",
30
- "billing"
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
+ }
@@ -0,0 +1,5 @@
1
+ import {requireNativeModule} from 'expo-modules-core';
2
+
3
+ // It loads the native module object from the JSI or falls back to
4
+ // the bridge module (from NativeModulesProxy) if the remote debugger is on.
5
+ export default requireNativeModule('ExpoIap');
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
+ };