expo-iap 2.8.6 → 2.9.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/CHANGELOG.md +41 -0
- package/CLAUDE.md +7 -0
- package/CONTRIBUTING.md +3 -4
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +120 -7
- package/android/src/main/java/expo/modules/iap/Types.kt +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 +31 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +53 -25
- 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.map +1 -1
- 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 +12 -4
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +10 -5
- package/build/useIAP.js.map +1 -1
- package/ios/ExpoIap.podspec +1 -0
- package/ios/ExpoIapModule.swift +354 -1159
- package/jest.config.js +14 -17
- package/package.json +5 -3
- package/plugin/build/withIAP.d.ts +7 -1
- package/plugin/build/withIAP.js +16 -2
- package/plugin/build/withLocalOpenIAP.d.ts +9 -0
- package/plugin/build/withLocalOpenIAP.js +85 -0
- package/plugin/src/withIAP.ts +21 -2
- package/plugin/src/withLocalOpenIAP.ts +66 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/helpers/subscription.ts +21 -28
- package/src/index.ts +70 -33
- package/src/modules/android.ts +7 -7
- package/src/modules/ios.ts +11 -5
- package/src/types/ExpoIapAndroid.types.ts +3 -4
- package/src/types/ExpoIapIOS.types.ts +4 -3
- package/src/useIAP.ts +40 -12
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-iap",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0-rc.1",
|
|
4
4
|
"description": "In App Purchase module in Expo",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"docs:start": "cd docs && bun run start",
|
|
26
26
|
"docs:build": "cd docs && bun run build",
|
|
27
27
|
"docs:serve": "cd docs && bun run serve",
|
|
28
|
-
"docs:install": "cd docs && bun install"
|
|
28
|
+
"docs:install": "cd docs && bun install",
|
|
29
|
+
"generate:icon": "npx sharp-cli resize 32 32 -i docs/static/img/icon.png -o docs/static/img/favicon-32x32.png && npx sharp-cli resize 16 16 -i docs/static/img/icon.png -o docs/static/img/favicon-16x16.png && npx sharp-cli resize 180 180 -i docs/static/img/icon.png -o docs/static/img/apple-touch-icon.png && npx sharp-cli resize 192 192 -i docs/static/img/icon.png -o docs/static/img/android-chrome-192x192.png && npx sharp-cli resize 512 512 -i docs/static/img/icon.png -o docs/static/img/android-chrome-512x512.png && npx sharp-cli resize 150 150 -i docs/static/img/icon.png -o docs/static/img/mstile-150x150.png && npx sharp-cli resize 1200 630 -i docs/static/img/icon.png -o docs/static/img/og-image.png && npx sharp-cli resize 1200 600 -i docs/static/img/icon.png -o docs/static/img/twitter-card.png && npx sharp-cli resize 16 16 -i docs/static/img/icon.png -o docs/static/img/favicon.png && cp docs/static/img/favicon-16x16.png docs/static/img/favicon.ico"
|
|
29
30
|
},
|
|
30
31
|
"keywords": [
|
|
31
32
|
"react-native",
|
|
@@ -60,5 +61,6 @@
|
|
|
60
61
|
},
|
|
61
62
|
"expo": {
|
|
62
63
|
"plugin": "./app.plugin.js"
|
|
63
|
-
}
|
|
64
|
+
},
|
|
65
|
+
"packageManager": "yarn@3.6.1+sha512.de524adec81a6c3d7a26d936d439d2832e351cdfc5728f9d91f3fc85dd20b04391c038e9b4ecab11cae2b0dd9f0d55fd355af766bc5c1a7f8d25d96bb2a0b2ca"
|
|
64
66
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import { ConfigPlugin } from 'expo/config-plugins';
|
|
2
|
-
|
|
2
|
+
export interface ExpoIapPluginOptions {
|
|
3
|
+
/** Local development path for OpenIAP library */
|
|
4
|
+
localPath?: string;
|
|
5
|
+
/** Enable local development mode */
|
|
6
|
+
enableLocalDev?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const _default: ConfigPlugin<void | ExpoIapPluginOptions>;
|
|
3
9
|
export default _default;
|
package/plugin/build/withIAP.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
const config_plugins_1 = require("expo/config-plugins");
|
|
7
|
+
const withLocalOpenIAP_1 = __importDefault(require("./withLocalOpenIAP"));
|
|
4
8
|
const pkg = require('../../package.json');
|
|
5
9
|
// Global flag to prevent duplicate logs
|
|
6
10
|
let hasLoggedPluginExecution = false;
|
|
@@ -65,9 +69,19 @@ const withIapAndroid = (config) => {
|
|
|
65
69
|
});
|
|
66
70
|
return config;
|
|
67
71
|
};
|
|
68
|
-
const withIap = (config,
|
|
72
|
+
const withIap = (config, options) => {
|
|
69
73
|
try {
|
|
70
|
-
|
|
74
|
+
// Apply Android modifications
|
|
75
|
+
let result = withIapAndroid(config);
|
|
76
|
+
// Apply iOS local development if enabled
|
|
77
|
+
if (options?.enableLocalDev || options?.localPath) {
|
|
78
|
+
const localPath = options.localPath || '/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
|
|
79
|
+
console.log(`🔧 [expo-iap] Enabling local OpenIAP development at: ${localPath}`);
|
|
80
|
+
result = (0, withLocalOpenIAP_1.default)(result, { localPath });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log('📦 [expo-iap] Using OpenIAP from CocoaPods');
|
|
84
|
+
}
|
|
71
85
|
// Set flag after first execution to prevent duplicate logs
|
|
72
86
|
hasLoggedPluginExecution = true;
|
|
73
87
|
return result;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
|
+
/**
|
|
3
|
+
* Plugin to add local OpenIAP pod dependency for development
|
|
4
|
+
* This is only for local development with openiap-apple library
|
|
5
|
+
*/
|
|
6
|
+
declare const withLocalOpenIAP: ConfigPlugin<{
|
|
7
|
+
localPath?: string;
|
|
8
|
+
} | void>;
|
|
9
|
+
export default withLocalOpenIAP;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Plugin to add local OpenIAP pod dependency for development
|
|
41
|
+
* This is only for local development with openiap-apple library
|
|
42
|
+
*/
|
|
43
|
+
const withLocalOpenIAP = (config, props) => {
|
|
44
|
+
return (0, config_plugins_1.withDangerousMod)(config, [
|
|
45
|
+
'ios',
|
|
46
|
+
async (config) => {
|
|
47
|
+
const { platformProjectRoot } = config.modRequest;
|
|
48
|
+
const podfilePath = path.join(platformProjectRoot, 'Podfile');
|
|
49
|
+
// Default local path or use provided one
|
|
50
|
+
const localOpenIapPath = props?.localPath ||
|
|
51
|
+
'/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
|
|
52
|
+
// Check if local path exists
|
|
53
|
+
if (!fs.existsSync(localOpenIapPath)) {
|
|
54
|
+
console.warn(`⚠️ Local openiap-apple path not found: ${localOpenIapPath}`);
|
|
55
|
+
console.warn(' Skipping local pod injection. Using default pod resolution.');
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
// Read Podfile
|
|
59
|
+
let podfileContent = fs.readFileSync(podfilePath, 'utf8');
|
|
60
|
+
// Check if already has the local pod reference
|
|
61
|
+
if (podfileContent.includes("pod 'openiap',")) {
|
|
62
|
+
console.log('✅ Local OpenIAP pod already configured');
|
|
63
|
+
return config;
|
|
64
|
+
}
|
|
65
|
+
// Find the target block and inject the local pod
|
|
66
|
+
const targetRegex = /target\s+['"][\w]+['"]\s+do\s*\n\s*use_expo_modules!/;
|
|
67
|
+
if (targetRegex.test(podfileContent)) {
|
|
68
|
+
podfileContent = podfileContent.replace(targetRegex, (match) => {
|
|
69
|
+
return `${match}
|
|
70
|
+
|
|
71
|
+
# Local OpenIAP pod for development (added by expo-iap plugin)
|
|
72
|
+
pod 'openiap', :path => '${localOpenIapPath}'`;
|
|
73
|
+
});
|
|
74
|
+
// Write back to Podfile
|
|
75
|
+
fs.writeFileSync(podfilePath, podfileContent);
|
|
76
|
+
console.log(`✅ Added local OpenIAP pod at: ${localOpenIapPath}`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.warn('⚠️ Could not find target block in Podfile');
|
|
80
|
+
}
|
|
81
|
+
return config;
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
};
|
|
85
|
+
exports.default = withLocalOpenIAP;
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
withAndroidManifest,
|
|
6
6
|
withAppBuildGradle,
|
|
7
7
|
} from 'expo/config-plugins';
|
|
8
|
+
import withLocalOpenIAP from './withLocalOpenIAP';
|
|
8
9
|
|
|
9
10
|
const pkg = require('../../package.json');
|
|
10
11
|
|
|
@@ -98,9 +99,27 @@ const withIapAndroid: ConfigPlugin = (config) => {
|
|
|
98
99
|
return config;
|
|
99
100
|
};
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
export interface ExpoIapPluginOptions {
|
|
103
|
+
/** Local development path for OpenIAP library */
|
|
104
|
+
localPath?: string;
|
|
105
|
+
/** Enable local development mode */
|
|
106
|
+
enableLocalDev?: boolean;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const withIap: ConfigPlugin<ExpoIapPluginOptions | void> = (config, options) => {
|
|
102
110
|
try {
|
|
103
|
-
|
|
111
|
+
// Apply Android modifications
|
|
112
|
+
let result = withIapAndroid(config);
|
|
113
|
+
|
|
114
|
+
// Apply iOS local development if enabled
|
|
115
|
+
if (options?.enableLocalDev || options?.localPath) {
|
|
116
|
+
const localPath = options.localPath || '/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
|
|
117
|
+
console.log(`🔧 [expo-iap] Enabling local OpenIAP development at: ${localPath}`);
|
|
118
|
+
result = withLocalOpenIAP(result, { localPath });
|
|
119
|
+
} else {
|
|
120
|
+
console.log('📦 [expo-iap] Using OpenIAP from CocoaPods');
|
|
121
|
+
}
|
|
122
|
+
|
|
104
123
|
// Set flag after first execution to prevent duplicate logs
|
|
105
124
|
hasLoggedPluginExecution = true;
|
|
106
125
|
return result;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {ConfigPlugin, withDangerousMod} from '@expo/config-plugins';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Plugin to add local OpenIAP pod dependency for development
|
|
7
|
+
* This is only for local development with openiap-apple library
|
|
8
|
+
*/
|
|
9
|
+
const withLocalOpenIAP: ConfigPlugin<{localPath?: string} | void> = (
|
|
10
|
+
config,
|
|
11
|
+
props,
|
|
12
|
+
) => {
|
|
13
|
+
return withDangerousMod(config, [
|
|
14
|
+
'ios',
|
|
15
|
+
async (config) => {
|
|
16
|
+
const {platformProjectRoot} = config.modRequest;
|
|
17
|
+
const podfilePath = path.join(platformProjectRoot, 'Podfile');
|
|
18
|
+
|
|
19
|
+
// Default local path or use provided one
|
|
20
|
+
const localOpenIapPath =
|
|
21
|
+
props?.localPath ||
|
|
22
|
+
'/Users/crossplatformkorea/Github/hyodotdev/openiap-apple';
|
|
23
|
+
|
|
24
|
+
// Check if local path exists
|
|
25
|
+
if (!fs.existsSync(localOpenIapPath)) {
|
|
26
|
+
console.warn(`⚠️ Local openiap-apple path not found: ${localOpenIapPath}`);
|
|
27
|
+
console.warn(
|
|
28
|
+
' Skipping local pod injection. Using default pod resolution.',
|
|
29
|
+
);
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Read Podfile
|
|
34
|
+
let podfileContent = fs.readFileSync(podfilePath, 'utf8');
|
|
35
|
+
|
|
36
|
+
// Check if already has the local pod reference
|
|
37
|
+
if (podfileContent.includes("pod 'openiap',")) {
|
|
38
|
+
console.log('✅ Local OpenIAP pod already configured');
|
|
39
|
+
return config;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Find the target block and inject the local pod
|
|
43
|
+
const targetRegex =
|
|
44
|
+
/target\s+['"][\w]+['"]\s+do\s*\n\s*use_expo_modules!/;
|
|
45
|
+
|
|
46
|
+
if (targetRegex.test(podfileContent)) {
|
|
47
|
+
podfileContent = podfileContent.replace(targetRegex, (match) => {
|
|
48
|
+
return `${match}
|
|
49
|
+
|
|
50
|
+
# Local OpenIAP pod for development (added by expo-iap plugin)
|
|
51
|
+
pod 'openiap', :path => '${localOpenIapPath}'`;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Write back to Podfile
|
|
55
|
+
fs.writeFileSync(podfilePath, podfileContent);
|
|
56
|
+
console.log(`✅ Added local OpenIAP pod at: ${localOpenIapPath}`);
|
|
57
|
+
} else {
|
|
58
|
+
console.warn('⚠️ Could not find target block in Podfile');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return config;
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default withLocalOpenIAP;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/
|
|
1
|
+
{"root":["./src/withiap.ts","./src/withlocalopeniap.ts"],"version":"5.9.2"}
|
|
@@ -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
|
+
};
|