react-native-iap 15.4.0-rc.1 → 15.4.0-rc.3
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/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +3 -3
- package/ios/HybridRnIap.swift +1 -1
- package/openiap-versions.json +2 -2
- package/package.json +4 -10
- package/app.plugin.js +0 -1
- package/lib/typescript/plugin/src/withIAP.d.ts +0 -81
- package/lib/typescript/plugin/src/withIAP.d.ts.map +0 -1
- package/plugin/build/withIAP.d.ts +0 -80
- package/plugin/build/withIAP.js +0 -381
- package/plugin/src/withIAP.ts +0 -630
- package/plugin/tsconfig.json +0 -18
|
@@ -1477,7 +1477,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1477
1477
|
val propsMap = mutableMapOf<String, Any?>("provider" to providerString)
|
|
1478
1478
|
(params.iapkit as? Variant_NullType_NitroVerifyPurchaseWithIapkitProps.Second)?.value?.let { iapkit ->
|
|
1479
1479
|
val iapkitMap = mutableMapOf<String, Any?>()
|
|
1480
|
-
// Use provided apiKey, or fallback to AndroidManifest meta-data
|
|
1480
|
+
// Use provided apiKey, or fallback to host app AndroidManifest meta-data.
|
|
1481
1481
|
val apiKey = iapkit.apiKey.unwrapString() ?: getIapkitApiKeyFromManifest()
|
|
1482
1482
|
apiKey?.let { iapkitMap["apiKey"] = it }
|
|
1483
1483
|
(iapkit.google as? Variant_NullType_NitroVerifyPurchaseWithIapkitGoogleProps.Second)?.value?.let { google ->
|
|
@@ -2025,8 +2025,8 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
2025
2025
|
}
|
|
2026
2026
|
|
|
2027
2027
|
/**
|
|
2028
|
-
* Read IAPKit API key from AndroidManifest.xml meta-data
|
|
2029
|
-
*
|
|
2028
|
+
* Read IAPKit API key from AndroidManifest.xml meta-data.
|
|
2029
|
+
* Host app sets: <meta-data android:name="dev.iapkit.API_KEY" android:value="..." />
|
|
2030
2030
|
*/
|
|
2031
2031
|
private fun getIapkitApiKeyFromManifest(): String? {
|
|
2032
2032
|
return try {
|
package/ios/HybridRnIap.swift
CHANGED
|
@@ -434,7 +434,7 @@ class HybridRnIap: HybridRnIapSpec {
|
|
|
434
434
|
var propsDict: [String: Any] = ["provider": params.provider.stringValue]
|
|
435
435
|
if case .second(let iapkit) = params.iapkit {
|
|
436
436
|
var iapkitDict: [String: Any] = [:]
|
|
437
|
-
// Use provided apiKey, or fallback to Info.plist IAPKitAPIKey
|
|
437
|
+
// Use provided apiKey, or fallback to the host app's Info.plist IAPKitAPIKey.
|
|
438
438
|
if case .second(let apiKey) = iapkit.apiKey {
|
|
439
439
|
iapkitDict["apiKey"] = apiKey
|
|
440
440
|
} else if let plistApiKey = Bundle.main.object(forInfoDictionaryKey: "IAPKitAPIKey") as? String {
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-iap",
|
|
3
|
-
"version": "15.4.0-rc.
|
|
3
|
+
"version": "15.4.0-rc.3",
|
|
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",
|
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
"types": "./lib/typescript/src/index.d.ts",
|
|
12
12
|
"default": "./lib/module/index.js"
|
|
13
13
|
},
|
|
14
|
-
"./plugin": "./app.plugin.js",
|
|
15
|
-
"./app.plugin.js": "./app.plugin.js",
|
|
16
14
|
"./package.json": "./package.json"
|
|
17
15
|
},
|
|
18
16
|
"files": [
|
|
19
17
|
"src",
|
|
20
18
|
"lib",
|
|
21
|
-
"plugin",
|
|
22
19
|
"android",
|
|
23
20
|
"ios",
|
|
24
21
|
"cpp",
|
|
@@ -26,7 +23,6 @@
|
|
|
26
23
|
"nitro.json",
|
|
27
24
|
"openiap-versions.json",
|
|
28
25
|
"*.podspec",
|
|
29
|
-
"app.plugin.js",
|
|
30
26
|
"react-native.config.js",
|
|
31
27
|
"!ios/build",
|
|
32
28
|
"!android/build",
|
|
@@ -43,7 +39,6 @@
|
|
|
43
39
|
"!**/.*"
|
|
44
40
|
],
|
|
45
41
|
"scripts": {
|
|
46
|
-
"build:plugin": "tsc --build plugin",
|
|
47
42
|
"example": "yarn workspace rn-iap-example",
|
|
48
43
|
"example:ios": "yarn workspace rn-iap-example ios",
|
|
49
44
|
"example:android": "yarn workspace rn-iap-example android",
|
|
@@ -52,21 +47,20 @@
|
|
|
52
47
|
"typecheck:lib": "yarn tsc --noEmit",
|
|
53
48
|
"clean": "rm -rf android/build node_modules/**/android/build lib nitrogen/generated",
|
|
54
49
|
"lint": "eslint --ext .ts,.tsx,.js,.jsx src",
|
|
55
|
-
"lint:eslint": "eslint --fix 'src/**/*.{ts,tsx}'
|
|
50
|
+
"lint:eslint": "eslint --fix 'src/**/*.{ts,tsx}'",
|
|
56
51
|
"lint:prettier": "prettier --write \"**/*.{md,js,jsx,ts,tsx}\"",
|
|
57
52
|
"lint:tsc": "tsc -p tsconfig.json --noEmit --skipLibCheck",
|
|
58
53
|
"lint:ci": "yarn lint:tsc && yarn lint:eslint && yarn lint:prettier",
|
|
59
|
-
"prepare": "npx tsx scripts/check-nitro-versions.ts && bob build && yarn nitrogen
|
|
54
|
+
"prepare": "npx tsx scripts/check-nitro-versions.ts && bob build && yarn nitrogen",
|
|
60
55
|
"nitrogen": "node --stack-size=65536 node_modules/nitrogen/lib/index.js",
|
|
61
56
|
"specs": "yarn nitrogen --logLevel=\"debug\"",
|
|
62
57
|
"test": "jest --coverage",
|
|
63
58
|
"test:library": "jest --coverage",
|
|
64
59
|
"test:example": "yarn workspace rn-iap-example test --coverage",
|
|
65
|
-
"test:plugin": "jest plugin/__tests__ --runInBand --coverage",
|
|
66
60
|
"test:all": "yarn test:library && yarn test:example",
|
|
67
61
|
"test:ci": "jest --maxWorkers=2 --coverage",
|
|
68
62
|
"test:ci:example": "yarn workspace rn-iap-example test --coverage",
|
|
69
|
-
"verify:consumer-install": "node ../../scripts/verify-npm-consumer-install.mjs --package . --package-name react-native-iap --required openiap-versions.json --required lib/module/index.js --required lib/typescript/src/index.d.ts --required android/build.gradle --required NitroIap.podspec --required
|
|
63
|
+
"verify:consumer-install": "node ../../scripts/verify-npm-consumer-install.mjs --package . --package-name react-native-iap --required openiap-versions.json --required lib/module/index.js --required lib/typescript/src/index.d.ts --required android/build.gradle --required NitroIap.podspec --required nitro.json",
|
|
70
64
|
"prepare:husky": "husky install",
|
|
71
65
|
"precommit": "lint-staged",
|
|
72
66
|
"ci:check": "./scripts/ci-check.sh",
|
package/app.plugin.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('./plugin/build/withIAP.js');
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import type { ConfigPlugin } from 'expo/config-plugins';
|
|
2
|
-
import type { ExpoConfig } from '@expo/config-types';
|
|
3
|
-
export declare const modifyProjectBuildGradle: (gradle: string) => string;
|
|
4
|
-
export interface IosAlternativeBillingConfig {
|
|
5
|
-
/** Country codes where external purchases are supported (ISO 3166-1 alpha-2) */
|
|
6
|
-
countries?: string[];
|
|
7
|
-
/** External purchase URLs per country (iOS 15.4+) */
|
|
8
|
-
links?: Record<string, string>;
|
|
9
|
-
/** Multiple external purchase URLs per country (iOS 17.5+, up to 5 per country) */
|
|
10
|
-
multiLinks?: Record<string, string[]>;
|
|
11
|
-
/** Custom link regions (iOS 18.1+) */
|
|
12
|
-
customLinkRegions?: string[];
|
|
13
|
-
/** Streaming link regions for music apps (iOS 18.2+) */
|
|
14
|
-
streamingLinkRegions?: string[];
|
|
15
|
-
/** Enable external purchase link entitlement */
|
|
16
|
-
enableExternalPurchaseLink?: boolean;
|
|
17
|
-
/** Enable external purchase link streaming entitlement (music apps only) */
|
|
18
|
-
enableExternalPurchaseLinkStreaming?: boolean;
|
|
19
|
-
}
|
|
20
|
-
/** Add external purchase entitlements and Info.plist configuration */
|
|
21
|
-
declare const withIosAlternativeBilling: ConfigPlugin<IosAlternativeBillingConfig | undefined>;
|
|
22
|
-
type IapPluginProps = {
|
|
23
|
-
ios?: {
|
|
24
|
-
'with-folly-no-coroutines'?: boolean;
|
|
25
|
-
'with-folly-no-couroutines'?: boolean;
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* iOS Alternative Billing configuration.
|
|
29
|
-
* Configure external purchase countries, links, and entitlements.
|
|
30
|
-
* Requires approval from Apple.
|
|
31
|
-
* @platform ios
|
|
32
|
-
*/
|
|
33
|
-
iosAlternativeBilling?: IosAlternativeBillingConfig;
|
|
34
|
-
/**
|
|
35
|
-
* IAPKit project key for managed purchase verification.
|
|
36
|
-
* This key will be added to AndroidManifest.xml (as meta-data) and Info.plist.
|
|
37
|
-
* Get your project key from https://kit.openiap.dev
|
|
38
|
-
*/
|
|
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;
|
|
73
|
-
};
|
|
74
|
-
type IapPluginCallable = {
|
|
75
|
-
(config: ExpoConfig): ExpoConfig;
|
|
76
|
-
(config: ExpoConfig, props?: IapPluginProps): ExpoConfig;
|
|
77
|
-
};
|
|
78
|
-
declare const pluginExport: IapPluginCallable;
|
|
79
|
-
export { withIosAlternativeBilling };
|
|
80
|
-
export { pluginExport as default };
|
|
81
|
-
//# sourceMappingURL=withIAP.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"withIAP.d.ts","sourceRoot":"","sources":["../../../../plugin/src/withIAP.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AA0BnD,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,KAAG,MAWzD,CAAC;AAqOF,MAAM,WAAW,2BAA2B;IAC1C,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,wDAAwD;IACxD,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,gDAAgD;IAChD,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,4EAA4E;IAC5E,mCAAmC,CAAC,EAAE,OAAO,CAAC;CAC/C;AAED,sEAAsE;AACtE,QAAA,MAAM,yBAAyB,EAAE,YAAY,CAC3C,2BAA2B,GAAG,SAAS,CA+HxC,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,GAAG,CAAC,EAAE;QAEJ,0BAA0B,CAAC,EAAE,OAAO,CAAC;QAErC,2BAA2B,CAAC,EAAE,OAAO,CAAC;KACvC,CAAC;IACF;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,2BAA2B,CAAC;IACpD;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QACP;;;WAGG;QACH,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB;;WAEG;QACH,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR;;;WAGG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG;IAClE,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAYA;AAsHD,KAAK,iBAAiB,GAAG;IACvB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;IACjC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,cAAc,GAAG,UAAU,CAAC;CAC1D,CAAC;AAWF,QAAA,MAAM,YAAY,EAAE,iBAGyC,CAAC;AAE9D,OAAO,EAAC,yBAAyB,EAAC,CAAC;AACnC,OAAO,EAAC,YAAY,IAAI,OAAO,EAAC,CAAC"}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { ConfigPlugin } from 'expo/config-plugins';
|
|
2
|
-
import type { ExpoConfig } from '@expo/config-types';
|
|
3
|
-
export declare const modifyProjectBuildGradle: (gradle: string) => string;
|
|
4
|
-
export interface IosAlternativeBillingConfig {
|
|
5
|
-
/** Country codes where external purchases are supported (ISO 3166-1 alpha-2) */
|
|
6
|
-
countries?: string[];
|
|
7
|
-
/** External purchase URLs per country (iOS 15.4+) */
|
|
8
|
-
links?: Record<string, string>;
|
|
9
|
-
/** Multiple external purchase URLs per country (iOS 17.5+, up to 5 per country) */
|
|
10
|
-
multiLinks?: Record<string, string[]>;
|
|
11
|
-
/** Custom link regions (iOS 18.1+) */
|
|
12
|
-
customLinkRegions?: string[];
|
|
13
|
-
/** Streaming link regions for music apps (iOS 18.2+) */
|
|
14
|
-
streamingLinkRegions?: string[];
|
|
15
|
-
/** Enable external purchase link entitlement */
|
|
16
|
-
enableExternalPurchaseLink?: boolean;
|
|
17
|
-
/** Enable external purchase link streaming entitlement (music apps only) */
|
|
18
|
-
enableExternalPurchaseLinkStreaming?: boolean;
|
|
19
|
-
}
|
|
20
|
-
/** Add external purchase entitlements and Info.plist configuration */
|
|
21
|
-
declare const withIosAlternativeBilling: ConfigPlugin<IosAlternativeBillingConfig | undefined>;
|
|
22
|
-
type IapPluginProps = {
|
|
23
|
-
ios?: {
|
|
24
|
-
'with-folly-no-coroutines'?: boolean;
|
|
25
|
-
'with-folly-no-couroutines'?: boolean;
|
|
26
|
-
};
|
|
27
|
-
/**
|
|
28
|
-
* iOS Alternative Billing configuration.
|
|
29
|
-
* Configure external purchase countries, links, and entitlements.
|
|
30
|
-
* Requires approval from Apple.
|
|
31
|
-
* @platform ios
|
|
32
|
-
*/
|
|
33
|
-
iosAlternativeBilling?: IosAlternativeBillingConfig;
|
|
34
|
-
/**
|
|
35
|
-
* IAPKit project key for managed purchase verification.
|
|
36
|
-
* This key will be added to AndroidManifest.xml (as meta-data) and Info.plist.
|
|
37
|
-
* Get your project key from https://kit.openiap.dev
|
|
38
|
-
*/
|
|
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;
|
|
73
|
-
};
|
|
74
|
-
type IapPluginCallable = {
|
|
75
|
-
(config: ExpoConfig): ExpoConfig;
|
|
76
|
-
(config: ExpoConfig, props?: IapPluginProps): ExpoConfig;
|
|
77
|
-
};
|
|
78
|
-
declare const pluginExport: IapPluginCallable;
|
|
79
|
-
export { withIosAlternativeBilling };
|
|
80
|
-
export { pluginExport as default };
|
package/plugin/build/withIAP.js
DELETED
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = exports.withIosAlternativeBilling = exports.modifyProjectBuildGradle = void 0;
|
|
4
|
-
exports.resolveAmazonPlatformFlags = resolveAmazonPlatformFlags;
|
|
5
|
-
const config_plugins_1 = require("expo/config-plugins");
|
|
6
|
-
const pkg = require('../../package.json');
|
|
7
|
-
// Global flag to prevent duplicate logs
|
|
8
|
-
let hasLoggedPluginExecution = false;
|
|
9
|
-
const addLineToGradle = (content, anchor, lineToAdd, offset = 1) => {
|
|
10
|
-
const lines = content.split('\n');
|
|
11
|
-
const index = lines.findIndex((line) => line.match(anchor));
|
|
12
|
-
if (index === -1) {
|
|
13
|
-
console.warn(`Anchor "${anchor}" not found in build.gradle. Appending to end.`);
|
|
14
|
-
lines.push(lineToAdd);
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
lines.splice(index + offset, 0, lineToAdd);
|
|
18
|
-
}
|
|
19
|
-
return lines.join('\n');
|
|
20
|
-
};
|
|
21
|
-
const modifyProjectBuildGradle = (gradle) => {
|
|
22
|
-
// Keep backward-compatible behavior: add supportLibVersion inside ext { } if missing
|
|
23
|
-
if (!gradle.includes('supportLibVersion')) {
|
|
24
|
-
const lines = gradle.split('\n');
|
|
25
|
-
const extIndex = lines.findIndex((line) => line.trim() === 'ext {');
|
|
26
|
-
if (extIndex !== -1) {
|
|
27
|
-
lines.splice(extIndex + 1, 0, 'supportLibVersion = "28.0.0"');
|
|
28
|
-
return lines.join('\n');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return gradle;
|
|
32
|
-
};
|
|
33
|
-
exports.modifyProjectBuildGradle = modifyProjectBuildGradle;
|
|
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})`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
let modified = gradle;
|
|
53
|
-
let openiapVersion;
|
|
54
|
-
try {
|
|
55
|
-
openiapVersion = loadOpenIapVersion();
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `react-native-iap: Failed to resolve OpenIAP version (${error instanceof Error ? error.message : error})`);
|
|
59
|
-
return gradle;
|
|
60
|
-
}
|
|
61
|
-
// Replace legacy Billing/GMS instructions with OpenIAP Google library
|
|
62
|
-
// Remove any old billingclient or play-services-base lines we may have added previously
|
|
63
|
-
modified = modified
|
|
64
|
-
.replace(/^[ \t]*(implementation|api)[ \t]+["']com\.android\.billingclient:billing-ktx:[^"']+["'][ \t]*$/gim, '')
|
|
65
|
-
.replace(/^[ \t]*(implementation|api)[ \t]+["']com\.google\.android\.gms:play-services-base:[^"']+["'][ \t]*$/gim, '')
|
|
66
|
-
.replace(/\n{3,}/g, '\n\n');
|
|
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)) {
|
|
87
|
-
if (!/dependencies\s*{/.test(modified)) {
|
|
88
|
-
modified += `\n\ndependencies {\n${openiapDep}\n}\n`;
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep);
|
|
92
|
-
}
|
|
93
|
-
if (!hasLoggedPluginExecution) {
|
|
94
|
-
console.log(`🛠️ react-native-iap: Added OpenIAP (${openiapVersion}) to build.gradle`);
|
|
95
|
-
}
|
|
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
|
-
}
|
|
112
|
-
return modified;
|
|
113
|
-
};
|
|
114
|
-
const withIapAndroid = (config, props) => {
|
|
115
|
-
// Add OpenIAP dependency to app build.gradle
|
|
116
|
-
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
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
|
-
});
|
|
135
|
-
return config;
|
|
136
|
-
});
|
|
137
|
-
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
138
|
-
const manifest = config.modResults;
|
|
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;
|
|
146
|
-
const billingPerm = { $: { 'android:name': 'com.android.vending.BILLING' } };
|
|
147
|
-
if (props?.isFireOsEnabled) {
|
|
148
|
-
manifest.manifest['uses-permission'] = permissions.filter((p) => p.$['android:name'] !== 'com.android.vending.BILLING');
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
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
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// Add IAPKit API key as meta-data if provided
|
|
165
|
-
if (props?.iapkitApiKey) {
|
|
166
|
-
const application = manifest.manifest.application;
|
|
167
|
-
const app = application?.[0];
|
|
168
|
-
if (app) {
|
|
169
|
-
if (!app['meta-data']) {
|
|
170
|
-
app['meta-data'] = [];
|
|
171
|
-
}
|
|
172
|
-
const metaDataKey = 'dev.iapkit.API_KEY';
|
|
173
|
-
const existingMetaData = app['meta-data'].find((m) => m.$?.['android:name'] === metaDataKey);
|
|
174
|
-
if (!existingMetaData) {
|
|
175
|
-
app['meta-data'].push({
|
|
176
|
-
$: {
|
|
177
|
-
'android:name': metaDataKey,
|
|
178
|
-
'android:value': props.iapkitApiKey,
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
if (!hasLoggedPluginExecution) {
|
|
182
|
-
console.log('✅ Added IAPKit API key to AndroidManifest.xml');
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return config;
|
|
188
|
-
});
|
|
189
|
-
return config;
|
|
190
|
-
};
|
|
191
|
-
/** Add external purchase entitlements and Info.plist configuration */
|
|
192
|
-
const withIosAlternativeBilling = (config, options) => {
|
|
193
|
-
if (!options || !options.countries || options.countries.length === 0) {
|
|
194
|
-
return config;
|
|
195
|
-
}
|
|
196
|
-
// Add entitlements
|
|
197
|
-
config = (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
|
|
198
|
-
// Always add basic external purchase entitlement when countries are specified
|
|
199
|
-
config.modResults['com.apple.developer.storekit.external-purchase'] = true;
|
|
200
|
-
if (!hasLoggedPluginExecution) {
|
|
201
|
-
console.log('✅ Added com.apple.developer.storekit.external-purchase to entitlements');
|
|
202
|
-
}
|
|
203
|
-
// Add external purchase link entitlement if enabled
|
|
204
|
-
if (options.enableExternalPurchaseLink) {
|
|
205
|
-
config.modResults['com.apple.developer.storekit.external-purchase-link'] =
|
|
206
|
-
true;
|
|
207
|
-
if (!hasLoggedPluginExecution) {
|
|
208
|
-
console.log('✅ Added com.apple.developer.storekit.external-purchase-link to entitlements');
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
// Add streaming entitlement if enabled
|
|
212
|
-
if (options.enableExternalPurchaseLinkStreaming) {
|
|
213
|
-
config.modResults['com.apple.developer.storekit.external-purchase-link-streaming'] = true;
|
|
214
|
-
if (!hasLoggedPluginExecution) {
|
|
215
|
-
console.log('✅ Added com.apple.developer.storekit.external-purchase-link-streaming to entitlements');
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return config;
|
|
219
|
-
});
|
|
220
|
-
// Add Info.plist configuration
|
|
221
|
-
config = (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
222
|
-
const plist = config.modResults;
|
|
223
|
-
// Helper function to normalize country codes to uppercase
|
|
224
|
-
const normalize = (code) => code.trim().toUpperCase();
|
|
225
|
-
// 1. SKExternalPurchase (Required)
|
|
226
|
-
const normalizedCountries = options.countries?.map(normalize);
|
|
227
|
-
plist.SKExternalPurchase = normalizedCountries;
|
|
228
|
-
if (!hasLoggedPluginExecution) {
|
|
229
|
-
console.log(`✅ Added SKExternalPurchase with countries: ${normalizedCountries?.join(', ')}`);
|
|
230
|
-
}
|
|
231
|
-
// 2. SKExternalPurchaseLink (Optional - iOS 15.4+)
|
|
232
|
-
if (options.links && Object.keys(options.links).length > 0) {
|
|
233
|
-
plist.SKExternalPurchaseLink = Object.fromEntries(Object.entries(options.links).map(([code, url]) => [
|
|
234
|
-
normalize(code),
|
|
235
|
-
url,
|
|
236
|
-
]));
|
|
237
|
-
if (!hasLoggedPluginExecution) {
|
|
238
|
-
console.log(`✅ Added SKExternalPurchaseLink for ${Object.keys(options.links).length} countries`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
// 3. SKExternalPurchaseMultiLink (iOS 17.5+)
|
|
242
|
-
if (options.multiLinks && Object.keys(options.multiLinks).length > 0) {
|
|
243
|
-
plist.SKExternalPurchaseMultiLink = Object.fromEntries(Object.entries(options.multiLinks).map(([code, urls]) => [
|
|
244
|
-
normalize(code),
|
|
245
|
-
urls,
|
|
246
|
-
]));
|
|
247
|
-
if (!hasLoggedPluginExecution) {
|
|
248
|
-
console.log(`✅ Added SKExternalPurchaseMultiLink for ${Object.keys(options.multiLinks).length} countries`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
// 4. SKExternalPurchaseCustomLinkRegions (iOS 18.1+)
|
|
252
|
-
if (options.customLinkRegions && options.customLinkRegions.length > 0) {
|
|
253
|
-
plist.SKExternalPurchaseCustomLinkRegions =
|
|
254
|
-
options.customLinkRegions.map(normalize);
|
|
255
|
-
if (!hasLoggedPluginExecution) {
|
|
256
|
-
console.log(`✅ Added SKExternalPurchaseCustomLinkRegions: ${options.customLinkRegions
|
|
257
|
-
.map(normalize)
|
|
258
|
-
.join(', ')}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
// 5. SKExternalPurchaseLinkStreamingRegions (iOS 18.2+)
|
|
262
|
-
if (options.streamingLinkRegions &&
|
|
263
|
-
options.streamingLinkRegions.length > 0) {
|
|
264
|
-
plist.SKExternalPurchaseLinkStreamingRegions =
|
|
265
|
-
options.streamingLinkRegions.map(normalize);
|
|
266
|
-
if (!hasLoggedPluginExecution) {
|
|
267
|
-
console.log(`✅ Added SKExternalPurchaseLinkStreamingRegions: ${options.streamingLinkRegions
|
|
268
|
-
.map(normalize)
|
|
269
|
-
.join(', ')}`);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return config;
|
|
273
|
-
});
|
|
274
|
-
return config;
|
|
275
|
-
};
|
|
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
|
-
}
|
|
289
|
-
const withIapIosFollyWorkaround = (config, props) => {
|
|
290
|
-
const newKey = props?.ios?.['with-folly-no-coroutines'];
|
|
291
|
-
const oldKey = props?.ios?.['with-folly-no-couroutines'];
|
|
292
|
-
if (oldKey && !hasLoggedPluginExecution) {
|
|
293
|
-
// Temporary deprecation notice; remove when old key is dropped
|
|
294
|
-
config_plugins_1.WarningAggregator.addWarningIOS('react-native-iap', "react-native-iap: 'ios.with-folly-no-couroutines' is deprecated; use 'ios.with-folly-no-coroutines'.");
|
|
295
|
-
}
|
|
296
|
-
const enabled = !!(newKey ?? oldKey);
|
|
297
|
-
if (!enabled)
|
|
298
|
-
return config;
|
|
299
|
-
return (0, config_plugins_1.withPodfile)(config, (config) => {
|
|
300
|
-
let contents = config.modResults.contents;
|
|
301
|
-
// Idempotency: if any of the defines already exists, assume it's applied
|
|
302
|
-
if (contents.includes('FOLLY_CFG_NO_COROUTINES') ||
|
|
303
|
-
contents.includes('FOLLY_HAS_COROUTINES=0')) {
|
|
304
|
-
return config;
|
|
305
|
-
}
|
|
306
|
-
const anchor = 'post_install do |installer|';
|
|
307
|
-
const snippet = `
|
|
308
|
-
# react-native-iap (expo): Disable Folly coroutines to avoid including non-vendored <folly/coro/*> headers
|
|
309
|
-
installer.pods_project.targets.each do |target|
|
|
310
|
-
target.build_configurations.each do |config|
|
|
311
|
-
defs = (config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] || ['$(inherited)'])
|
|
312
|
-
defs << 'FOLLY_NO_CONFIG=1' unless defs.any? { |d| d.to_s.include?('FOLLY_NO_CONFIG') }
|
|
313
|
-
# Portability.h respects FOLLY_CFG_NO_COROUTINES to fully disable coroutine support
|
|
314
|
-
defs << 'FOLLY_CFG_NO_COROUTINES=1' unless defs.any? { |d| d.to_s.include?('FOLLY_CFG_NO_COROUTINES') }
|
|
315
|
-
defs << 'FOLLY_HAS_COROUTINES=0' unless defs.any? { |d| d.to_s.include?('FOLLY_HAS_COROUTINES') }
|
|
316
|
-
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = defs
|
|
317
|
-
end
|
|
318
|
-
end`;
|
|
319
|
-
if (contents.includes(anchor)) {
|
|
320
|
-
contents = contents.replace(anchor, `${anchor}\n${snippet}`);
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
// As a fallback, append a new post_install block
|
|
324
|
-
contents += `
|
|
325
|
-
|
|
326
|
-
${anchor}
|
|
327
|
-
${snippet}
|
|
328
|
-
end
|
|
329
|
-
`;
|
|
330
|
-
}
|
|
331
|
-
config.modResults.contents = contents;
|
|
332
|
-
return config;
|
|
333
|
-
});
|
|
334
|
-
};
|
|
335
|
-
/** Add IAPKit API key to iOS Info.plist */
|
|
336
|
-
const withIapkitApiKeyIOS = (config, apiKey) => {
|
|
337
|
-
if (!apiKey) {
|
|
338
|
-
return config;
|
|
339
|
-
}
|
|
340
|
-
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
341
|
-
const plist = config.modResults;
|
|
342
|
-
const plistKey = 'IAPKitAPIKey';
|
|
343
|
-
if (!plist[plistKey]) {
|
|
344
|
-
plist[plistKey] = apiKey;
|
|
345
|
-
if (!hasLoggedPluginExecution) {
|
|
346
|
-
console.log('✅ Added IAPKit API key to Info.plist');
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return config;
|
|
350
|
-
});
|
|
351
|
-
};
|
|
352
|
-
const withIAP = (config, props) => {
|
|
353
|
-
try {
|
|
354
|
-
const { isFireOsEnabled, isHorizonEnabled } = resolveAmazonPlatformFlags(props);
|
|
355
|
-
let result = withIapAndroid(config, {
|
|
356
|
-
iapkitApiKey: props?.iapkitApiKey,
|
|
357
|
-
isHorizonEnabled,
|
|
358
|
-
isFireOsEnabled,
|
|
359
|
-
});
|
|
360
|
-
result = withIapIosFollyWorkaround(result, props);
|
|
361
|
-
// Add iOS alternative billing configuration if provided
|
|
362
|
-
if (props?.iosAlternativeBilling) {
|
|
363
|
-
result = withIosAlternativeBilling(result, props.iosAlternativeBilling);
|
|
364
|
-
}
|
|
365
|
-
// Add IAPKit API key to iOS Info.plist if provided
|
|
366
|
-
if (props?.iapkitApiKey) {
|
|
367
|
-
result = withIapkitApiKeyIOS(result, props.iapkitApiKey);
|
|
368
|
-
}
|
|
369
|
-
// Set flag after first execution to prevent duplicate logs
|
|
370
|
-
hasLoggedPluginExecution = true;
|
|
371
|
-
return result;
|
|
372
|
-
}
|
|
373
|
-
catch (error) {
|
|
374
|
-
config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `react-native-iap plugin encountered an error: ${error}`);
|
|
375
|
-
console.error('react-native-iap plugin error:', error);
|
|
376
|
-
return config;
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
const _wrapped = (0, config_plugins_1.createRunOncePlugin)(withIAP, pkg.name, pkg.version);
|
|
380
|
-
const pluginExport = ((config, props) => _wrapped(config, props));
|
|
381
|
-
exports.default = pluginExport;
|
package/plugin/src/withIAP.ts
DELETED
|
@@ -1,630 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createRunOncePlugin,
|
|
3
|
-
WarningAggregator,
|
|
4
|
-
withAndroidManifest,
|
|
5
|
-
withAppBuildGradle,
|
|
6
|
-
withGradleProperties,
|
|
7
|
-
withPodfile,
|
|
8
|
-
withEntitlementsPlist,
|
|
9
|
-
withInfoPlist,
|
|
10
|
-
} from 'expo/config-plugins';
|
|
11
|
-
import type {ConfigPlugin} from 'expo/config-plugins';
|
|
12
|
-
import type {ExpoConfig} from '@expo/config-types';
|
|
13
|
-
|
|
14
|
-
const pkg = require('../../package.json');
|
|
15
|
-
|
|
16
|
-
// Global flag to prevent duplicate logs
|
|
17
|
-
let hasLoggedPluginExecution = false;
|
|
18
|
-
|
|
19
|
-
const addLineToGradle = (
|
|
20
|
-
content: string,
|
|
21
|
-
anchor: RegExp | string,
|
|
22
|
-
lineToAdd: string,
|
|
23
|
-
offset: number = 1,
|
|
24
|
-
): string => {
|
|
25
|
-
const lines = content.split('\n');
|
|
26
|
-
const index = lines.findIndex((line) => line.match(anchor));
|
|
27
|
-
if (index === -1) {
|
|
28
|
-
console.warn(
|
|
29
|
-
`Anchor "${anchor}" not found in build.gradle. Appending to end.`,
|
|
30
|
-
);
|
|
31
|
-
lines.push(lineToAdd);
|
|
32
|
-
} else {
|
|
33
|
-
lines.splice(index + offset, 0, lineToAdd);
|
|
34
|
-
}
|
|
35
|
-
return lines.join('\n');
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const modifyProjectBuildGradle = (gradle: string): string => {
|
|
39
|
-
// Keep backward-compatible behavior: add supportLibVersion inside ext { } if missing
|
|
40
|
-
if (!gradle.includes('supportLibVersion')) {
|
|
41
|
-
const lines = gradle.split('\n');
|
|
42
|
-
const extIndex = lines.findIndex((line) => line.trim() === 'ext {');
|
|
43
|
-
if (extIndex !== -1) {
|
|
44
|
-
lines.splice(extIndex + 1, 0, 'supportLibVersion = "28.0.0"');
|
|
45
|
-
return lines.join('\n');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return gradle;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const OPENIAP_GROUP = 'io.github.hyochan.openiap';
|
|
52
|
-
|
|
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) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`react-native-iap: Unable to load openiap-versions.json (${error instanceof Error ? error.message : error})`,
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let modified = gradle;
|
|
81
|
-
|
|
82
|
-
let openiapVersion: string;
|
|
83
|
-
try {
|
|
84
|
-
openiapVersion = loadOpenIapVersion();
|
|
85
|
-
} catch (error) {
|
|
86
|
-
WarningAggregator.addWarningAndroid(
|
|
87
|
-
'react-native-iap',
|
|
88
|
-
`react-native-iap: Failed to resolve OpenIAP version (${error instanceof Error ? error.message : error})`,
|
|
89
|
-
);
|
|
90
|
-
return gradle;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Replace legacy Billing/GMS instructions with OpenIAP Google library
|
|
94
|
-
// Remove any old billingclient or play-services-base lines we may have added previously
|
|
95
|
-
modified = modified
|
|
96
|
-
.replace(
|
|
97
|
-
/^[ \t]*(implementation|api)[ \t]+["']com\.android\.billingclient:billing-ktx:[^"']+["'][ \t]*$/gim,
|
|
98
|
-
'',
|
|
99
|
-
)
|
|
100
|
-
.replace(
|
|
101
|
-
/^[ \t]*(implementation|api)[ \t]+["']com\.google\.android\.gms:play-services-base:[^"']+["'][ \t]*$/gim,
|
|
102
|
-
'',
|
|
103
|
-
)
|
|
104
|
-
.replace(/\n{3,}/g, '\n\n');
|
|
105
|
-
|
|
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
|
-
}
|
|
132
|
-
|
|
133
|
-
if (!desiredOpeniapLine.test(modified)) {
|
|
134
|
-
if (!/dependencies\s*{/.test(modified)) {
|
|
135
|
-
modified += `\n\ndependencies {\n${openiapDep}\n}\n`;
|
|
136
|
-
} else {
|
|
137
|
-
modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep);
|
|
138
|
-
}
|
|
139
|
-
if (!hasLoggedPluginExecution) {
|
|
140
|
-
console.log(
|
|
141
|
-
`🛠️ react-native-iap: Added OpenIAP (${openiapVersion}) to build.gradle`,
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
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
|
-
|
|
164
|
-
return modified;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const withIapAndroid: ConfigPlugin<
|
|
168
|
-
| {
|
|
169
|
-
iapkitApiKey?: string;
|
|
170
|
-
isHorizonEnabled?: boolean;
|
|
171
|
-
isFireOsEnabled?: boolean;
|
|
172
|
-
}
|
|
173
|
-
| undefined
|
|
174
|
-
> = (config, props) => {
|
|
175
|
-
// Add OpenIAP dependency to app build.gradle
|
|
176
|
-
config = withAppBuildGradle(config, (config) => {
|
|
177
|
-
config.modResults.contents = modifyAppBuildGradle(
|
|
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),
|
|
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
|
-
|
|
206
|
-
return config;
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
config = withAndroidManifest(config, (config) => {
|
|
210
|
-
const manifest = config.modResults;
|
|
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;
|
|
218
|
-
const billingPerm = {$: {'android:name': 'com.android.vending.BILLING'}};
|
|
219
|
-
|
|
220
|
-
if (props?.isFireOsEnabled) {
|
|
221
|
-
manifest.manifest['uses-permission'] = permissions.filter(
|
|
222
|
-
(p) => p.$['android:name'] !== 'com.android.vending.BILLING',
|
|
223
|
-
);
|
|
224
|
-
} else {
|
|
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
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Add IAPKit API key as meta-data if provided
|
|
245
|
-
if (props?.iapkitApiKey) {
|
|
246
|
-
const application = manifest.manifest.application;
|
|
247
|
-
const app = application?.[0];
|
|
248
|
-
if (app) {
|
|
249
|
-
if (!app['meta-data']) {
|
|
250
|
-
app['meta-data'] = [];
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const metaDataKey = 'dev.iapkit.API_KEY';
|
|
254
|
-
const existingMetaData = (
|
|
255
|
-
app['meta-data'] as {$?: {'android:name'?: string}}[]
|
|
256
|
-
).find((m) => m.$?.['android:name'] === metaDataKey);
|
|
257
|
-
|
|
258
|
-
if (!existingMetaData) {
|
|
259
|
-
app['meta-data'].push({
|
|
260
|
-
$: {
|
|
261
|
-
'android:name': metaDataKey,
|
|
262
|
-
'android:value': props.iapkitApiKey,
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
if (!hasLoggedPluginExecution) {
|
|
266
|
-
console.log('✅ Added IAPKit API key to AndroidManifest.xml');
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return config;
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
return config;
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
export interface IosAlternativeBillingConfig {
|
|
279
|
-
/** Country codes where external purchases are supported (ISO 3166-1 alpha-2) */
|
|
280
|
-
countries?: string[];
|
|
281
|
-
/** External purchase URLs per country (iOS 15.4+) */
|
|
282
|
-
links?: Record<string, string>;
|
|
283
|
-
/** Multiple external purchase URLs per country (iOS 17.5+, up to 5 per country) */
|
|
284
|
-
multiLinks?: Record<string, string[]>;
|
|
285
|
-
/** Custom link regions (iOS 18.1+) */
|
|
286
|
-
customLinkRegions?: string[];
|
|
287
|
-
/** Streaming link regions for music apps (iOS 18.2+) */
|
|
288
|
-
streamingLinkRegions?: string[];
|
|
289
|
-
/** Enable external purchase link entitlement */
|
|
290
|
-
enableExternalPurchaseLink?: boolean;
|
|
291
|
-
/** Enable external purchase link streaming entitlement (music apps only) */
|
|
292
|
-
enableExternalPurchaseLinkStreaming?: boolean;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/** Add external purchase entitlements and Info.plist configuration */
|
|
296
|
-
const withIosAlternativeBilling: ConfigPlugin<
|
|
297
|
-
IosAlternativeBillingConfig | undefined
|
|
298
|
-
> = (config, options) => {
|
|
299
|
-
if (!options || !options.countries || options.countries.length === 0) {
|
|
300
|
-
return config;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Add entitlements
|
|
304
|
-
config = withEntitlementsPlist(config, (config) => {
|
|
305
|
-
// Always add basic external purchase entitlement when countries are specified
|
|
306
|
-
config.modResults['com.apple.developer.storekit.external-purchase'] = true;
|
|
307
|
-
if (!hasLoggedPluginExecution) {
|
|
308
|
-
console.log(
|
|
309
|
-
'✅ Added com.apple.developer.storekit.external-purchase to entitlements',
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Add external purchase link entitlement if enabled
|
|
314
|
-
if (options.enableExternalPurchaseLink) {
|
|
315
|
-
config.modResults['com.apple.developer.storekit.external-purchase-link'] =
|
|
316
|
-
true;
|
|
317
|
-
if (!hasLoggedPluginExecution) {
|
|
318
|
-
console.log(
|
|
319
|
-
'✅ Added com.apple.developer.storekit.external-purchase-link to entitlements',
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Add streaming entitlement if enabled
|
|
325
|
-
if (options.enableExternalPurchaseLinkStreaming) {
|
|
326
|
-
config.modResults[
|
|
327
|
-
'com.apple.developer.storekit.external-purchase-link-streaming'
|
|
328
|
-
] = true;
|
|
329
|
-
if (!hasLoggedPluginExecution) {
|
|
330
|
-
console.log(
|
|
331
|
-
'✅ Added com.apple.developer.storekit.external-purchase-link-streaming to entitlements',
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return config;
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Add Info.plist configuration
|
|
340
|
-
config = withInfoPlist(config, (config) => {
|
|
341
|
-
const plist = config.modResults;
|
|
342
|
-
|
|
343
|
-
// Helper function to normalize country codes to uppercase
|
|
344
|
-
const normalize = (code: string) => code.trim().toUpperCase();
|
|
345
|
-
|
|
346
|
-
// 1. SKExternalPurchase (Required)
|
|
347
|
-
const normalizedCountries = options.countries?.map(normalize);
|
|
348
|
-
plist.SKExternalPurchase = normalizedCountries;
|
|
349
|
-
if (!hasLoggedPluginExecution) {
|
|
350
|
-
console.log(
|
|
351
|
-
`✅ Added SKExternalPurchase with countries: ${normalizedCountries?.join(
|
|
352
|
-
', ',
|
|
353
|
-
)}`,
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// 2. SKExternalPurchaseLink (Optional - iOS 15.4+)
|
|
358
|
-
if (options.links && Object.keys(options.links).length > 0) {
|
|
359
|
-
plist.SKExternalPurchaseLink = Object.fromEntries(
|
|
360
|
-
Object.entries(options.links).map(([code, url]) => [
|
|
361
|
-
normalize(code),
|
|
362
|
-
url,
|
|
363
|
-
]),
|
|
364
|
-
);
|
|
365
|
-
if (!hasLoggedPluginExecution) {
|
|
366
|
-
console.log(
|
|
367
|
-
`✅ Added SKExternalPurchaseLink for ${
|
|
368
|
-
Object.keys(options.links).length
|
|
369
|
-
} countries`,
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// 3. SKExternalPurchaseMultiLink (iOS 17.5+)
|
|
375
|
-
if (options.multiLinks && Object.keys(options.multiLinks).length > 0) {
|
|
376
|
-
plist.SKExternalPurchaseMultiLink = Object.fromEntries(
|
|
377
|
-
Object.entries(options.multiLinks).map(([code, urls]) => [
|
|
378
|
-
normalize(code),
|
|
379
|
-
urls,
|
|
380
|
-
]),
|
|
381
|
-
);
|
|
382
|
-
if (!hasLoggedPluginExecution) {
|
|
383
|
-
console.log(
|
|
384
|
-
`✅ Added SKExternalPurchaseMultiLink for ${
|
|
385
|
-
Object.keys(options.multiLinks).length
|
|
386
|
-
} countries`,
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// 4. SKExternalPurchaseCustomLinkRegions (iOS 18.1+)
|
|
392
|
-
if (options.customLinkRegions && options.customLinkRegions.length > 0) {
|
|
393
|
-
plist.SKExternalPurchaseCustomLinkRegions =
|
|
394
|
-
options.customLinkRegions.map(normalize);
|
|
395
|
-
if (!hasLoggedPluginExecution) {
|
|
396
|
-
console.log(
|
|
397
|
-
`✅ Added SKExternalPurchaseCustomLinkRegions: ${options.customLinkRegions
|
|
398
|
-
.map(normalize)
|
|
399
|
-
.join(', ')}`,
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// 5. SKExternalPurchaseLinkStreamingRegions (iOS 18.2+)
|
|
405
|
-
if (
|
|
406
|
-
options.streamingLinkRegions &&
|
|
407
|
-
options.streamingLinkRegions.length > 0
|
|
408
|
-
) {
|
|
409
|
-
plist.SKExternalPurchaseLinkStreamingRegions =
|
|
410
|
-
options.streamingLinkRegions.map(normalize);
|
|
411
|
-
if (!hasLoggedPluginExecution) {
|
|
412
|
-
console.log(
|
|
413
|
-
`✅ Added SKExternalPurchaseLinkStreamingRegions: ${options.streamingLinkRegions
|
|
414
|
-
.map(normalize)
|
|
415
|
-
.join(', ')}`,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return config;
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
return config;
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
type IapPluginProps = {
|
|
427
|
-
ios?: {
|
|
428
|
-
// Enable to inject Folly coroutine-disabling macros into Podfile during prebuild
|
|
429
|
-
'with-folly-no-coroutines'?: boolean;
|
|
430
|
-
// @deprecated Use 'with-folly-no-coroutines'. Kept for backward compatibility.
|
|
431
|
-
'with-folly-no-couroutines'?: boolean;
|
|
432
|
-
};
|
|
433
|
-
/**
|
|
434
|
-
* iOS Alternative Billing configuration.
|
|
435
|
-
* Configure external purchase countries, links, and entitlements.
|
|
436
|
-
* Requires approval from Apple.
|
|
437
|
-
* @platform ios
|
|
438
|
-
*/
|
|
439
|
-
iosAlternativeBilling?: IosAlternativeBillingConfig;
|
|
440
|
-
/**
|
|
441
|
-
* IAPKit project key for managed purchase verification.
|
|
442
|
-
* This key will be added to AndroidManifest.xml (as meta-data) and Info.plist.
|
|
443
|
-
* Get your project key from https://kit.openiap.dev
|
|
444
|
-
*/
|
|
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
|
-
};
|
|
474
|
-
};
|
|
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
|
-
|
|
494
|
-
const withIapIosFollyWorkaround: ConfigPlugin<IapPluginProps | undefined> = (
|
|
495
|
-
config,
|
|
496
|
-
props,
|
|
497
|
-
) => {
|
|
498
|
-
const newKey = props?.ios?.['with-folly-no-coroutines'];
|
|
499
|
-
const oldKey = props?.ios?.['with-folly-no-couroutines'];
|
|
500
|
-
if (oldKey && !hasLoggedPluginExecution) {
|
|
501
|
-
// Temporary deprecation notice; remove when old key is dropped
|
|
502
|
-
WarningAggregator.addWarningIOS(
|
|
503
|
-
'react-native-iap',
|
|
504
|
-
"react-native-iap: 'ios.with-folly-no-couroutines' is deprecated; use 'ios.with-folly-no-coroutines'.",
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
const enabled = !!(newKey ?? oldKey);
|
|
508
|
-
if (!enabled) return config;
|
|
509
|
-
|
|
510
|
-
return withPodfile(config, (config) => {
|
|
511
|
-
let contents = config.modResults.contents;
|
|
512
|
-
|
|
513
|
-
// Idempotency: if any of the defines already exists, assume it's applied
|
|
514
|
-
if (
|
|
515
|
-
contents.includes('FOLLY_CFG_NO_COROUTINES') ||
|
|
516
|
-
contents.includes('FOLLY_HAS_COROUTINES=0')
|
|
517
|
-
) {
|
|
518
|
-
return config;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
const anchor = 'post_install do |installer|';
|
|
522
|
-
const snippet = `
|
|
523
|
-
# react-native-iap (expo): Disable Folly coroutines to avoid including non-vendored <folly/coro/*> headers
|
|
524
|
-
installer.pods_project.targets.each do |target|
|
|
525
|
-
target.build_configurations.each do |config|
|
|
526
|
-
defs = (config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] || ['$(inherited)'])
|
|
527
|
-
defs << 'FOLLY_NO_CONFIG=1' unless defs.any? { |d| d.to_s.include?('FOLLY_NO_CONFIG') }
|
|
528
|
-
# Portability.h respects FOLLY_CFG_NO_COROUTINES to fully disable coroutine support
|
|
529
|
-
defs << 'FOLLY_CFG_NO_COROUTINES=1' unless defs.any? { |d| d.to_s.include?('FOLLY_CFG_NO_COROUTINES') }
|
|
530
|
-
defs << 'FOLLY_HAS_COROUTINES=0' unless defs.any? { |d| d.to_s.include?('FOLLY_HAS_COROUTINES') }
|
|
531
|
-
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = defs
|
|
532
|
-
end
|
|
533
|
-
end`;
|
|
534
|
-
|
|
535
|
-
if (contents.includes(anchor)) {
|
|
536
|
-
contents = contents.replace(anchor, `${anchor}\n${snippet}`);
|
|
537
|
-
} else {
|
|
538
|
-
// As a fallback, append a new post_install block
|
|
539
|
-
contents += `
|
|
540
|
-
|
|
541
|
-
${anchor}
|
|
542
|
-
${snippet}
|
|
543
|
-
end
|
|
544
|
-
`;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
config.modResults.contents = contents;
|
|
548
|
-
return config;
|
|
549
|
-
});
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
/** Add IAPKit API key to iOS Info.plist */
|
|
553
|
-
const withIapkitApiKeyIOS: ConfigPlugin<string | undefined> = (
|
|
554
|
-
config,
|
|
555
|
-
apiKey,
|
|
556
|
-
) => {
|
|
557
|
-
if (!apiKey) {
|
|
558
|
-
return config;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
return withInfoPlist(config, (config) => {
|
|
562
|
-
const plist = config.modResults;
|
|
563
|
-
const plistKey = 'IAPKitAPIKey';
|
|
564
|
-
|
|
565
|
-
if (!plist[plistKey]) {
|
|
566
|
-
plist[plistKey] = apiKey;
|
|
567
|
-
if (!hasLoggedPluginExecution) {
|
|
568
|
-
console.log('✅ Added IAPKit API key to Info.plist');
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return config;
|
|
573
|
-
});
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
const withIAP: ConfigPlugin<IapPluginProps | undefined> = (config, props) => {
|
|
577
|
-
try {
|
|
578
|
-
const {isFireOsEnabled, isHorizonEnabled} =
|
|
579
|
-
resolveAmazonPlatformFlags(props);
|
|
580
|
-
|
|
581
|
-
let result = withIapAndroid(config, {
|
|
582
|
-
iapkitApiKey: props?.iapkitApiKey,
|
|
583
|
-
isHorizonEnabled,
|
|
584
|
-
isFireOsEnabled,
|
|
585
|
-
});
|
|
586
|
-
result = withIapIosFollyWorkaround(result, props);
|
|
587
|
-
// Add iOS alternative billing configuration if provided
|
|
588
|
-
if (props?.iosAlternativeBilling) {
|
|
589
|
-
result = withIosAlternativeBilling(result, props.iosAlternativeBilling);
|
|
590
|
-
}
|
|
591
|
-
// Add IAPKit API key to iOS Info.plist if provided
|
|
592
|
-
if (props?.iapkitApiKey) {
|
|
593
|
-
result = withIapkitApiKeyIOS(result, props.iapkitApiKey);
|
|
594
|
-
}
|
|
595
|
-
// Set flag after first execution to prevent duplicate logs
|
|
596
|
-
hasLoggedPluginExecution = true;
|
|
597
|
-
return result;
|
|
598
|
-
} catch (error) {
|
|
599
|
-
WarningAggregator.addWarningAndroid(
|
|
600
|
-
'react-native-iap',
|
|
601
|
-
`react-native-iap plugin encountered an error: ${error}`,
|
|
602
|
-
);
|
|
603
|
-
console.error('react-native-iap plugin error:', error);
|
|
604
|
-
return config;
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
// Standard Expo config plugin export
|
|
609
|
-
// Export a test-friendly wrapper that accepts 1 or 2 args
|
|
610
|
-
type IapPluginCallable = {
|
|
611
|
-
(config: ExpoConfig): ExpoConfig;
|
|
612
|
-
(config: ExpoConfig, props?: IapPluginProps): ExpoConfig;
|
|
613
|
-
};
|
|
614
|
-
|
|
615
|
-
const _wrapped = createRunOncePlugin(
|
|
616
|
-
withIAP,
|
|
617
|
-
pkg.name,
|
|
618
|
-
pkg.version,
|
|
619
|
-
) as unknown as (
|
|
620
|
-
config: ExpoConfig,
|
|
621
|
-
props: IapPluginProps | undefined,
|
|
622
|
-
) => ExpoConfig;
|
|
623
|
-
|
|
624
|
-
const pluginExport: IapPluginCallable = ((
|
|
625
|
-
config: ExpoConfig,
|
|
626
|
-
props?: IapPluginProps,
|
|
627
|
-
) => _wrapped(config, props)) as unknown as IapPluginCallable;
|
|
628
|
-
|
|
629
|
-
export {withIosAlternativeBilling};
|
|
630
|
-
export {pluginExport as default};
|
package/plugin/tsconfig.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"lib": ["ES2020"],
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"outDir": "./build",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"moduleResolution": "node"
|
|
15
|
-
},
|
|
16
|
-
"include": ["src/**/*"],
|
|
17
|
-
"exclude": ["node_modules", "build"]
|
|
18
|
-
}
|