expo-iap 2.0.0 → 2.2.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/README.md +15 -19
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +10 -9
- package/build/ExpoIap.types.d.ts +31 -40
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js +0 -11
- package/build/ExpoIap.types.js.map +1 -1
- package/build/index.d.ts +3 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +33 -46
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts +12 -4
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +8 -4
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +16 -8
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +9 -5
- package/build/modules/ios.js.map +1 -1
- package/build/types/ExpoIapAndroid.types.d.ts +31 -31
- package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
- package/build/types/ExpoIapAndroid.types.js +6 -0
- package/build/types/ExpoIapAndroid.types.js.map +1 -1
- package/build/types/ExpoIapIos.types.d.ts +31 -34
- package/build/types/ExpoIapIos.types.d.ts.map +1 -1
- package/build/types/ExpoIapIos.types.js.map +1 -1
- package/build/useIap.d.ts +23 -0
- package/build/useIap.d.ts.map +1 -0
- package/build/useIap.js +100 -0
- package/build/useIap.js.map +1 -0
- package/bun.lockb +0 -0
- package/iap.md +161 -0
- package/ios/ExpoIapModule.swift +124 -31
- package/package.json +5 -5
- package/plugin/build/withIAP.d.ts +0 -3
- package/plugin/build/withIAP.js +31 -49
- package/plugin/src/withIAP.ts +39 -86
- package/src/ExpoIap.types.ts +48 -48
- package/src/index.ts +50 -96
- package/src/modules/android.ts +16 -12
- package/src/modules/ios.ts +21 -18
- package/src/types/ExpoIapAndroid.types.ts +37 -33
- package/src/types/ExpoIapIos.types.ts +36 -40
- package/src/useIap.ts +185 -0
package/plugin/src/withIAP.ts
CHANGED
|
@@ -1,38 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WarningAggregator,
|
|
3
|
-
|
|
3
|
+
withAndroidManifest,
|
|
4
4
|
withProjectBuildGradle,
|
|
5
5
|
} from 'expo/config-plugins';
|
|
6
6
|
import {ConfigPlugin, createRunOncePlugin} from 'expo/config-plugins';
|
|
7
7
|
|
|
8
8
|
const pkg = require('../../package.json');
|
|
9
9
|
|
|
10
|
-
type PaymentProvider = 'Amazon AppStore' | 'both' | 'Play Store';
|
|
11
|
-
|
|
12
|
-
const hasPaymentProviderProperValue = (
|
|
13
|
-
paymentProvider: string,
|
|
14
|
-
): paymentProvider is PaymentProvider => {
|
|
15
|
-
return ['Amazon AppStore', 'Play Store', 'both'].includes(paymentProvider);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const linesToAdd: {[key in PaymentProvider]: string} = {
|
|
19
|
-
['Amazon AppStore']: `missingDimensionStrategy "store", "amazon"`,
|
|
20
|
-
['Play Store']: `missingDimensionStrategy "store", "play"`,
|
|
21
|
-
['both']: `flavorDimensions "appstore"
|
|
22
|
-
|
|
23
|
-
productFlavors {
|
|
24
|
-
googlePlay {
|
|
25
|
-
dimension "appstore"
|
|
26
|
-
missingDimensionStrategy "store", "play"
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
amazon {
|
|
30
|
-
dimension "appstore"
|
|
31
|
-
missingDimensionStrategy "store", "amazon"
|
|
32
|
-
}
|
|
33
|
-
}`,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
10
|
const addToBuildGradle = (
|
|
37
11
|
newLine: string,
|
|
38
12
|
anchor: RegExp | string,
|
|
@@ -41,37 +15,13 @@ const addToBuildGradle = (
|
|
|
41
15
|
) => {
|
|
42
16
|
const lines = buildGradle.split('\n');
|
|
43
17
|
const lineIndex = lines.findIndex((line) => line.match(anchor));
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const modifyAppBuildGradle = (
|
|
50
|
-
buildGradle: string,
|
|
51
|
-
paymentProvider: PaymentProvider,
|
|
52
|
-
) => {
|
|
53
|
-
if (paymentProvider === 'both') {
|
|
54
|
-
if (buildGradle.includes(`flavorDimensions "appstore"`)) {
|
|
55
|
-
return buildGradle;
|
|
56
|
-
}
|
|
57
|
-
return addToBuildGradle(
|
|
58
|
-
linesToAdd[paymentProvider],
|
|
59
|
-
'defaultConfig',
|
|
60
|
-
-1,
|
|
61
|
-
buildGradle,
|
|
62
|
-
);
|
|
18
|
+
if (lineIndex === -1) {
|
|
19
|
+
console.warn('Anchor "ext" not found in build.gradle, appending to end');
|
|
20
|
+
lines.push(newLine);
|
|
21
|
+
} else {
|
|
22
|
+
lines.splice(lineIndex + offset, 0, newLine);
|
|
63
23
|
}
|
|
64
|
-
|
|
65
|
-
const missingDimensionStrategy = linesToAdd[paymentProvider];
|
|
66
|
-
if (buildGradle.includes(missingDimensionStrategy)) {
|
|
67
|
-
return buildGradle;
|
|
68
|
-
}
|
|
69
|
-
return addToBuildGradle(
|
|
70
|
-
missingDimensionStrategy,
|
|
71
|
-
'defaultConfig',
|
|
72
|
-
1,
|
|
73
|
-
buildGradle,
|
|
74
|
-
);
|
|
24
|
+
return lines.join('\n');
|
|
75
25
|
};
|
|
76
26
|
|
|
77
27
|
export const modifyProjectBuildGradle = (buildGradle: string) => {
|
|
@@ -82,54 +32,57 @@ export const modifyProjectBuildGradle = (buildGradle: string) => {
|
|
|
82
32
|
return addToBuildGradle(supportLibVersion, 'ext', 1, buildGradle);
|
|
83
33
|
};
|
|
84
34
|
|
|
85
|
-
const withIAPAndroid: ConfigPlugin
|
|
86
|
-
config,
|
|
87
|
-
|
|
88
|
-
) => {
|
|
89
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
90
|
-
config = withAppBuildGradle(config, (config) => {
|
|
91
|
-
config.modResults.contents = modifyAppBuildGradle(
|
|
35
|
+
const withIAPAndroid: ConfigPlugin = (config) => {
|
|
36
|
+
config = withProjectBuildGradle(config, (config) => {
|
|
37
|
+
config.modResults.contents = modifyProjectBuildGradle(
|
|
92
38
|
config.modResults.contents,
|
|
93
|
-
paymentProvider,
|
|
94
39
|
);
|
|
95
40
|
return config;
|
|
96
41
|
});
|
|
97
42
|
|
|
98
|
-
//
|
|
99
|
-
config =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
43
|
+
// Adding BILLING permission to AndroidManifest.xml
|
|
44
|
+
config = withAndroidManifest(config, (config) => {
|
|
45
|
+
console.log('Modifying AndroidManifest.xml...');
|
|
46
|
+
const manifest = config.modResults;
|
|
47
|
+
|
|
48
|
+
if (!manifest.manifest['uses-permission']) {
|
|
49
|
+
manifest.manifest['uses-permission'] = [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const permissions = manifest.manifest['uses-permission'];
|
|
53
|
+
const billingPermission = {
|
|
54
|
+
$: {'android:name': 'com.android.vending.BILLING'},
|
|
55
|
+
};
|
|
56
|
+
if (
|
|
57
|
+
!permissions.some(
|
|
58
|
+
(perm: any) => perm.$['android:name'] === 'com.android.vending.BILLING',
|
|
59
|
+
)
|
|
60
|
+
) {
|
|
61
|
+
permissions.push(billingPermission);
|
|
62
|
+
console.log('Added com.android.vending.BILLING to permissions');
|
|
63
|
+
} else {
|
|
64
|
+
console.log('com.android.vending.BILLING already exists in manifest');
|
|
65
|
+
}
|
|
66
|
+
|
|
103
67
|
return config;
|
|
104
68
|
});
|
|
69
|
+
|
|
105
70
|
return config;
|
|
106
71
|
};
|
|
107
72
|
|
|
108
|
-
interface Props {
|
|
109
|
-
paymentProvider?: PaymentProvider;
|
|
110
|
-
}
|
|
73
|
+
interface Props {}
|
|
111
74
|
|
|
112
75
|
const withIAP: ConfigPlugin<Props | undefined> = (config, props) => {
|
|
113
|
-
const paymentProvider = props?.paymentProvider ?? 'Play Store';
|
|
114
|
-
|
|
115
|
-
if (!hasPaymentProviderProperValue(paymentProvider)) {
|
|
116
|
-
WarningAggregator.addWarningAndroid(
|
|
117
|
-
'expo-iap',
|
|
118
|
-
|
|
119
|
-
`The payment provider '${paymentProvider}' is not supported. Please update your app.json file with one of the following supported values: 'Play Store', 'Amazon AppStore', or 'both'.`,
|
|
120
|
-
);
|
|
121
|
-
return config;
|
|
122
|
-
}
|
|
123
76
|
try {
|
|
124
|
-
|
|
77
|
+
console.log('Applying expo-iap plugin...');
|
|
78
|
+
config = withIAPAndroid(config);
|
|
125
79
|
} catch (error) {
|
|
126
80
|
WarningAggregator.addWarningAndroid(
|
|
127
81
|
'expo-iap',
|
|
128
|
-
|
|
129
82
|
`There was a problem configuring expo-iap in your native Android project: ${error}`,
|
|
130
83
|
);
|
|
84
|
+
console.error('Error in expo-iap plugin:', error);
|
|
131
85
|
}
|
|
132
|
-
|
|
133
86
|
return config;
|
|
134
87
|
};
|
|
135
88
|
|
package/src/ExpoIap.types.ts
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProductAndroid,
|
|
3
|
+
ProductPurchaseAndroid,
|
|
3
4
|
RequestPurchaseAndroidProps,
|
|
4
5
|
RequestSubscriptionAndroidProps,
|
|
5
6
|
SubscriptionProductAndroid,
|
|
6
7
|
} from './types/ExpoIapAndroid.types';
|
|
7
8
|
import {
|
|
8
9
|
ProductIos,
|
|
10
|
+
ProductPurchaseIos,
|
|
9
11
|
RequestPurchaseIosProps,
|
|
10
12
|
RequestSubscriptionIosProps,
|
|
11
13
|
SubscriptionProductIos,
|
|
12
14
|
} from './types/ExpoIapIos.types';
|
|
15
|
+
|
|
13
16
|
export type ChangeEventPayload = {
|
|
14
17
|
value: string;
|
|
15
18
|
};
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Base product type with common properties shared between iOS and Android
|
|
22
|
+
*/
|
|
23
|
+
export type ProductBase = {
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
description: string;
|
|
27
|
+
type: ProductType;
|
|
28
|
+
displayName?: string;
|
|
29
|
+
displayPrice?: string;
|
|
30
|
+
price?: number;
|
|
31
|
+
currency?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Define literal platform types for better type discrimination
|
|
35
|
+
export type IosPlatform = {platform: 'ios'};
|
|
36
|
+
export type AndroidPlatform = {platform: 'android'};
|
|
37
|
+
|
|
18
38
|
export enum ProductType {
|
|
19
39
|
InAppPurchase = 'inapp',
|
|
20
40
|
Subscription = 'subs',
|
|
21
41
|
}
|
|
22
42
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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;
|
|
43
|
+
// Common base purchase type
|
|
44
|
+
export type PurchaseBase = {
|
|
45
|
+
id: string;
|
|
39
46
|
transactionId?: string;
|
|
40
47
|
transactionDate: number;
|
|
41
48
|
transactionReceipt: string;
|
|
42
49
|
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
50
|
};
|
|
61
51
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
|
52
|
+
// Union type for platform-specific product types with proper discriminators
|
|
53
|
+
export type Product =
|
|
54
|
+
| (ProductAndroid & AndroidPlatform)
|
|
55
|
+
| (ProductIos & IosPlatform);
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
// Union type for platform-specific purchase types with proper discriminators
|
|
58
|
+
export type ProductPurchase =
|
|
59
|
+
| (ProductPurchaseAndroid & AndroidPlatform)
|
|
60
|
+
| (ProductPurchaseIos & IosPlatform);
|
|
70
61
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
verificationResultIOS?: string;
|
|
76
|
-
transactionReasonIOS?: TransactionReason | string;
|
|
77
|
-
} & ProductPurchase;
|
|
62
|
+
// Union type for platform-specific subscription purchase types with proper discriminators
|
|
63
|
+
export type SubscriptionPurchase =
|
|
64
|
+
| (ProductPurchaseAndroid & AndroidPlatform & { autoRenewingAndroid: boolean })
|
|
65
|
+
| (ProductPurchaseIos & IosPlatform);
|
|
78
66
|
|
|
79
67
|
export type Purchase = ProductPurchase | SubscriptionPurchase;
|
|
80
68
|
|
|
69
|
+
export type RequestPurchaseProps =
|
|
70
|
+
| RequestPurchaseIosProps
|
|
71
|
+
| RequestPurchaseAndroidProps;
|
|
72
|
+
|
|
73
|
+
export type SubscriptionProduct =
|
|
74
|
+
| (SubscriptionProductAndroid & AndroidPlatform)
|
|
75
|
+
| (SubscriptionProductIos & IosPlatform);
|
|
76
|
+
|
|
77
|
+
export type RequestSubscriptionProps =
|
|
78
|
+
| RequestSubscriptionAndroidProps
|
|
79
|
+
| RequestSubscriptionIosProps;
|
|
80
|
+
|
|
81
81
|
export type PurchaseResult = {
|
|
82
82
|
responseCode?: number;
|
|
83
83
|
debugMessage?: string;
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// and on native platforms to ExpoIap.ts
|
|
3
3
|
import {NativeModulesProxy, EventEmitter} from 'expo-modules-core';
|
|
4
4
|
import {Platform} from 'react-native';
|
|
5
|
-
|
|
6
5
|
import {
|
|
7
6
|
Product,
|
|
8
7
|
ProductPurchase,
|
|
@@ -21,13 +20,14 @@ import {
|
|
|
21
20
|
} from './types/ExpoIapAndroid.types';
|
|
22
21
|
import {
|
|
23
22
|
PaymentDiscount,
|
|
24
|
-
ProductIos,
|
|
25
23
|
RequestPurchaseIosProps,
|
|
26
24
|
RequestSubscriptionIosProps,
|
|
27
|
-
SubscriptionProductIos,
|
|
28
|
-
TransactionSk2,
|
|
29
25
|
} from './types/ExpoIapIos.types';
|
|
30
|
-
import {isProductIos} from './modules/ios';
|
|
26
|
+
import {isProductIos, isSubscriptionProductIos} from './modules/ios';
|
|
27
|
+
import {
|
|
28
|
+
isProductAndroid,
|
|
29
|
+
isSubscriptionProductAndroid,
|
|
30
|
+
} from './modules/android';
|
|
31
31
|
|
|
32
32
|
export * from './modules/android';
|
|
33
33
|
export * from './modules/ios';
|
|
@@ -70,22 +70,25 @@ export function initConnection() {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export const getProducts = async (skus: string[]): Promise<Product[]> => {
|
|
73
|
+
console.log('getProducts', skus);
|
|
73
74
|
if (!skus?.length) {
|
|
74
75
|
return Promise.reject(new Error('"skus" is required'));
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
return Platform.select({
|
|
78
79
|
ios: async () => {
|
|
79
|
-
const items =
|
|
80
|
-
|
|
80
|
+
const items = await ExpoIapModule.getItems(skus);
|
|
81
|
+
console.log('items', items);
|
|
82
|
+
return items.filter((item: unknown) => isProductIos<Product>(item));
|
|
81
83
|
},
|
|
82
84
|
android: async () => {
|
|
83
85
|
const products = await ExpoIapModule.getItemsByType(
|
|
84
86
|
ProductType.InAppPurchase,
|
|
85
87
|
skus,
|
|
86
88
|
);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
return products.filter((product: unknown) =>
|
|
90
|
+
isProductAndroid<Product>(product),
|
|
91
|
+
);
|
|
89
92
|
},
|
|
90
93
|
default: () => Promise.reject(new Error('Unsupported Platform')),
|
|
91
94
|
})();
|
|
@@ -99,15 +102,33 @@ export const getSubscriptions = async (
|
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
return Platform.select({
|
|
102
|
-
ios: async ()
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
ios: async () => {
|
|
106
|
+
const rawItems = await ExpoIapModule.getItems(skus);
|
|
107
|
+
|
|
108
|
+
return rawItems.filter((item: unknown) => {
|
|
109
|
+
if (!isSubscriptionProductIos(item)) return false;
|
|
110
|
+
return (
|
|
111
|
+
typeof item === 'object' &&
|
|
112
|
+
item !== null &&
|
|
113
|
+
'id' in item &&
|
|
114
|
+
typeof item.id === 'string' &&
|
|
115
|
+
skus.includes(item.id)
|
|
116
|
+
);
|
|
117
|
+
}) as SubscriptionProduct[];
|
|
108
118
|
},
|
|
109
119
|
android: async () => {
|
|
110
|
-
|
|
120
|
+
const rawItems = await ExpoIapModule.getItemsByType('subs', skus);
|
|
121
|
+
|
|
122
|
+
return rawItems.filter((item: unknown) => {
|
|
123
|
+
if (!isSubscriptionProductAndroid(item)) return false;
|
|
124
|
+
return (
|
|
125
|
+
typeof item === 'object' &&
|
|
126
|
+
item !== null &&
|
|
127
|
+
'id' in item &&
|
|
128
|
+
typeof item.id === 'string' &&
|
|
129
|
+
skus.includes(item.id)
|
|
130
|
+
);
|
|
131
|
+
}) as SubscriptionProduct[];
|
|
111
132
|
},
|
|
112
133
|
default: () => Promise.reject(new Error('Unsupported Platform')),
|
|
113
134
|
})();
|
|
@@ -119,11 +140,9 @@ export async function endConnection(): Promise<boolean> {
|
|
|
119
140
|
|
|
120
141
|
export const getPurchaseHistory = ({
|
|
121
142
|
alsoPublishToEventListener = false,
|
|
122
|
-
automaticallyFinishRestoredTransactions = true,
|
|
123
143
|
onlyIncludeActiveItems = false,
|
|
124
144
|
}: {
|
|
125
145
|
alsoPublishToEventListener?: boolean;
|
|
126
|
-
automaticallyFinishRestoredTransactions?: boolean;
|
|
127
146
|
onlyIncludeActiveItems?: boolean;
|
|
128
147
|
} = {}): Promise<ProductPurchase[]> =>
|
|
129
148
|
(
|
|
@@ -150,11 +169,9 @@ export const getPurchaseHistory = ({
|
|
|
150
169
|
|
|
151
170
|
export const getAvailablePurchases = ({
|
|
152
171
|
alsoPublishToEventListener = false,
|
|
153
|
-
automaticallyFinishRestoredTransactions = false,
|
|
154
172
|
onlyIncludeActiveItems = true,
|
|
155
173
|
}: {
|
|
156
174
|
alsoPublishToEventListener?: boolean;
|
|
157
|
-
automaticallyFinishRestoredTransactions?: boolean;
|
|
158
175
|
onlyIncludeActiveItems?: boolean;
|
|
159
176
|
} = {}): Promise<ProductPurchase[]> =>
|
|
160
177
|
(
|
|
@@ -193,43 +210,6 @@ const offerToRecordIos = (
|
|
|
193
210
|
};
|
|
194
211
|
};
|
|
195
212
|
|
|
196
|
-
const iosTransactionToPurchaseMap = ({
|
|
197
|
-
id,
|
|
198
|
-
originalPurchaseDate,
|
|
199
|
-
productID,
|
|
200
|
-
purchaseDate,
|
|
201
|
-
purchasedQuantity,
|
|
202
|
-
originalID,
|
|
203
|
-
verificationResult,
|
|
204
|
-
appAccountToken,
|
|
205
|
-
jsonRepresentation,
|
|
206
|
-
}: TransactionSk2): Purchase => {
|
|
207
|
-
let transactionReasonIOS;
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
if (jsonRepresentation) {
|
|
211
|
-
const transactionData = JSON.parse(jsonRepresentation);
|
|
212
|
-
transactionReasonIOS = transactionData.transactionReason;
|
|
213
|
-
}
|
|
214
|
-
} catch (e) {
|
|
215
|
-
console.log('SK2 Error parsing jsonRepresentation', e);
|
|
216
|
-
}
|
|
217
|
-
const purchase: Purchase = {
|
|
218
|
-
productId: productID,
|
|
219
|
-
transactionId: String(id),
|
|
220
|
-
transactionDate: purchaseDate, //??
|
|
221
|
-
transactionReceipt: '', // Not available
|
|
222
|
-
purchaseToken: '', //Not available
|
|
223
|
-
quantityIOS: purchasedQuantity,
|
|
224
|
-
originalTransactionDateIOS: originalPurchaseDate,
|
|
225
|
-
originalTransactionIdentifierIOS: originalID,
|
|
226
|
-
verificationResultIOS: verificationResult ?? '',
|
|
227
|
-
appAccountToken: appAccountToken ?? '',
|
|
228
|
-
transactionReasonIOS: transactionReasonIOS ?? '',
|
|
229
|
-
};
|
|
230
|
-
return purchase;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
213
|
export const requestPurchase = (
|
|
234
214
|
request: RequestPurchaseIosProps | RequestPurchaseAndroidProps,
|
|
235
215
|
): Promise<ProductPurchase | ProductPurchase[] | void> =>
|
|
@@ -240,31 +220,17 @@ export const requestPurchase = (
|
|
|
240
220
|
throw new Error('sku is required for iOS purchase');
|
|
241
221
|
}
|
|
242
222
|
|
|
243
|
-
const {
|
|
244
|
-
sku,
|
|
245
|
-
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
246
|
-
appAccountToken,
|
|
247
|
-
quantity,
|
|
248
|
-
withOffer,
|
|
249
|
-
} = request;
|
|
250
|
-
|
|
251
|
-
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
252
|
-
console.warn(
|
|
253
|
-
'You are dangerously allowing expo-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.',
|
|
254
|
-
);
|
|
255
|
-
}
|
|
223
|
+
const {sku, appAccountToken, quantity, withOffer} = request;
|
|
256
224
|
|
|
257
225
|
const offer = offerToRecordIos(withOffer);
|
|
258
226
|
|
|
259
|
-
const
|
|
227
|
+
const purchase = await ExpoIapModule.buyProduct(
|
|
260
228
|
sku,
|
|
261
|
-
andDangerouslyFinishTransactionAutomaticallyIOS,
|
|
262
229
|
appAccountToken,
|
|
263
230
|
quantity ?? -1,
|
|
264
231
|
offer,
|
|
265
232
|
);
|
|
266
233
|
|
|
267
|
-
const purchase = iosTransactionToPurchaseMap(result);
|
|
268
234
|
return Promise.resolve(purchase);
|
|
269
235
|
},
|
|
270
236
|
android: async () => {
|
|
@@ -303,35 +269,21 @@ export const requestSubscription = (
|
|
|
303
269
|
throw new Error('sku is required for iOS subscriptions');
|
|
304
270
|
}
|
|
305
271
|
|
|
306
|
-
const {
|
|
307
|
-
|
|
308
|
-
andDangerouslyFinishTransactionAutomaticallyIOS = false,
|
|
309
|
-
appAccountToken,
|
|
310
|
-
quantity,
|
|
311
|
-
withOffer,
|
|
312
|
-
} = request as RequestSubscriptionIosProps;
|
|
313
|
-
|
|
314
|
-
if (andDangerouslyFinishTransactionAutomaticallyIOS) {
|
|
315
|
-
console.warn(
|
|
316
|
-
'You are dangerously allowing expo-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.',
|
|
317
|
-
);
|
|
318
|
-
}
|
|
272
|
+
const {sku, appAccountToken, quantity, withOffer} =
|
|
273
|
+
request as RequestSubscriptionIosProps;
|
|
319
274
|
|
|
320
275
|
const offer = offerToRecordIos(withOffer);
|
|
321
276
|
|
|
322
|
-
const purchase =
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
quantity ?? -1,
|
|
328
|
-
offer,
|
|
329
|
-
),
|
|
277
|
+
const purchase = await ExpoIapModule.buyProduct(
|
|
278
|
+
sku,
|
|
279
|
+
appAccountToken,
|
|
280
|
+
quantity ?? -1,
|
|
281
|
+
offer,
|
|
330
282
|
);
|
|
331
|
-
|
|
283
|
+
|
|
284
|
+
return Promise.resolve(purchase as SubscriptionPurchase);
|
|
332
285
|
},
|
|
333
286
|
android: async () => {
|
|
334
|
-
console.log('requestSubscription', request);
|
|
335
287
|
const {
|
|
336
288
|
skus,
|
|
337
289
|
isOfferPersonalized,
|
|
@@ -398,3 +350,5 @@ export const finishTransaction = ({
|
|
|
398
350
|
}) || (() => Promise.reject(new Error('Unsupported Platform')))
|
|
399
351
|
)();
|
|
400
352
|
};
|
|
353
|
+
|
|
354
|
+
export * from './useIap';
|
package/src/modules/android.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import {Linking} from 'react-native';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
ProductAndroid,
|
|
5
|
-
ReceiptAndroid,
|
|
6
|
-
SubscriptionProductAndroid,
|
|
7
|
-
} from '../types/ExpoIapAndroid.types';
|
|
2
|
+
import {PurchaseResult} from '../ExpoIap.types';
|
|
3
|
+
import {ReceiptAndroid} from '../types/ExpoIapAndroid.types';
|
|
8
4
|
import ExpoIapModule from '../ExpoIapModule';
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
// Type guards
|
|
7
|
+
export function isProductAndroid<T extends {platform?: string}>(
|
|
8
|
+
item: unknown,
|
|
9
|
+
): item is T & {platform: 'android'} {
|
|
10
|
+
return (
|
|
11
|
+
item != null &&
|
|
12
|
+
typeof item === 'object' &&
|
|
13
|
+
'platform' in item &&
|
|
14
|
+
item.platform === 'android'
|
|
15
|
+
);
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
export function isSubscriptionProductAndroid(
|
|
15
|
-
|
|
16
|
-
):
|
|
17
|
-
return (
|
|
18
|
+
export function isSubscriptionProductAndroid<T extends {platform?: string}>(
|
|
19
|
+
item: unknown,
|
|
20
|
+
): item is T & {platform: 'android'} {
|
|
21
|
+
return isProductAndroid(item);
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
/**
|
package/src/modules/ios.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import {Platform} from 'react-native';
|
|
2
2
|
import {emitter, IapEvent} from '..';
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
ProductIos,
|
|
6
|
-
ProductStatusIos,
|
|
7
|
-
SubscriptionProductIos,
|
|
8
|
-
TransactionSk2,
|
|
9
|
-
} from '../types/ExpoIapIos.types';
|
|
3
|
+
import {ProductPurchase, PurchaseError} from '../ExpoIap.types';
|
|
4
|
+
import type {ProductStatusIos} from '../types/ExpoIapIos.types';
|
|
10
5
|
import ExpoIapModule from '../ExpoIapModule';
|
|
11
6
|
|
|
12
|
-
type TransactionEvent = {
|
|
13
|
-
transaction?:
|
|
7
|
+
export type TransactionEvent = {
|
|
8
|
+
transaction?: ProductPurchase;
|
|
14
9
|
error?: PurchaseError;
|
|
15
10
|
};
|
|
16
11
|
|
|
@@ -25,17 +20,25 @@ export const transactionUpdatedIos = (
|
|
|
25
20
|
return emitter.addListener(IapEvent.TransactionIapUpdated, listener);
|
|
26
21
|
};
|
|
27
22
|
|
|
28
|
-
//
|
|
29
|
-
export function isProductIos
|
|
30
|
-
|
|
23
|
+
// Type guards
|
|
24
|
+
export function isProductIos<T extends {platform?: string}>(
|
|
25
|
+
item: unknown,
|
|
26
|
+
): item is T & {platform: 'ios'} {
|
|
27
|
+
return (
|
|
28
|
+
item != null &&
|
|
29
|
+
typeof item === 'object' &&
|
|
30
|
+
'platform' in item &&
|
|
31
|
+
item.platform === 'ios'
|
|
32
|
+
);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
export function isSubscriptionProductIos(
|
|
34
|
-
|
|
35
|
-
):
|
|
36
|
-
return (
|
|
35
|
+
export function isSubscriptionProductIos<T extends {platform?: string}>(
|
|
36
|
+
item: unknown,
|
|
37
|
+
): item is T & {platform: 'ios'} {
|
|
38
|
+
return isProductIos(item);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// Functions
|
|
39
42
|
/**
|
|
40
43
|
* Sync state with Appstore (iOS only)
|
|
41
44
|
* https://developer.apple.com/documentation/storekit/appstore/3791906-sync
|
|
@@ -58,13 +61,13 @@ export const subscriptionStatus = (sku: string): Promise<ProductStatusIos[]> =>
|
|
|
58
61
|
/**
|
|
59
62
|
*
|
|
60
63
|
*/
|
|
61
|
-
export const currentEntitlement = (sku: string): Promise<
|
|
64
|
+
export const currentEntitlement = (sku: string): Promise<ProductPurchase> =>
|
|
62
65
|
ExpoIapModule.currentEntitlement(sku);
|
|
63
66
|
|
|
64
67
|
/**
|
|
65
68
|
*
|
|
66
69
|
*/
|
|
67
|
-
export const latestTransaction = (sku: string): Promise<
|
|
70
|
+
export const latestTransaction = (sku: string): Promise<ProductPurchase> =>
|
|
68
71
|
ExpoIapModule.latestTransaction(sku);
|
|
69
72
|
|
|
70
73
|
/**
|