expo-iap 2.8.1 → 2.8.3-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/CHANGELOG.md +15 -6
- package/CLAUDE.md +0 -5
- package/CONTRIBUTING.md +3 -4
- package/build/ExpoIap.types.d.ts +13 -9
- package/build/ExpoIap.types.d.ts.map +1 -1
- package/build/ExpoIap.types.js.map +1 -1
- package/build/helpers/subscription.d.ts.map +1 -1
- package/build/helpers/subscription.js +3 -6
- package/build/helpers/subscription.js.map +1 -1
- package/build/index.d.ts +6 -6
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +8 -8
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +3 -3
- package/build/modules/ios.js.map +1 -1
- package/build/types/ExpoIapAndroid.types.d.ts +2 -2
- package/build/types/ExpoIapAndroid.types.d.ts.map +1 -1
- package/build/types/ExpoIapAndroid.types.js.map +1 -1
- package/build/types/ExpoIapIOS.types.d.ts +3 -3
- package/build/types/ExpoIapIOS.types.d.ts.map +1 -1
- package/build/types/ExpoIapIOS.types.js.map +1 -1
- package/build/useIAP.d.ts +6 -6
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js.map +1 -1
- package/bun.lock +122 -456
- package/coverage/clover.xml +601 -0
- package/coverage/coverage-final.json +9 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +176 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/ExpoIap.types.ts.html +1243 -0
- package/coverage/lcov-report/src/helpers/index.html +116 -0
- package/coverage/lcov-report/src/helpers/subscription.ts.html +430 -0
- package/coverage/lcov-report/src/index.html +146 -0
- package/coverage/lcov-report/src/index.ts.html +2227 -0
- package/coverage/lcov-report/src/modules/android.ts.html +469 -0
- package/coverage/lcov-report/src/modules/index.html +131 -0
- package/coverage/lcov-report/src/modules/ios.ts.html +1411 -0
- package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +487 -0
- package/coverage/lcov-report/src/types/index.html +116 -0
- package/coverage/lcov-report/src/useIap.ts.html +1483 -0
- package/coverage/lcov-report/src/utils/errorMapping.ts.html +349 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +1115 -0
- package/ios/expoiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/expoiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/jest.config.js +14 -17
- package/package.json +1 -1
- package/plugin/build/withIAP.js +26 -3
- package/plugin/src/withIAP.ts +35 -3
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/ExpoIap.types.ts +15 -16
- package/src/helpers/subscription.ts +21 -28
- package/src/index.ts +16 -21
- package/src/modules/android.ts +7 -7
- package/src/modules/ios.ts +13 -15
- package/src/types/ExpoIapAndroid.types.ts +3 -4
- package/src/types/ExpoIapIOS.types.ts +4 -3
- package/src/useIAP.ts +11 -13
package/jest.config.js
CHANGED
|
@@ -4,21 +4,18 @@ module.exports = {
|
|
|
4
4
|
roots: ['<rootDir>/src'],
|
|
5
5
|
testMatch: [
|
|
6
6
|
'**/__tests__/**/*.+(ts|tsx|js)',
|
|
7
|
-
'**/?(*.)+(spec|test).+(ts|tsx|js)'
|
|
7
|
+
'**/?(*.)+(spec|test).+(ts|tsx|js)'
|
|
8
8
|
],
|
|
9
9
|
transform: {
|
|
10
|
-
'^.+\\.(ts|tsx)$': [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
],
|
|
10
|
+
'^.+\\.(ts|tsx)$': ['ts-jest', {
|
|
11
|
+
tsconfig: {
|
|
12
|
+
jsx: 'react',
|
|
13
|
+
esModuleInterop: true,
|
|
14
|
+
allowSyntheticDefaultImports: true,
|
|
15
|
+
moduleResolution: 'node',
|
|
16
|
+
skipLibCheck: true,
|
|
17
|
+
}
|
|
18
|
+
}]
|
|
22
19
|
},
|
|
23
20
|
moduleNameMapper: {
|
|
24
21
|
'^react-native$': '<rootDir>/src/__mocks__/react-native.js',
|
|
@@ -37,7 +34,7 @@ module.exports = {
|
|
|
37
34
|
branches: 15,
|
|
38
35
|
functions: 15,
|
|
39
36
|
lines: 15,
|
|
40
|
-
statements: 15
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
37
|
+
statements: 15
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
package/package.json
CHANGED
package/plugin/build/withIAP.js
CHANGED
|
@@ -36,7 +36,28 @@ const modifyAppBuildGradle = (gradle) => {
|
|
|
36
36
|
}
|
|
37
37
|
return modified;
|
|
38
38
|
};
|
|
39
|
-
const
|
|
39
|
+
const withIapIOS = (config) => {
|
|
40
|
+
// Add In-App Purchase capability to entitlements
|
|
41
|
+
config = (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
|
|
42
|
+
config.modResults['com.apple.developer.in-app-payments'] = ['Default'];
|
|
43
|
+
if (!hasLoggedPluginExecution) {
|
|
44
|
+
console.log('✅ Added In-App Purchase capability to iOS entitlements');
|
|
45
|
+
}
|
|
46
|
+
return config;
|
|
47
|
+
});
|
|
48
|
+
// Add StoreKit configuration for development (optional)
|
|
49
|
+
config = (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
50
|
+
// This is optional - adds StoreKit configuration for testing
|
|
51
|
+
// Users can override this with their own configuration file
|
|
52
|
+
if (!config.modResults.SKStoreProductParameterITunesItemIdentifier) {
|
|
53
|
+
// This is just a placeholder, users should set their actual app ID
|
|
54
|
+
config.modResults.SKStoreProductParameterITunesItemIdentifier = '';
|
|
55
|
+
}
|
|
56
|
+
return config;
|
|
57
|
+
});
|
|
58
|
+
return config;
|
|
59
|
+
};
|
|
60
|
+
const withIapAndroid = (config) => {
|
|
40
61
|
// Add IAP dependencies to app build.gradle
|
|
41
62
|
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
42
63
|
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
|
|
@@ -67,10 +88,12 @@ const withIAPAndroid = (config) => {
|
|
|
67
88
|
};
|
|
68
89
|
const withIAP = (config, _props) => {
|
|
69
90
|
try {
|
|
70
|
-
|
|
91
|
+
// Apply both iOS and Android configurations
|
|
92
|
+
config = withIapIOS(config);
|
|
93
|
+
config = withIapAndroid(config);
|
|
71
94
|
// Set flag after first execution to prevent duplicate logs
|
|
72
95
|
hasLoggedPluginExecution = true;
|
|
73
|
-
return
|
|
96
|
+
return config;
|
|
74
97
|
}
|
|
75
98
|
catch (error) {
|
|
76
99
|
config_plugins_1.WarningAggregator.addWarningAndroid('expo-iap', `expo-iap plugin encountered an error: ${error}`);
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
WarningAggregator,
|
|
5
5
|
withAndroidManifest,
|
|
6
6
|
withAppBuildGradle,
|
|
7
|
+
withEntitlementsPlist,
|
|
8
|
+
withInfoPlist,
|
|
7
9
|
} from 'expo/config-plugins';
|
|
8
10
|
|
|
9
11
|
const pkg = require('../../package.json');
|
|
@@ -56,7 +58,34 @@ const modifyAppBuildGradle = (gradle: string): string => {
|
|
|
56
58
|
return modified;
|
|
57
59
|
};
|
|
58
60
|
|
|
59
|
-
const
|
|
61
|
+
const withIapIOS: ConfigPlugin = (config) => {
|
|
62
|
+
// Add In-App Purchase capability to entitlements
|
|
63
|
+
config = withEntitlementsPlist(config, (config) => {
|
|
64
|
+
config.modResults['com.apple.developer.in-app-payments'] = ['Default'];
|
|
65
|
+
|
|
66
|
+
if (!hasLoggedPluginExecution) {
|
|
67
|
+
console.log('✅ Added In-App Purchase capability to iOS entitlements');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return config;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Add StoreKit configuration for development (optional)
|
|
74
|
+
config = withInfoPlist(config, (config) => {
|
|
75
|
+
// This is optional - adds StoreKit configuration for testing
|
|
76
|
+
// Users can override this with their own configuration file
|
|
77
|
+
if (!config.modResults.SKStoreProductParameterITunesItemIdentifier) {
|
|
78
|
+
// This is just a placeholder, users should set their actual app ID
|
|
79
|
+
config.modResults.SKStoreProductParameterITunesItemIdentifier = '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return config;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return config;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const withIapAndroid: ConfigPlugin = (config) => {
|
|
60
89
|
// Add IAP dependencies to app build.gradle
|
|
61
90
|
config = withAppBuildGradle(config, (config) => {
|
|
62
91
|
config.modResults.contents = modifyAppBuildGradle(
|
|
@@ -100,10 +129,13 @@ const withIAPAndroid: ConfigPlugin = (config) => {
|
|
|
100
129
|
|
|
101
130
|
const withIAP: ConfigPlugin = (config, _props) => {
|
|
102
131
|
try {
|
|
103
|
-
|
|
132
|
+
// Apply both iOS and Android configurations
|
|
133
|
+
config = withIapIOS(config);
|
|
134
|
+
config = withIapAndroid(config);
|
|
135
|
+
|
|
104
136
|
// Set flag after first execution to prevent duplicate logs
|
|
105
137
|
hasLoggedPluginExecution = true;
|
|
106
|
-
return
|
|
138
|
+
return config;
|
|
107
139
|
} catch (error) {
|
|
108
140
|
WarningAggregator.addWarningAndroid(
|
|
109
141
|
'expo-iap',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/
|
|
1
|
+
{"root":["./src/withiap.ts"],"version":"5.8.3"}
|
package/src/ExpoIap.types.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProductAndroid,
|
|
3
|
-
|
|
3
|
+
PurchaseAndroid,
|
|
4
4
|
ProductSubscriptionAndroid,
|
|
5
5
|
} from './types/ExpoIapAndroid.types';
|
|
6
6
|
import {
|
|
7
7
|
ProductIOS,
|
|
8
|
-
|
|
8
|
+
PurchaseIOS,
|
|
9
9
|
ProductSubscriptionIOS,
|
|
10
10
|
} from './types/ExpoIapIOS.types';
|
|
11
11
|
import {NATIVE_ERROR_CODES} from './ExpoIapModule';
|
|
@@ -63,29 +63,28 @@ export type SubscriptionProduct =
|
|
|
63
63
|
|
|
64
64
|
// Re-export platform-specific types
|
|
65
65
|
export type {
|
|
66
|
-
ProductPurchaseAndroid,
|
|
67
66
|
PurchaseAndroid,
|
|
68
67
|
ProductSubscriptionAndroid,
|
|
69
|
-
SubscriptionProductAndroid, // Legacy
|
|
70
68
|
} from './types/ExpoIapAndroid.types';
|
|
71
69
|
export type {
|
|
72
|
-
ProductPurchaseIOS,
|
|
73
70
|
PurchaseIOS,
|
|
74
71
|
ProductSubscriptionIOS,
|
|
75
|
-
SubscriptionProductIOS, // Legacy
|
|
76
72
|
} from './types/ExpoIapIOS.types';
|
|
77
73
|
|
|
78
|
-
//
|
|
79
|
-
export type
|
|
80
|
-
| (
|
|
81
|
-
| (
|
|
74
|
+
// Unified purchase type for both products and subscriptions
|
|
75
|
+
export type Purchase =
|
|
76
|
+
| (PurchaseAndroid & AndroidPlatform)
|
|
77
|
+
| (PurchaseIOS & IosPlatform);
|
|
82
78
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
// Legacy type aliases - deprecated, use Purchase instead
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated Use `Purchase` instead. This type alias will be removed in v2.9.0.
|
|
82
|
+
*/
|
|
83
|
+
export type ProductPurchase = Purchase;
|
|
84
|
+
/**
|
|
85
|
+
* @deprecated Use `Purchase` instead. This type alias will be removed in v2.9.0.
|
|
86
|
+
*/
|
|
87
|
+
export type SubscriptionPurchase = Purchase;
|
|
89
88
|
|
|
90
89
|
export type PurchaseResult = {
|
|
91
90
|
responseCode?: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {Platform} from 'react-native';
|
|
2
|
-
import {getAvailablePurchases} from '../index';
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { getAvailablePurchases } from '../index';
|
|
3
3
|
|
|
4
4
|
export interface ActiveSubscription {
|
|
5
5
|
productId: string;
|
|
@@ -17,13 +17,13 @@ export interface ActiveSubscription {
|
|
|
17
17
|
* @returns Promise<ActiveSubscription[]> array of active subscriptions with details
|
|
18
18
|
*/
|
|
19
19
|
export const getActiveSubscriptions = async (
|
|
20
|
-
subscriptionIds?: string[]
|
|
20
|
+
subscriptionIds?: string[]
|
|
21
21
|
): Promise<ActiveSubscription[]> => {
|
|
22
22
|
try {
|
|
23
23
|
const purchases = await getAvailablePurchases();
|
|
24
24
|
const currentTime = Date.now();
|
|
25
25
|
const activeSubscriptions: ActiveSubscription[] = [];
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
// Filter purchases to find active subscriptions
|
|
28
28
|
const filteredPurchases = purchases.filter((purchase) => {
|
|
29
29
|
// If specific IDs provided, filter by them
|
|
@@ -32,17 +32,17 @@ export const getActiveSubscriptions = async (
|
|
|
32
32
|
return false;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
// Check if this purchase has subscription-specific fields
|
|
37
|
-
const hasSubscriptionFields =
|
|
37
|
+
const hasSubscriptionFields =
|
|
38
38
|
('expirationDateIOS' in purchase && purchase.expirationDateIOS) ||
|
|
39
|
-
'autoRenewingAndroid' in purchase ||
|
|
39
|
+
('autoRenewingAndroid' in purchase) ||
|
|
40
40
|
('environmentIOS' in purchase && purchase.environmentIOS === 'Sandbox');
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
if (!hasSubscriptionFields) {
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Check if it's actually active
|
|
47
47
|
if (Platform.OS === 'ios') {
|
|
48
48
|
if ('expirationDateIOS' in purchase && purchase.expirationDateIOS) {
|
|
@@ -53,15 +53,8 @@ export const getActiveSubscriptions = async (
|
|
|
53
53
|
if ('environmentIOS' in purchase && purchase.environmentIOS) {
|
|
54
54
|
const dayInMs = 24 * 60 * 60 * 1000;
|
|
55
55
|
// If no expiration date, consider active if transaction is recent (within 24 hours for Sandbox)
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
!purchase.expirationDateIOS
|
|
59
|
-
) {
|
|
60
|
-
if (
|
|
61
|
-
purchase.environmentIOS === 'Sandbox' &&
|
|
62
|
-
purchase.transactionDate &&
|
|
63
|
-
currentTime - purchase.transactionDate < dayInMs
|
|
64
|
-
) {
|
|
56
|
+
if (!('expirationDateIOS' in purchase) || !purchase.expirationDateIOS) {
|
|
57
|
+
if (purchase.environmentIOS === 'Sandbox' && purchase.transactionDate && (currentTime - purchase.transactionDate) < dayInMs) {
|
|
65
58
|
return true;
|
|
66
59
|
}
|
|
67
60
|
}
|
|
@@ -70,31 +63,31 @@ export const getActiveSubscriptions = async (
|
|
|
70
63
|
// For Android, if it's in the purchases list, it's active
|
|
71
64
|
return true;
|
|
72
65
|
}
|
|
73
|
-
|
|
66
|
+
|
|
74
67
|
return false;
|
|
75
68
|
});
|
|
76
|
-
|
|
69
|
+
|
|
77
70
|
// Convert to ActiveSubscription format
|
|
78
71
|
for (const purchase of filteredPurchases) {
|
|
79
72
|
const subscription: ActiveSubscription = {
|
|
80
73
|
productId: purchase.productId,
|
|
81
74
|
isActive: true,
|
|
82
75
|
};
|
|
83
|
-
|
|
76
|
+
|
|
84
77
|
// Add platform-specific details
|
|
85
78
|
if (Platform.OS === 'ios') {
|
|
86
79
|
if ('expirationDateIOS' in purchase && purchase.expirationDateIOS) {
|
|
87
80
|
const expirationDate = new Date(purchase.expirationDateIOS);
|
|
88
81
|
subscription.expirationDateIOS = expirationDate;
|
|
89
|
-
|
|
82
|
+
|
|
90
83
|
// Calculate days until expiration (round to nearest day)
|
|
91
84
|
const daysUntilExpiration = Math.round(
|
|
92
|
-
(purchase.expirationDateIOS - currentTime) / (1000 * 60 * 60 * 24)
|
|
85
|
+
(purchase.expirationDateIOS - currentTime) / (1000 * 60 * 60 * 24)
|
|
93
86
|
);
|
|
94
87
|
subscription.daysUntilExpirationIOS = daysUntilExpiration;
|
|
95
88
|
subscription.willExpireSoon = daysUntilExpiration <= 7;
|
|
96
89
|
}
|
|
97
|
-
|
|
90
|
+
|
|
98
91
|
if ('environmentIOS' in purchase) {
|
|
99
92
|
subscription.environmentIOS = purchase.environmentIOS;
|
|
100
93
|
}
|
|
@@ -105,10 +98,10 @@ export const getActiveSubscriptions = async (
|
|
|
105
98
|
subscription.willExpireSoon = !purchase.autoRenewingAndroid;
|
|
106
99
|
}
|
|
107
100
|
}
|
|
108
|
-
|
|
101
|
+
|
|
109
102
|
activeSubscriptions.push(subscription);
|
|
110
103
|
}
|
|
111
|
-
|
|
104
|
+
|
|
112
105
|
return activeSubscriptions;
|
|
113
106
|
} catch (error) {
|
|
114
107
|
console.error('Error getting active subscriptions:', error);
|
|
@@ -122,8 +115,8 @@ export const getActiveSubscriptions = async (
|
|
|
122
115
|
* @returns Promise<boolean> true if user has at least one active subscription
|
|
123
116
|
*/
|
|
124
117
|
export const hasActiveSubscriptions = async (
|
|
125
|
-
subscriptionIds?: string[]
|
|
118
|
+
subscriptionIds?: string[]
|
|
126
119
|
): Promise<boolean> => {
|
|
127
120
|
const subscriptions = await getActiveSubscriptions(subscriptionIds);
|
|
128
121
|
return subscriptions.length > 0;
|
|
129
|
-
};
|
|
122
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -18,16 +18,14 @@ import {
|
|
|
18
18
|
// Types
|
|
19
19
|
import {
|
|
20
20
|
Product,
|
|
21
|
-
ProductPurchase,
|
|
22
21
|
Purchase,
|
|
23
22
|
PurchaseError,
|
|
24
23
|
PurchaseResult,
|
|
25
24
|
RequestSubscriptionProps,
|
|
26
25
|
RequestPurchaseProps,
|
|
27
26
|
SubscriptionProduct,
|
|
28
|
-
SubscriptionPurchase,
|
|
29
27
|
} from './ExpoIap.types';
|
|
30
|
-
import {
|
|
28
|
+
import {PurchaseAndroid} from './types/ExpoIapAndroid.types';
|
|
31
29
|
import {PaymentDiscount} from './types/ExpoIapIOS.types';
|
|
32
30
|
|
|
33
31
|
// Export all types
|
|
@@ -281,7 +279,7 @@ export const getPurchaseHistory = ({
|
|
|
281
279
|
}: {
|
|
282
280
|
alsoPublishToEventListener?: boolean;
|
|
283
281
|
onlyIncludeActiveItems?: boolean;
|
|
284
|
-
} = {}): Promise<
|
|
282
|
+
} = {}): Promise<Purchase[]> => {
|
|
285
283
|
console.warn(
|
|
286
284
|
'`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.',
|
|
287
285
|
);
|
|
@@ -297,7 +295,7 @@ export const getPurchaseHistories = ({
|
|
|
297
295
|
}: {
|
|
298
296
|
alsoPublishToEventListener?: boolean;
|
|
299
297
|
onlyIncludeActiveItems?: boolean;
|
|
300
|
-
} = {}): Promise<
|
|
298
|
+
} = {}): Promise<Purchase[]> =>
|
|
301
299
|
(
|
|
302
300
|
Platform.select({
|
|
303
301
|
ios: async () => {
|
|
@@ -323,7 +321,7 @@ export const getAvailablePurchases = ({
|
|
|
323
321
|
}: {
|
|
324
322
|
alsoPublishToEventListener?: boolean;
|
|
325
323
|
onlyIncludeActiveItems?: boolean;
|
|
326
|
-
} = {}): Promise<
|
|
324
|
+
} = {}): Promise<Purchase[]> =>
|
|
327
325
|
(
|
|
328
326
|
Platform.select({
|
|
329
327
|
ios: () =>
|
|
@@ -333,9 +331,8 @@ export const getAvailablePurchases = ({
|
|
|
333
331
|
),
|
|
334
332
|
android: async () => {
|
|
335
333
|
const products = await ExpoIapModule.getAvailableItemsByType('inapp');
|
|
336
|
-
const subscriptions =
|
|
337
|
-
'subs'
|
|
338
|
-
);
|
|
334
|
+
const subscriptions =
|
|
335
|
+
await ExpoIapModule.getAvailableItemsByType('subs');
|
|
339
336
|
return products.concat(subscriptions);
|
|
340
337
|
},
|
|
341
338
|
}) || (() => Promise.resolve([]))
|
|
@@ -410,10 +407,8 @@ const normalizeRequestProps = (
|
|
|
410
407
|
export const requestPurchase = (
|
|
411
408
|
requestObj: PurchaseRequest,
|
|
412
409
|
): Promise<
|
|
413
|
-
|
|
|
414
|
-
|
|
|
415
|
-
| ProductPurchase[]
|
|
416
|
-
| SubscriptionPurchase[]
|
|
410
|
+
| Purchase
|
|
411
|
+
| Purchase[]
|
|
417
412
|
| void
|
|
418
413
|
> => {
|
|
419
414
|
const {request, type = 'inapp'} = requestObj;
|
|
@@ -446,8 +441,8 @@ export const requestPurchase = (
|
|
|
446
441
|
);
|
|
447
442
|
|
|
448
443
|
return type === 'inapp'
|
|
449
|
-
? (purchase as
|
|
450
|
-
: (purchase as
|
|
444
|
+
? (purchase as Purchase)
|
|
445
|
+
: (purchase as Purchase);
|
|
451
446
|
})();
|
|
452
447
|
}
|
|
453
448
|
|
|
@@ -478,7 +473,7 @@ export const requestPurchase = (
|
|
|
478
473
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
479
474
|
offerTokenArr: [],
|
|
480
475
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
481
|
-
}) as Promise<
|
|
476
|
+
}) as Promise<Purchase[]>;
|
|
482
477
|
})();
|
|
483
478
|
}
|
|
484
479
|
|
|
@@ -504,7 +499,7 @@ export const requestPurchase = (
|
|
|
504
499
|
obfuscatedProfileId: obfuscatedProfileIdAndroid,
|
|
505
500
|
offerTokenArr: subscriptionOffers.map((so: any) => so.offerToken),
|
|
506
501
|
isOfferPersonalized: isOfferPersonalized ?? false,
|
|
507
|
-
}) as Promise<
|
|
502
|
+
}) as Promise<Purchase[]>;
|
|
508
503
|
})();
|
|
509
504
|
}
|
|
510
505
|
|
|
@@ -543,13 +538,13 @@ export const requestPurchase = (
|
|
|
543
538
|
*/
|
|
544
539
|
export const requestSubscription = async (
|
|
545
540
|
request: RequestSubscriptionProps,
|
|
546
|
-
): Promise<
|
|
541
|
+
): Promise<Purchase | Purchase[] | null | void> => {
|
|
547
542
|
console.warn(
|
|
548
543
|
"`requestSubscription` is deprecated and will be removed in version 3.0.0. Use `requestPurchase({ request, type: 'subs' })` instead.",
|
|
549
544
|
);
|
|
550
545
|
return (await requestPurchase({request, type: 'subs'})) as
|
|
551
|
-
|
|
|
552
|
-
|
|
|
546
|
+
| Purchase
|
|
547
|
+
| Purchase[]
|
|
553
548
|
| null
|
|
554
549
|
| void;
|
|
555
550
|
};
|
|
@@ -574,7 +569,7 @@ export const finishTransaction = ({
|
|
|
574
569
|
return Promise.resolve(true);
|
|
575
570
|
},
|
|
576
571
|
android: async () => {
|
|
577
|
-
const androidPurchase = purchase as
|
|
572
|
+
const androidPurchase = purchase as PurchaseAndroid;
|
|
578
573
|
|
|
579
574
|
if (isConsumable) {
|
|
580
575
|
return ExpoIapModule.consumeProduct(androidPurchase.purchaseToken);
|
package/src/modules/android.ts
CHANGED
|
@@ -26,7 +26,7 @@ export function isProductAndroid<T extends {platform?: string}>(
|
|
|
26
26
|
* @param {string} params.sku - The product's SKU (on Android)
|
|
27
27
|
* @param {string} params.packageName - The package name of your Android app (e.g., 'com.example.app')
|
|
28
28
|
* @returns {Promise<void>}
|
|
29
|
-
*
|
|
29
|
+
*
|
|
30
30
|
* @example
|
|
31
31
|
* ```typescript
|
|
32
32
|
* await deepLinkToSubscriptionsAndroid({
|
|
@@ -43,11 +43,9 @@ export const deepLinkToSubscriptionsAndroid = async ({
|
|
|
43
43
|
packageName: string;
|
|
44
44
|
}): Promise<void> => {
|
|
45
45
|
if (!packageName) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
'packageName is required for deepLinkToSubscriptionsAndroid',
|
|
48
|
-
);
|
|
46
|
+
throw new Error('packageName is required for deepLinkToSubscriptionsAndroid');
|
|
49
47
|
}
|
|
50
|
-
|
|
48
|
+
|
|
51
49
|
return Linking.openURL(
|
|
52
50
|
`https://play.google.com/store/account/subscriptions?package=${packageName}&sku=${sku}`,
|
|
53
51
|
);
|
|
@@ -120,9 +118,11 @@ export const acknowledgePurchaseAndroid = ({
|
|
|
120
118
|
* Open the Google Play Store to redeem offer codes (Android only).
|
|
121
119
|
* Note: Google Play does not provide a direct API to redeem codes within the app.
|
|
122
120
|
* This function opens the Play Store where users can manually enter their codes.
|
|
123
|
-
*
|
|
121
|
+
*
|
|
124
122
|
* @returns {Promise<void>}
|
|
125
123
|
*/
|
|
126
124
|
export const openRedeemOfferCodeAndroid = async (): Promise<void> => {
|
|
127
|
-
return Linking.openURL(
|
|
125
|
+
return Linking.openURL(
|
|
126
|
+
`https://play.google.com/redeem?code=`
|
|
127
|
+
);
|
|
128
128
|
};
|
package/src/modules/ios.ts
CHANGED
|
@@ -6,10 +6,8 @@ import ExpoIapModule from '../ExpoIapModule';
|
|
|
6
6
|
|
|
7
7
|
// Types
|
|
8
8
|
import {
|
|
9
|
-
ProductPurchase,
|
|
10
|
-
PurchaseError,
|
|
11
9
|
Purchase,
|
|
12
|
-
|
|
10
|
+
PurchaseError,
|
|
13
11
|
} from '../ExpoIap.types';
|
|
14
12
|
import type {
|
|
15
13
|
ProductStatusIOS,
|
|
@@ -18,7 +16,7 @@ import type {
|
|
|
18
16
|
import {Linking} from 'react-native';
|
|
19
17
|
|
|
20
18
|
export type TransactionEvent = {
|
|
21
|
-
transaction?:
|
|
19
|
+
transaction?: Purchase;
|
|
22
20
|
error?: PurchaseError;
|
|
23
21
|
};
|
|
24
22
|
|
|
@@ -39,7 +37,7 @@ export type TransactionEvent = {
|
|
|
39
37
|
export const transactionUpdatedIOS = (
|
|
40
38
|
listener: (event: TransactionEvent) => void,
|
|
41
39
|
) => {
|
|
42
|
-
const
|
|
40
|
+
const isPurchase = (item: unknown): item is Purchase => {
|
|
43
41
|
return (
|
|
44
42
|
item != null &&
|
|
45
43
|
typeof item === 'object' &&
|
|
@@ -51,18 +49,18 @@ export const transactionUpdatedIOS = (
|
|
|
51
49
|
|
|
52
50
|
// Helper function to safely convert Purchase to TransactionEvent
|
|
53
51
|
const mapPurchaseToTransactionEvent = (
|
|
54
|
-
purchase: Purchase
|
|
52
|
+
purchase: Purchase,
|
|
55
53
|
): TransactionEvent => {
|
|
56
54
|
// Validate the purchase object before casting
|
|
57
|
-
if (
|
|
55
|
+
if (isPurchase(purchase)) {
|
|
58
56
|
return {
|
|
59
|
-
transaction: purchase as
|
|
57
|
+
transaction: purchase as Purchase,
|
|
60
58
|
};
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
// Fallback: create a basic TransactionEvent structure
|
|
64
62
|
return {
|
|
65
|
-
transaction: purchase as
|
|
63
|
+
transaction: purchase as Purchase,
|
|
66
64
|
};
|
|
67
65
|
};
|
|
68
66
|
|
|
@@ -140,7 +138,7 @@ export const subscriptionStatusIOS = (
|
|
|
140
138
|
*/
|
|
141
139
|
export const currentEntitlementIOS = (
|
|
142
140
|
sku: string,
|
|
143
|
-
): Promise<
|
|
141
|
+
): Promise<Purchase> => {
|
|
144
142
|
return ExpoIapModule.currentEntitlement(sku);
|
|
145
143
|
};
|
|
146
144
|
|
|
@@ -153,7 +151,7 @@ export const currentEntitlementIOS = (
|
|
|
153
151
|
*
|
|
154
152
|
* @platform iOS
|
|
155
153
|
*/
|
|
156
|
-
export const latestTransactionIOS = (sku: string): Promise<
|
|
154
|
+
export const latestTransactionIOS = (sku: string): Promise<Purchase> => {
|
|
157
155
|
return ExpoIapModule.latestTransaction(sku);
|
|
158
156
|
};
|
|
159
157
|
|
|
@@ -241,7 +239,7 @@ export const getTransactionJwsIOS = (sku: string): Promise<string> => {
|
|
|
241
239
|
* isValid: boolean;
|
|
242
240
|
* receiptData: string;
|
|
243
241
|
* jwsRepresentation: string;
|
|
244
|
-
* latestTransaction?:
|
|
242
|
+
* latestTransaction?: Purchase;
|
|
245
243
|
* }>}
|
|
246
244
|
*/
|
|
247
245
|
export const validateReceiptIOS = async (
|
|
@@ -250,7 +248,7 @@ export const validateReceiptIOS = async (
|
|
|
250
248
|
isValid: boolean;
|
|
251
249
|
receiptData: string;
|
|
252
250
|
jwsRepresentation: string;
|
|
253
|
-
latestTransaction?:
|
|
251
|
+
latestTransaction?: Purchase;
|
|
254
252
|
}> => {
|
|
255
253
|
const result = await ExpoIapModule.validateReceiptIOS(sku);
|
|
256
254
|
return result;
|
|
@@ -362,7 +360,7 @@ export const subscriptionStatus = (
|
|
|
362
360
|
/**
|
|
363
361
|
* @deprecated Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.
|
|
364
362
|
*/
|
|
365
|
-
export const currentEntitlement = (sku: string): Promise<
|
|
363
|
+
export const currentEntitlement = (sku: string): Promise<Purchase> => {
|
|
366
364
|
console.warn(
|
|
367
365
|
'`currentEntitlement` is deprecated. Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.',
|
|
368
366
|
);
|
|
@@ -372,7 +370,7 @@ export const currentEntitlement = (sku: string): Promise<ProductPurchase> => {
|
|
|
372
370
|
/**
|
|
373
371
|
* @deprecated Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.
|
|
374
372
|
*/
|
|
375
|
-
export const latestTransaction = (sku: string): Promise<
|
|
373
|
+
export const latestTransaction = (sku: string): Promise<Purchase> => {
|
|
376
374
|
console.warn(
|
|
377
375
|
'`latestTransaction` is deprecated. Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.',
|
|
378
376
|
);
|
|
@@ -31,7 +31,7 @@ type ProductSubscriptionAndroidOfferDetail = {
|
|
|
31
31
|
export type ProductAndroid = ProductCommon & {
|
|
32
32
|
nameAndroid: string;
|
|
33
33
|
oneTimePurchaseOfferDetailsAndroid?: ProductAndroidOneTimePurchaseOfferDetail;
|
|
34
|
-
platform:
|
|
34
|
+
platform: "android";
|
|
35
35
|
subscriptionOfferDetailsAndroid?: ProductSubscriptionAndroidOfferDetail[];
|
|
36
36
|
/**
|
|
37
37
|
* @deprecated Use `nameAndroid` instead. This field will be removed in v2.9.0.
|
|
@@ -144,7 +144,7 @@ export const PurchaseStateAndroid = PurchaseAndroidState;
|
|
|
144
144
|
|
|
145
145
|
// Legacy naming for backward compatibility
|
|
146
146
|
export type ProductPurchaseAndroid = PurchaseCommon & {
|
|
147
|
-
platform:
|
|
147
|
+
platform: "android";
|
|
148
148
|
/**
|
|
149
149
|
* @deprecated Use `purchaseToken` instead. This field will be removed in a future version.
|
|
150
150
|
*/
|
|
@@ -167,8 +167,7 @@ export type PurchaseAndroid = ProductPurchaseAndroid;
|
|
|
167
167
|
/**
|
|
168
168
|
* @deprecated Use `ProductAndroidOneTimePurchaseOfferDetail` instead. This type will be removed in v2.9.0.
|
|
169
169
|
*/
|
|
170
|
-
export type OneTimePurchaseOfferDetails =
|
|
171
|
-
ProductAndroidOneTimePurchaseOfferDetail;
|
|
170
|
+
export type OneTimePurchaseOfferDetails = ProductAndroidOneTimePurchaseOfferDetail;
|
|
172
171
|
|
|
173
172
|
/**
|
|
174
173
|
* @deprecated Use `ProductSubscriptionAndroidOfferDetail` instead. This type will be removed in v2.9.0.
|