react-native-iap 15.3.6 → 15.4.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 +1 -1
- package/android/build.gradle +13 -5
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +16 -3
- package/ios/HybridRnIap.swift +12 -0
- package/lib/module/index.js +56 -16
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.kepler.js +170 -0
- package/lib/module/index.kepler.js.map +1 -0
- package/lib/module/types/amazon-devices-kepler/index.d.js +2 -0
- package/lib/module/types/amazon-devices-kepler/index.d.js.map +1 -0
- package/lib/module/types.js +5 -2
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/error.js +2 -2
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/utils/type-bridge.js +4 -1
- package/lib/module/utils/type-bridge.js.map +1 -1
- package/lib/module/vega-adapter.js +981 -0
- package/lib/module/vega-adapter.js.map +1 -0
- package/lib/module/vega.js +10 -0
- package/lib/module/vega.js.map +1 -0
- package/lib/module/vega.kepler.js +18 -0
- package/lib/module/vega.kepler.js.map +1 -0
- package/lib/typescript/plugin/src/withIAP.d.ts +33 -0
- package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +9 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.kepler.d.ts +55 -0
- package/lib/typescript/src/index.kepler.d.ts.map +1 -0
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +10 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +16 -3
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
- package/lib/typescript/src/vega-adapter.d.ts +76 -0
- package/lib/typescript/src/vega-adapter.d.ts.map +1 -0
- package/lib/typescript/src/vega.d.ts +4 -0
- package/lib/typescript/src/vega.d.ts.map +1 -0
- package/lib/typescript/src/vega.kepler.d.ts +4 -0
- package/lib/typescript/src/vega.kepler.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroIap+autolinking.cmake +1 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +5 -0
- package/nitrogen/generated/android/c++/JIapStore.hpp +3 -0
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitAmazonProps.hpp +71 -0
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitProps.hpp +9 -1
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithProviderProps.hpp +4 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitProps.hpp +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/IapStore.kt +2 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitAmazonProps.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitProps.kt +5 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.kt +53 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +47 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +3 -0
- package/nitrogen/generated/ios/swift/IapStore.swift +4 -0
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitAmazonProps.swift +103 -0
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitProps.swift +38 -1
- package/nitrogen/generated/ios/swift/Variant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.swift +18 -0
- package/nitrogen/generated/shared/c++/IapStore.hpp +4 -0
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitAmazonProps.hpp +94 -0
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitProps.hpp +8 -1
- package/openiap-versions.json +1 -1
- package/package.json +17 -4
- package/plugin/build/withIAP.d.ts +33 -0
- package/plugin/build/withIAP.js +109 -42
- package/plugin/src/withIAP.ts +185 -55
- package/src/index.kepler.ts +311 -0
- package/src/index.ts +65 -19
- package/src/specs/RnIap.nitro.ts +13 -2
- package/src/types/amazon-devices-kepler/index.d.ts +14 -0
- package/src/types.ts +17 -3
- package/src/utils/error.ts +2 -2
- package/src/utils/type-bridge.ts +4 -1
- package/src/vega-adapter.ts +1503 -0
- package/src/vega.kepler.ts +21 -0
- package/src/vega.ts +10 -0
- package/android/src/test/java/com/margelo/nitro/iap/ListenerThreadSafetyTest.kt +0 -154
- package/android/src/test/java/com/margelo/nitro/iap/OpenIapExceptionTest.kt +0 -93
- package/android/src/test/java/com/margelo/nitro/iap/ProductQueryHelpersTest.kt +0 -140
- package/plugin/tsconfig.tsbuildinfo +0 -1
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-iap",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.4.0-rc.1",
|
|
4
4
|
"description": "React Native In-App Purchases module for iOS and Android using Nitro",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"source": "./src/index.ts",
|
|
10
|
+
"react-native": "./src/index.ts",
|
|
10
11
|
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
-
"default": "./lib/module/index.js"
|
|
12
|
-
"react-native": "./src/index.ts"
|
|
12
|
+
"default": "./lib/module/index.js"
|
|
13
13
|
},
|
|
14
14
|
"./plugin": "./app.plugin.js",
|
|
15
15
|
"./app.plugin.js": "./app.plugin.js",
|
|
@@ -34,9 +34,12 @@
|
|
|
34
34
|
"!android/gradlew",
|
|
35
35
|
"!android/gradlew.bat",
|
|
36
36
|
"!android/local.properties",
|
|
37
|
+
"!android/src/androidTest",
|
|
38
|
+
"!android/src/test",
|
|
37
39
|
"!**/__tests__",
|
|
38
40
|
"!**/__fixtures__",
|
|
39
41
|
"!**/__mocks__",
|
|
42
|
+
"!**/*.tsbuildinfo",
|
|
40
43
|
"!**/.*"
|
|
41
44
|
],
|
|
42
45
|
"scripts": {
|
|
@@ -59,7 +62,7 @@
|
|
|
59
62
|
"test": "jest --coverage",
|
|
60
63
|
"test:library": "jest --coverage",
|
|
61
64
|
"test:example": "yarn workspace rn-iap-example test --coverage",
|
|
62
|
-
"test:plugin": "jest plugin --coverage",
|
|
65
|
+
"test:plugin": "jest plugin/__tests__ --runInBand --coverage",
|
|
63
66
|
"test:all": "yarn test:library && yarn test:example",
|
|
64
67
|
"test:ci": "jest --maxWorkers=2 --coverage",
|
|
65
68
|
"test:ci:example": "yarn workspace rn-iap-example test --coverage",
|
|
@@ -118,10 +121,20 @@
|
|
|
118
121
|
"typescript": "^5.9.2"
|
|
119
122
|
},
|
|
120
123
|
"peerDependencies": {
|
|
124
|
+
"@amazon-devices/keplerscript-appstore-iap-lib": "~2.12.13",
|
|
125
|
+
"@amazon-devices/package-manager-lib": "~1.0.1767254401",
|
|
121
126
|
"react": "*",
|
|
122
127
|
"react-native": "*",
|
|
123
128
|
"react-native-nitro-modules": "^0.35.0"
|
|
124
129
|
},
|
|
130
|
+
"peerDependenciesMeta": {
|
|
131
|
+
"@amazon-devices/keplerscript-appstore-iap-lib": {
|
|
132
|
+
"optional": true
|
|
133
|
+
},
|
|
134
|
+
"@amazon-devices/package-manager-lib": {
|
|
135
|
+
"optional": true
|
|
136
|
+
}
|
|
137
|
+
},
|
|
125
138
|
"workspaces": [
|
|
126
139
|
"example"
|
|
127
140
|
],
|
|
@@ -37,6 +37,39 @@ type IapPluginProps = {
|
|
|
37
37
|
* Get your project key from https://kit.openiap.dev
|
|
38
38
|
*/
|
|
39
39
|
iapkitApiKey?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Amazon platform targets.
|
|
42
|
+
*
|
|
43
|
+
* Fire OS selects the Android Amazon Appstore flavor. Vega OS is selected by
|
|
44
|
+
* the Kepler runtime and does not change the Android Gradle flavor.
|
|
45
|
+
*/
|
|
46
|
+
amazon?: {
|
|
47
|
+
/**
|
|
48
|
+
* Fire OS module for Amazon-distributed Android builds.
|
|
49
|
+
* @platform android
|
|
50
|
+
*/
|
|
51
|
+
fireOS?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Vega OS runtime target. This is not an Android flavor.
|
|
54
|
+
*/
|
|
55
|
+
vegaOS?: boolean;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Optional non-Amazon Android store modules.
|
|
59
|
+
* Fire OS takes precedence when both Fire OS and Horizon are enabled.
|
|
60
|
+
*/
|
|
61
|
+
modules?: {
|
|
62
|
+
/**
|
|
63
|
+
* Horizon module for Meta Quest/VR devices.
|
|
64
|
+
* @platform android
|
|
65
|
+
*/
|
|
66
|
+
horizon?: boolean;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export declare function resolveAmazonPlatformFlags(props?: IapPluginProps): {
|
|
70
|
+
isFireOsEnabled: boolean;
|
|
71
|
+
isVegaEnabled: boolean;
|
|
72
|
+
isHorizonEnabled: boolean;
|
|
40
73
|
};
|
|
41
74
|
type IapPluginCallable = {
|
|
42
75
|
(config: ExpoConfig): ExpoConfig;
|
package/plugin/build/withIAP.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = exports.withIosAlternativeBilling = exports.modifyProjectBuildGradle = void 0;
|
|
4
|
+
exports.resolveAmazonPlatformFlags = resolveAmazonPlatformFlags;
|
|
4
5
|
const config_plugins_1 = require("expo/config-plugins");
|
|
5
|
-
const node_fs_1 = require("node:fs");
|
|
6
|
-
const node_path_1 = require("node:path");
|
|
7
6
|
const pkg = require('../../package.json');
|
|
8
7
|
// Global flag to prevent duplicate logs
|
|
9
8
|
let hasLoggedPluginExecution = false;
|
|
@@ -32,35 +31,28 @@ const modifyProjectBuildGradle = (gradle) => {
|
|
|
32
31
|
return gradle;
|
|
33
32
|
};
|
|
34
33
|
exports.modifyProjectBuildGradle = modifyProjectBuildGradle;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const OPENIAP_GROUP = 'io.github.hyochan.openiap';
|
|
35
|
+
const modifyAppBuildGradle = (gradle, isHorizonEnabled, isFireOsEnabled) => {
|
|
36
|
+
const escapeRegExp = (value) => {
|
|
37
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
38
|
+
};
|
|
39
|
+
function loadOpenIapVersion() {
|
|
40
|
+
try {
|
|
41
|
+
const parsed = require('../../openiap-versions.json');
|
|
42
|
+
const googleVersion = typeof parsed?.google === 'string' ? parsed.google.trim() : '';
|
|
43
|
+
if (!googleVersion) {
|
|
44
|
+
throw new Error('react-native-iap: "google" version missing or invalid in openiap-versions.json');
|
|
45
|
+
}
|
|
46
|
+
return googleVersion;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new Error(`react-native-iap: Unable to load openiap-versions.json (${error instanceof Error ? error.message : error})`);
|
|
44
50
|
}
|
|
45
|
-
return { google: googleVersion };
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
throw new Error(`react-native-iap: Unable to load openiap-versions.json (${error instanceof Error ? error.message : error})`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
let cachedOpenIapVersion = null;
|
|
52
|
-
const getOpenIapVersion = () => {
|
|
53
|
-
if (cachedOpenIapVersion) {
|
|
54
|
-
return cachedOpenIapVersion;
|
|
55
51
|
}
|
|
56
|
-
cachedOpenIapVersion = loadOpenIapConfig().google;
|
|
57
|
-
return cachedOpenIapVersion;
|
|
58
|
-
};
|
|
59
|
-
const modifyAppBuildGradle = (gradle) => {
|
|
60
52
|
let modified = gradle;
|
|
61
53
|
let openiapVersion;
|
|
62
54
|
try {
|
|
63
|
-
openiapVersion =
|
|
55
|
+
openiapVersion = loadOpenIapVersion();
|
|
64
56
|
}
|
|
65
57
|
catch (error) {
|
|
66
58
|
config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `react-native-iap: Failed to resolve OpenIAP version (${error instanceof Error ? error.message : error})`);
|
|
@@ -72,8 +64,26 @@ const modifyAppBuildGradle = (gradle) => {
|
|
|
72
64
|
.replace(/^[ \t]*(implementation|api)[ \t]+["']com\.android\.billingclient:billing-ktx:[^"']+["'][ \t]*$/gim, '')
|
|
73
65
|
.replace(/^[ \t]*(implementation|api)[ \t]+["']com\.google\.android\.gms:play-services-base:[^"']+["'][ \t]*$/gim, '')
|
|
74
66
|
.replace(/\n{3,}/g, '\n\n');
|
|
75
|
-
const
|
|
76
|
-
|
|
67
|
+
const flavor = isFireOsEnabled
|
|
68
|
+
? 'amazon'
|
|
69
|
+
: isHorizonEnabled
|
|
70
|
+
? 'horizon'
|
|
71
|
+
: 'play';
|
|
72
|
+
const artifactId = isFireOsEnabled
|
|
73
|
+
? 'openiap-google-amazon'
|
|
74
|
+
: isHorizonEnabled
|
|
75
|
+
? 'openiap-google-horizon'
|
|
76
|
+
: 'openiap-google';
|
|
77
|
+
const openiapCoord = `${OPENIAP_GROUP}:${artifactId}`;
|
|
78
|
+
const openiapDep = ` implementation "${openiapCoord}:${openiapVersion}"`;
|
|
79
|
+
const desiredOpeniapLine = new RegExp(`^[ \\t]*(?:implementation|api)[ \\t]+\\(?["']${escapeRegExp(`${openiapCoord}:${openiapVersion}`)}["']\\)?[ \\t]*$`, 'm');
|
|
80
|
+
const openiapAnyLine = /^[ \t]*(implementation|api)[ \t]+\(?["']io\.github\.hyochan\.openiap:openiap-google(?:-(?:horizon|amazon))?:[^"']+["']\)?[ \t]*$/gim;
|
|
81
|
+
const withoutExistingOpeniap = modified.replace(openiapAnyLine, '');
|
|
82
|
+
const hadExistingOpeniap = withoutExistingOpeniap !== modified;
|
|
83
|
+
if (hadExistingOpeniap) {
|
|
84
|
+
modified = withoutExistingOpeniap.replace(/\n{3,}/g, '\n\n');
|
|
85
|
+
}
|
|
86
|
+
if (!desiredOpeniapLine.test(modified)) {
|
|
77
87
|
if (!/dependencies\s*{/.test(modified)) {
|
|
78
88
|
modified += `\n\ndependencies {\n${openiapDep}\n}\n`;
|
|
79
89
|
}
|
|
@@ -84,31 +94,71 @@ const modifyAppBuildGradle = (gradle) => {
|
|
|
84
94
|
console.log(`🛠️ react-native-iap: Added OpenIAP (${openiapVersion}) to build.gradle`);
|
|
85
95
|
}
|
|
86
96
|
}
|
|
97
|
+
// Remove stale OpenIAP platform strategies even when returning to the default
|
|
98
|
+
// Play artifact. Otherwise a previous Fire OS/Horizon prebuild can keep
|
|
99
|
+
// selecting the wrong local flavor.
|
|
100
|
+
const strategyPattern = /^[ \t]*missingDimensionStrategy[ \t]*\(?[ \t]*["']platform["'][ \t]*,[ \t]*["'](play|horizon|amazon)["'][ \t]*\)?[ \t]*$/gm;
|
|
101
|
+
const withoutExistingStrategy = modified.replace(strategyPattern, '');
|
|
102
|
+
if (withoutExistingStrategy !== modified) {
|
|
103
|
+
modified = withoutExistingStrategy;
|
|
104
|
+
}
|
|
105
|
+
const defaultConfigRegex = /defaultConfig\s*{/;
|
|
106
|
+
if (defaultConfigRegex.test(modified)) {
|
|
107
|
+
const strategyLine = ` missingDimensionStrategy "platform", "${flavor}"`;
|
|
108
|
+
if (!/missingDimensionStrategy.*platform/.test(modified)) {
|
|
109
|
+
modified = addLineToGradle(modified, defaultConfigRegex, strategyLine);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
87
112
|
return modified;
|
|
88
113
|
};
|
|
89
114
|
const withIapAndroid = (config, props) => {
|
|
90
115
|
// Add OpenIAP dependency to app build.gradle
|
|
91
116
|
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
92
|
-
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
|
|
117
|
+
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents, props?.isHorizonEnabled, props?.isFireOsEnabled);
|
|
118
|
+
return config;
|
|
119
|
+
});
|
|
120
|
+
config = (0, config_plugins_1.withGradleProperties)(config, (config) => {
|
|
121
|
+
const horizonValue = props?.isHorizonEnabled ?? false;
|
|
122
|
+
const fireOsValue = props?.isFireOsEnabled ?? false;
|
|
123
|
+
config.modResults = config.modResults.filter((item) => item.type !== 'property' ||
|
|
124
|
+
!['horizonEnabled', 'fireOsEnabled'].includes(item.key));
|
|
125
|
+
config.modResults.push({
|
|
126
|
+
type: 'property',
|
|
127
|
+
key: 'horizonEnabled',
|
|
128
|
+
value: String(horizonValue),
|
|
129
|
+
});
|
|
130
|
+
config.modResults.push({
|
|
131
|
+
type: 'property',
|
|
132
|
+
key: 'fireOsEnabled',
|
|
133
|
+
value: String(fireOsValue),
|
|
134
|
+
});
|
|
93
135
|
return config;
|
|
94
136
|
});
|
|
95
137
|
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
96
138
|
const manifest = config.modResults;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
139
|
+
const existingPermissions = manifest.manifest['uses-permission'];
|
|
140
|
+
const permissions = Array.isArray(existingPermissions)
|
|
141
|
+
? existingPermissions
|
|
142
|
+
: existingPermissions
|
|
143
|
+
? [existingPermissions]
|
|
144
|
+
: [];
|
|
145
|
+
manifest.manifest['uses-permission'] = permissions;
|
|
101
146
|
const billingPerm = { $: { 'android:name': 'com.android.vending.BILLING' } };
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
permissions.push(billingPerm);
|
|
105
|
-
if (!hasLoggedPluginExecution) {
|
|
106
|
-
console.log('✅ Added com.android.vending.BILLING to AndroidManifest.xml');
|
|
107
|
-
}
|
|
147
|
+
if (props?.isFireOsEnabled) {
|
|
148
|
+
manifest.manifest['uses-permission'] = permissions.filter((p) => p.$['android:name'] !== 'com.android.vending.BILLING');
|
|
108
149
|
}
|
|
109
150
|
else {
|
|
110
|
-
|
|
111
|
-
|
|
151
|
+
const alreadyExists = permissions.some((p) => p.$['android:name'] === 'com.android.vending.BILLING');
|
|
152
|
+
if (!alreadyExists) {
|
|
153
|
+
permissions.push(billingPerm);
|
|
154
|
+
if (!hasLoggedPluginExecution) {
|
|
155
|
+
console.log('✅ Added com.android.vending.BILLING to AndroidManifest.xml');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
if (!hasLoggedPluginExecution) {
|
|
160
|
+
console.log('ℹ️ com.android.vending.BILLING already exists in AndroidManifest.xml');
|
|
161
|
+
}
|
|
112
162
|
}
|
|
113
163
|
}
|
|
114
164
|
// Add IAPKit API key as meta-data if provided
|
|
@@ -224,6 +274,18 @@ const withIosAlternativeBilling = (config, options) => {
|
|
|
224
274
|
return config;
|
|
225
275
|
};
|
|
226
276
|
exports.withIosAlternativeBilling = withIosAlternativeBilling;
|
|
277
|
+
function resolveAmazonPlatformFlags(props) {
|
|
278
|
+
const isFireOsEnabled = props?.amazon?.fireOS ?? false;
|
|
279
|
+
const isVegaEnabled = props?.amazon?.vegaOS ?? false;
|
|
280
|
+
const isHorizonEnabled = isFireOsEnabled
|
|
281
|
+
? false
|
|
282
|
+
: (props?.modules?.horizon ?? false);
|
|
283
|
+
return {
|
|
284
|
+
isFireOsEnabled,
|
|
285
|
+
isVegaEnabled,
|
|
286
|
+
isHorizonEnabled,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
227
289
|
const withIapIosFollyWorkaround = (config, props) => {
|
|
228
290
|
const newKey = props?.ios?.['with-folly-no-coroutines'];
|
|
229
291
|
const oldKey = props?.ios?.['with-folly-no-couroutines'];
|
|
@@ -289,7 +351,12 @@ const withIapkitApiKeyIOS = (config, apiKey) => {
|
|
|
289
351
|
};
|
|
290
352
|
const withIAP = (config, props) => {
|
|
291
353
|
try {
|
|
292
|
-
|
|
354
|
+
const { isFireOsEnabled, isHorizonEnabled } = resolveAmazonPlatformFlags(props);
|
|
355
|
+
let result = withIapAndroid(config, {
|
|
356
|
+
iapkitApiKey: props?.iapkitApiKey,
|
|
357
|
+
isHorizonEnabled,
|
|
358
|
+
isFireOsEnabled,
|
|
359
|
+
});
|
|
293
360
|
result = withIapIosFollyWorkaround(result, props);
|
|
294
361
|
// Add iOS alternative billing configuration if provided
|
|
295
362
|
if (props?.iosAlternativeBilling) {
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -3,14 +3,13 @@ import {
|
|
|
3
3
|
WarningAggregator,
|
|
4
4
|
withAndroidManifest,
|
|
5
5
|
withAppBuildGradle,
|
|
6
|
+
withGradleProperties,
|
|
6
7
|
withPodfile,
|
|
7
8
|
withEntitlementsPlist,
|
|
8
9
|
withInfoPlist,
|
|
9
10
|
} from 'expo/config-plugins';
|
|
10
11
|
import type {ConfigPlugin} from 'expo/config-plugins';
|
|
11
12
|
import type {ExpoConfig} from '@expo/config-types';
|
|
12
|
-
import {readFileSync} from 'node:fs';
|
|
13
|
-
import {resolve as resolvePath} from 'node:path';
|
|
14
13
|
|
|
15
14
|
const pkg = require('../../package.json');
|
|
16
15
|
|
|
@@ -49,43 +48,40 @@ export const modifyProjectBuildGradle = (gradle: string): string => {
|
|
|
49
48
|
return gradle;
|
|
50
49
|
};
|
|
51
50
|
|
|
52
|
-
const
|
|
51
|
+
const OPENIAP_GROUP = 'io.github.hyochan.openiap';
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const modifyAppBuildGradle = (
|
|
54
|
+
gradle: string,
|
|
55
|
+
isHorizonEnabled?: boolean,
|
|
56
|
+
isFireOsEnabled?: boolean,
|
|
57
|
+
): string => {
|
|
58
|
+
const escapeRegExp = (value: string): string => {
|
|
59
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function loadOpenIapVersion(): string {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = require('../../openiap-versions.json');
|
|
65
|
+
const googleVersion =
|
|
66
|
+
typeof parsed?.google === 'string' ? parsed.google.trim() : '';
|
|
67
|
+
if (!googleVersion) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
'react-native-iap: "google" version missing or invalid in openiap-versions.json',
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return googleVersion;
|
|
73
|
+
} catch (error) {
|
|
62
74
|
throw new Error(
|
|
63
|
-
|
|
75
|
+
`react-native-iap: Unable to load openiap-versions.json (${error instanceof Error ? error.message : error})`,
|
|
64
76
|
);
|
|
65
77
|
}
|
|
66
|
-
return {google: googleVersion};
|
|
67
|
-
} catch (error) {
|
|
68
|
-
throw new Error(
|
|
69
|
-
`react-native-iap: Unable to load openiap-versions.json (${error instanceof Error ? error.message : error})`,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let cachedOpenIapVersion: string | null = null;
|
|
75
|
-
const getOpenIapVersion = (): string => {
|
|
76
|
-
if (cachedOpenIapVersion) {
|
|
77
|
-
return cachedOpenIapVersion;
|
|
78
78
|
}
|
|
79
|
-
cachedOpenIapVersion = loadOpenIapConfig().google;
|
|
80
|
-
return cachedOpenIapVersion;
|
|
81
|
-
};
|
|
82
79
|
|
|
83
|
-
const modifyAppBuildGradle = (gradle: string): string => {
|
|
84
80
|
let modified = gradle;
|
|
85
81
|
|
|
86
82
|
let openiapVersion: string;
|
|
87
83
|
try {
|
|
88
|
-
openiapVersion =
|
|
84
|
+
openiapVersion = loadOpenIapVersion();
|
|
89
85
|
} catch (error) {
|
|
90
86
|
WarningAggregator.addWarningAndroid(
|
|
91
87
|
'react-native-iap',
|
|
@@ -107,9 +103,34 @@ const modifyAppBuildGradle = (gradle: string): string => {
|
|
|
107
103
|
)
|
|
108
104
|
.replace(/\n{3,}/g, '\n\n');
|
|
109
105
|
|
|
110
|
-
const
|
|
106
|
+
const flavor = isFireOsEnabled
|
|
107
|
+
? 'amazon'
|
|
108
|
+
: isHorizonEnabled
|
|
109
|
+
? 'horizon'
|
|
110
|
+
: 'play';
|
|
111
|
+
const artifactId = isFireOsEnabled
|
|
112
|
+
? 'openiap-google-amazon'
|
|
113
|
+
: isHorizonEnabled
|
|
114
|
+
? 'openiap-google-horizon'
|
|
115
|
+
: 'openiap-google';
|
|
116
|
+
const openiapCoord = `${OPENIAP_GROUP}:${artifactId}`;
|
|
117
|
+
const openiapDep = ` implementation "${openiapCoord}:${openiapVersion}"`;
|
|
118
|
+
const desiredOpeniapLine = new RegExp(
|
|
119
|
+
`^[ \\t]*(?:implementation|api)[ \\t]+\\(?["']${escapeRegExp(
|
|
120
|
+
`${openiapCoord}:${openiapVersion}`,
|
|
121
|
+
)}["']\\)?[ \\t]*$`,
|
|
122
|
+
'm',
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const openiapAnyLine =
|
|
126
|
+
/^[ \t]*(implementation|api)[ \t]+\(?["']io\.github\.hyochan\.openiap:openiap-google(?:-(?:horizon|amazon))?:[^"']+["']\)?[ \t]*$/gim;
|
|
127
|
+
const withoutExistingOpeniap = modified.replace(openiapAnyLine, '');
|
|
128
|
+
const hadExistingOpeniap = withoutExistingOpeniap !== modified;
|
|
129
|
+
if (hadExistingOpeniap) {
|
|
130
|
+
modified = withoutExistingOpeniap.replace(/\n{3,}/g, '\n\n');
|
|
131
|
+
}
|
|
111
132
|
|
|
112
|
-
if (!
|
|
133
|
+
if (!desiredOpeniapLine.test(modified)) {
|
|
113
134
|
if (!/dependencies\s*{/.test(modified)) {
|
|
114
135
|
modified += `\n\ndependencies {\n${openiapDep}\n}\n`;
|
|
115
136
|
} else {
|
|
@@ -122,45 +143,101 @@ const modifyAppBuildGradle = (gradle: string): string => {
|
|
|
122
143
|
}
|
|
123
144
|
}
|
|
124
145
|
|
|
146
|
+
// Remove stale OpenIAP platform strategies even when returning to the default
|
|
147
|
+
// Play artifact. Otherwise a previous Fire OS/Horizon prebuild can keep
|
|
148
|
+
// selecting the wrong local flavor.
|
|
149
|
+
const strategyPattern =
|
|
150
|
+
/^[ \t]*missingDimensionStrategy[ \t]*\(?[ \t]*["']platform["'][ \t]*,[ \t]*["'](play|horizon|amazon)["'][ \t]*\)?[ \t]*$/gm;
|
|
151
|
+
const withoutExistingStrategy = modified.replace(strategyPattern, '');
|
|
152
|
+
if (withoutExistingStrategy !== modified) {
|
|
153
|
+
modified = withoutExistingStrategy;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const defaultConfigRegex = /defaultConfig\s*{/;
|
|
157
|
+
if (defaultConfigRegex.test(modified)) {
|
|
158
|
+
const strategyLine = ` missingDimensionStrategy "platform", "${flavor}"`;
|
|
159
|
+
if (!/missingDimensionStrategy.*platform/.test(modified)) {
|
|
160
|
+
modified = addLineToGradle(modified, defaultConfigRegex, strategyLine);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
125
164
|
return modified;
|
|
126
165
|
};
|
|
127
166
|
|
|
128
|
-
const withIapAndroid: ConfigPlugin<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
167
|
+
const withIapAndroid: ConfigPlugin<
|
|
168
|
+
| {
|
|
169
|
+
iapkitApiKey?: string;
|
|
170
|
+
isHorizonEnabled?: boolean;
|
|
171
|
+
isFireOsEnabled?: boolean;
|
|
172
|
+
}
|
|
173
|
+
| undefined
|
|
174
|
+
> = (config, props) => {
|
|
132
175
|
// Add OpenIAP dependency to app build.gradle
|
|
133
176
|
config = withAppBuildGradle(config, (config) => {
|
|
134
177
|
config.modResults.contents = modifyAppBuildGradle(
|
|
135
178
|
config.modResults.contents,
|
|
179
|
+
props?.isHorizonEnabled,
|
|
180
|
+
props?.isFireOsEnabled,
|
|
181
|
+
);
|
|
182
|
+
return config;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
config = withGradleProperties(config, (config) => {
|
|
186
|
+
const horizonValue = props?.isHorizonEnabled ?? false;
|
|
187
|
+
const fireOsValue = props?.isFireOsEnabled ?? false;
|
|
188
|
+
|
|
189
|
+
config.modResults = config.modResults.filter(
|
|
190
|
+
(item) =>
|
|
191
|
+
item.type !== 'property' ||
|
|
192
|
+
!['horizonEnabled', 'fireOsEnabled'].includes(item.key),
|
|
136
193
|
);
|
|
194
|
+
|
|
195
|
+
config.modResults.push({
|
|
196
|
+
type: 'property',
|
|
197
|
+
key: 'horizonEnabled',
|
|
198
|
+
value: String(horizonValue),
|
|
199
|
+
});
|
|
200
|
+
config.modResults.push({
|
|
201
|
+
type: 'property',
|
|
202
|
+
key: 'fireOsEnabled',
|
|
203
|
+
value: String(fireOsValue),
|
|
204
|
+
});
|
|
205
|
+
|
|
137
206
|
return config;
|
|
138
207
|
});
|
|
139
208
|
|
|
140
209
|
config = withAndroidManifest(config, (config) => {
|
|
141
210
|
const manifest = config.modResults;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
211
|
+
const existingPermissions = manifest.manifest['uses-permission'];
|
|
212
|
+
const permissions = Array.isArray(existingPermissions)
|
|
213
|
+
? existingPermissions
|
|
214
|
+
: existingPermissions
|
|
215
|
+
? [existingPermissions]
|
|
216
|
+
: [];
|
|
217
|
+
manifest.manifest['uses-permission'] = permissions;
|
|
147
218
|
const billingPerm = {$: {'android:name': 'com.android.vending.BILLING'}};
|
|
148
219
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
permissions.push(billingPerm);
|
|
154
|
-
if (!hasLoggedPluginExecution) {
|
|
155
|
-
console.log(
|
|
156
|
-
'✅ Added com.android.vending.BILLING to AndroidManifest.xml',
|
|
157
|
-
);
|
|
158
|
-
}
|
|
220
|
+
if (props?.isFireOsEnabled) {
|
|
221
|
+
manifest.manifest['uses-permission'] = permissions.filter(
|
|
222
|
+
(p) => p.$['android:name'] !== 'com.android.vending.BILLING',
|
|
223
|
+
);
|
|
159
224
|
} else {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
225
|
+
const alreadyExists = permissions.some(
|
|
226
|
+
(p) => p.$['android:name'] === 'com.android.vending.BILLING',
|
|
227
|
+
);
|
|
228
|
+
if (!alreadyExists) {
|
|
229
|
+
permissions.push(billingPerm);
|
|
230
|
+
if (!hasLoggedPluginExecution) {
|
|
231
|
+
console.log(
|
|
232
|
+
'✅ Added com.android.vending.BILLING to AndroidManifest.xml',
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
if (!hasLoggedPluginExecution) {
|
|
237
|
+
console.log(
|
|
238
|
+
'ℹ️ com.android.vending.BILLING already exists in AndroidManifest.xml',
|
|
239
|
+
);
|
|
240
|
+
}
|
|
164
241
|
}
|
|
165
242
|
}
|
|
166
243
|
|
|
@@ -366,8 +443,54 @@ type IapPluginProps = {
|
|
|
366
443
|
* Get your project key from https://kit.openiap.dev
|
|
367
444
|
*/
|
|
368
445
|
iapkitApiKey?: string;
|
|
446
|
+
/**
|
|
447
|
+
* Amazon platform targets.
|
|
448
|
+
*
|
|
449
|
+
* Fire OS selects the Android Amazon Appstore flavor. Vega OS is selected by
|
|
450
|
+
* the Kepler runtime and does not change the Android Gradle flavor.
|
|
451
|
+
*/
|
|
452
|
+
amazon?: {
|
|
453
|
+
/**
|
|
454
|
+
* Fire OS module for Amazon-distributed Android builds.
|
|
455
|
+
* @platform android
|
|
456
|
+
*/
|
|
457
|
+
fireOS?: boolean;
|
|
458
|
+
/**
|
|
459
|
+
* Vega OS runtime target. This is not an Android flavor.
|
|
460
|
+
*/
|
|
461
|
+
vegaOS?: boolean;
|
|
462
|
+
};
|
|
463
|
+
/**
|
|
464
|
+
* Optional non-Amazon Android store modules.
|
|
465
|
+
* Fire OS takes precedence when both Fire OS and Horizon are enabled.
|
|
466
|
+
*/
|
|
467
|
+
modules?: {
|
|
468
|
+
/**
|
|
469
|
+
* Horizon module for Meta Quest/VR devices.
|
|
470
|
+
* @platform android
|
|
471
|
+
*/
|
|
472
|
+
horizon?: boolean;
|
|
473
|
+
};
|
|
369
474
|
};
|
|
370
475
|
|
|
476
|
+
export function resolveAmazonPlatformFlags(props?: IapPluginProps): {
|
|
477
|
+
isFireOsEnabled: boolean;
|
|
478
|
+
isVegaEnabled: boolean;
|
|
479
|
+
isHorizonEnabled: boolean;
|
|
480
|
+
} {
|
|
481
|
+
const isFireOsEnabled = props?.amazon?.fireOS ?? false;
|
|
482
|
+
const isVegaEnabled = props?.amazon?.vegaOS ?? false;
|
|
483
|
+
const isHorizonEnabled = isFireOsEnabled
|
|
484
|
+
? false
|
|
485
|
+
: (props?.modules?.horizon ?? false);
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
isFireOsEnabled,
|
|
489
|
+
isVegaEnabled,
|
|
490
|
+
isHorizonEnabled,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
371
494
|
const withIapIosFollyWorkaround: ConfigPlugin<IapPluginProps | undefined> = (
|
|
372
495
|
config,
|
|
373
496
|
props,
|
|
@@ -452,7 +575,14 @@ const withIapkitApiKeyIOS: ConfigPlugin<string | undefined> = (
|
|
|
452
575
|
|
|
453
576
|
const withIAP: ConfigPlugin<IapPluginProps | undefined> = (config, props) => {
|
|
454
577
|
try {
|
|
455
|
-
|
|
578
|
+
const {isFireOsEnabled, isHorizonEnabled} =
|
|
579
|
+
resolveAmazonPlatformFlags(props);
|
|
580
|
+
|
|
581
|
+
let result = withIapAndroid(config, {
|
|
582
|
+
iapkitApiKey: props?.iapkitApiKey,
|
|
583
|
+
isHorizonEnabled,
|
|
584
|
+
isFireOsEnabled,
|
|
585
|
+
});
|
|
456
586
|
result = withIapIosFollyWorkaround(result, props);
|
|
457
587
|
// Add iOS alternative billing configuration if provided
|
|
458
588
|
if (props?.iosAlternativeBilling) {
|