react-native-iap 14.4.34 â 14.4.35-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/android/build.gradle +5 -0
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +42 -26
- package/android/src/test/java/com/margelo/nitro/iap/OpenIapExceptionTest.kt +93 -0
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/package.json +1 -1
- package/plugin/build/src/withIAP.d.ts +3 -0
- package/plugin/build/src/withIAP.js +81 -0
- package/plugin/build/tsconfig.tsbuildinfo +1 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
package/android/build.gradle
CHANGED
|
@@ -194,6 +194,11 @@ dependencies {
|
|
|
194
194
|
// Use standard Google Play Billing
|
|
195
195
|
implementation "io.github.hyochan.openiap:openiap-google:${googleVersionString}"
|
|
196
196
|
}
|
|
197
|
+
|
|
198
|
+
// Test dependencies
|
|
199
|
+
testImplementation 'junit:junit:4.13.2'
|
|
200
|
+
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
|
201
|
+
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
|
|
197
202
|
}
|
|
198
203
|
|
|
199
204
|
configurations.all {
|
|
@@ -37,6 +37,22 @@ import org.json.JSONArray
|
|
|
37
37
|
import org.json.JSONObject
|
|
38
38
|
import java.util.Locale
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Custom exception for OpenIAP errors that only includes the error JSON without stack traces.
|
|
42
|
+
* This ensures clean error messages are passed to JavaScript without Java/Kotlin stack traces.
|
|
43
|
+
*/
|
|
44
|
+
class OpenIapException(private val errorJson: String) : Exception() {
|
|
45
|
+
override val message: String
|
|
46
|
+
get() = errorJson
|
|
47
|
+
|
|
48
|
+
override fun toString(): String = errorJson
|
|
49
|
+
|
|
50
|
+
override fun fillInStackTrace(): Throwable {
|
|
51
|
+
// Don't fill in stack trace to avoid it being serialized
|
|
52
|
+
return this
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
class HybridRnIap : HybridRnIapSpec() {
|
|
41
57
|
|
|
42
58
|
// Get ReactApplicationContext lazily from NitroModules
|
|
@@ -167,7 +183,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
167
183
|
} catch (err: Throwable) {
|
|
168
184
|
val error = OpenIAPError.InitConnection
|
|
169
185
|
RnIapLog.failure("initConnection.native", err)
|
|
170
|
-
throw
|
|
186
|
+
throw OpenIapException(
|
|
171
187
|
toErrorJson(
|
|
172
188
|
error = error,
|
|
173
189
|
debugMessage = err.message,
|
|
@@ -178,7 +194,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
178
194
|
if (!ok) {
|
|
179
195
|
val error = OpenIAPError.InitConnection
|
|
180
196
|
RnIapLog.failure("initConnection.native", Exception(error.message))
|
|
181
|
-
throw
|
|
197
|
+
throw OpenIapException(
|
|
182
198
|
toErrorJson(
|
|
183
199
|
error = error,
|
|
184
200
|
messageOverride = "Failed to initialize connection"
|
|
@@ -225,7 +241,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
225
241
|
)
|
|
226
242
|
|
|
227
243
|
if (skus.isEmpty()) {
|
|
228
|
-
throw
|
|
244
|
+
throw OpenIapException(toErrorJson(OpenIAPError.EmptySkuList))
|
|
229
245
|
}
|
|
230
246
|
|
|
231
247
|
initConnection(null).await()
|
|
@@ -528,7 +544,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
528
544
|
} catch (e: Exception) {
|
|
529
545
|
RnIapLog.failure("getActiveSubscriptions", e)
|
|
530
546
|
val error = OpenIAPError.ServiceUnavailable
|
|
531
|
-
throw
|
|
547
|
+
throw OpenIapException(
|
|
532
548
|
toErrorJson(
|
|
533
549
|
error = error,
|
|
534
550
|
debugMessage = e.message,
|
|
@@ -937,14 +953,14 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
937
953
|
// iOS-specific method - not supported on Android
|
|
938
954
|
override fun getStorefrontIOS(): Promise<String> {
|
|
939
955
|
return Promise.async {
|
|
940
|
-
throw
|
|
956
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
941
957
|
}
|
|
942
958
|
}
|
|
943
959
|
|
|
944
960
|
// iOS-specific method - not supported on Android
|
|
945
961
|
override fun getAppTransactionIOS(): Promise<String?> {
|
|
946
962
|
return Promise.async {
|
|
947
|
-
throw
|
|
963
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
948
964
|
}
|
|
949
965
|
}
|
|
950
966
|
|
|
@@ -1031,7 +1047,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1031
1047
|
try {
|
|
1032
1048
|
// For Android, we need the androidOptions to be provided
|
|
1033
1049
|
val androidOptions = params.androidOptions
|
|
1034
|
-
?: throw
|
|
1050
|
+
?: throw OpenIapException(toErrorJson(OpenIAPError.DeveloperError))
|
|
1035
1051
|
|
|
1036
1052
|
// Android receipt validation would typically involve server-side validation
|
|
1037
1053
|
// using Google Play Developer API. Here we provide a simplified implementation
|
|
@@ -1070,7 +1086,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1070
1086
|
} catch (e: Exception) {
|
|
1071
1087
|
val debugMessage = e.message
|
|
1072
1088
|
val error = OpenIAPError.InvalidReceipt
|
|
1073
|
-
throw
|
|
1089
|
+
throw OpenIapException(
|
|
1074
1090
|
toErrorJson(
|
|
1075
1091
|
error = error,
|
|
1076
1092
|
debugMessage = debugMessage,
|
|
@@ -1084,31 +1100,31 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1084
1100
|
// iOS-specific methods - Not applicable on Android, return appropriate defaults
|
|
1085
1101
|
override fun subscriptionStatusIOS(sku: String): Promise<Array<NitroSubscriptionStatus>?> {
|
|
1086
1102
|
return Promise.async {
|
|
1087
|
-
throw
|
|
1103
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1088
1104
|
}
|
|
1089
1105
|
}
|
|
1090
1106
|
|
|
1091
1107
|
override fun currentEntitlementIOS(sku: String): Promise<NitroPurchase?> {
|
|
1092
1108
|
return Promise.async {
|
|
1093
|
-
throw
|
|
1109
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1094
1110
|
}
|
|
1095
1111
|
}
|
|
1096
1112
|
|
|
1097
1113
|
override fun latestTransactionIOS(sku: String): Promise<NitroPurchase?> {
|
|
1098
1114
|
return Promise.async {
|
|
1099
|
-
throw
|
|
1115
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1100
1116
|
}
|
|
1101
1117
|
}
|
|
1102
1118
|
|
|
1103
1119
|
override fun getPendingTransactionsIOS(): Promise<Array<NitroPurchase>> {
|
|
1104
1120
|
return Promise.async {
|
|
1105
|
-
throw
|
|
1121
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1106
1122
|
}
|
|
1107
1123
|
}
|
|
1108
1124
|
|
|
1109
1125
|
override fun syncIOS(): Promise<Boolean> {
|
|
1110
1126
|
return Promise.async {
|
|
1111
|
-
throw
|
|
1127
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1112
1128
|
}
|
|
1113
1129
|
}
|
|
1114
1130
|
|
|
@@ -1116,37 +1132,37 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1116
1132
|
|
|
1117
1133
|
override fun isEligibleForIntroOfferIOS(groupID: String): Promise<Boolean> {
|
|
1118
1134
|
return Promise.async {
|
|
1119
|
-
throw
|
|
1135
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1120
1136
|
}
|
|
1121
1137
|
}
|
|
1122
1138
|
|
|
1123
1139
|
override fun getReceiptDataIOS(): Promise<String> {
|
|
1124
1140
|
return Promise.async {
|
|
1125
|
-
throw
|
|
1141
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1126
1142
|
}
|
|
1127
1143
|
}
|
|
1128
1144
|
|
|
1129
1145
|
override fun getReceiptIOS(): Promise<String> {
|
|
1130
1146
|
return Promise.async {
|
|
1131
|
-
throw
|
|
1147
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1132
1148
|
}
|
|
1133
1149
|
}
|
|
1134
1150
|
|
|
1135
1151
|
override fun requestReceiptRefreshIOS(): Promise<String> {
|
|
1136
1152
|
return Promise.async {
|
|
1137
|
-
throw
|
|
1153
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1138
1154
|
}
|
|
1139
1155
|
}
|
|
1140
1156
|
|
|
1141
1157
|
override fun isTransactionVerifiedIOS(sku: String): Promise<Boolean> {
|
|
1142
1158
|
return Promise.async {
|
|
1143
|
-
throw
|
|
1159
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1144
1160
|
}
|
|
1145
1161
|
}
|
|
1146
1162
|
|
|
1147
1163
|
override fun getTransactionJwsIOS(sku: String): Promise<String?> {
|
|
1148
1164
|
return Promise.async {
|
|
1149
|
-
throw
|
|
1165
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1150
1166
|
}
|
|
1151
1167
|
}
|
|
1152
1168
|
|
|
@@ -1166,7 +1182,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1166
1182
|
} catch (err: Throwable) {
|
|
1167
1183
|
RnIapLog.failure("checkAlternativeBillingAvailabilityAndroid", err)
|
|
1168
1184
|
val errorType = parseOpenIapError(err)
|
|
1169
|
-
throw
|
|
1185
|
+
throw OpenIapException(toErrorJson(errorType, debugMessage = err.message))
|
|
1170
1186
|
}
|
|
1171
1187
|
}
|
|
1172
1188
|
}
|
|
@@ -1176,7 +1192,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1176
1192
|
RnIapLog.payload("showAlternativeBillingDialogAndroid", null)
|
|
1177
1193
|
try {
|
|
1178
1194
|
val activity = context.currentActivity
|
|
1179
|
-
?: throw
|
|
1195
|
+
?: throw OpenIapException(toErrorJson(OpenIAPError.DeveloperError, debugMessage = "Activity not available"))
|
|
1180
1196
|
|
|
1181
1197
|
val userAccepted = withContext(Dispatchers.Main) {
|
|
1182
1198
|
openIap.setActivity(activity)
|
|
@@ -1187,7 +1203,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1187
1203
|
} catch (err: Throwable) {
|
|
1188
1204
|
RnIapLog.failure("showAlternativeBillingDialogAndroid", err)
|
|
1189
1205
|
val errorType = parseOpenIapError(err)
|
|
1190
|
-
throw
|
|
1206
|
+
throw OpenIapException(toErrorJson(errorType, debugMessage = err.message))
|
|
1191
1207
|
}
|
|
1192
1208
|
}
|
|
1193
1209
|
}
|
|
@@ -1206,7 +1222,7 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1206
1222
|
} catch (err: Throwable) {
|
|
1207
1223
|
RnIapLog.failure("createAlternativeBillingTokenAndroid", err)
|
|
1208
1224
|
val errorType = parseOpenIapError(err)
|
|
1209
|
-
throw
|
|
1225
|
+
throw OpenIapException(toErrorJson(errorType, debugMessage = err.message))
|
|
1210
1226
|
}
|
|
1211
1227
|
}
|
|
1212
1228
|
}
|
|
@@ -1236,19 +1252,19 @@ class HybridRnIap : HybridRnIapSpec() {
|
|
|
1236
1252
|
|
|
1237
1253
|
override fun canPresentExternalPurchaseNoticeIOS(): Promise<Boolean> {
|
|
1238
1254
|
return Promise.async {
|
|
1239
|
-
throw
|
|
1255
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1240
1256
|
}
|
|
1241
1257
|
}
|
|
1242
1258
|
|
|
1243
1259
|
override fun presentExternalPurchaseNoticeSheetIOS(): Promise<ExternalPurchaseNoticeResultIOS> {
|
|
1244
1260
|
return Promise.async {
|
|
1245
|
-
throw
|
|
1261
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1246
1262
|
}
|
|
1247
1263
|
}
|
|
1248
1264
|
|
|
1249
1265
|
override fun presentExternalPurchaseLinkIOS(url: String): Promise<ExternalPurchaseLinkResultIOS> {
|
|
1250
1266
|
return Promise.async {
|
|
1251
|
-
throw
|
|
1267
|
+
throw OpenIapException(toErrorJson(OpenIAPError.FeatureNotSupported))
|
|
1252
1268
|
}
|
|
1253
1269
|
}
|
|
1254
1270
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package com.margelo.nitro.iap
|
|
2
|
+
|
|
3
|
+
import org.junit.Test
|
|
4
|
+
import org.junit.Assert.*
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Unit tests for OpenIapException to verify that error messages
|
|
8
|
+
* are returned without Java/Kotlin stack traces.
|
|
9
|
+
*
|
|
10
|
+
* This addresses Issue #3075 where error.code was undefined because
|
|
11
|
+
* stack traces were breaking JSON parsing on the JavaScript side.
|
|
12
|
+
*/
|
|
13
|
+
class OpenIapExceptionTest {
|
|
14
|
+
|
|
15
|
+
@Test
|
|
16
|
+
fun `test OpenIapException message returns clean JSON`() {
|
|
17
|
+
val errorJson = """{"code":"init-connection","message":"Failed to initialize connection"}"""
|
|
18
|
+
val exception = OpenIapException(errorJson)
|
|
19
|
+
|
|
20
|
+
// Verify message is exactly the JSON string
|
|
21
|
+
assertEquals(errorJson, exception.message)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Test
|
|
25
|
+
fun `test OpenIapException toString returns clean JSON without stack trace`() {
|
|
26
|
+
val errorJson = """{"code":"user-cancelled","message":"User cancelled"}"""
|
|
27
|
+
val exception = OpenIapException(errorJson)
|
|
28
|
+
|
|
29
|
+
// toString() should return only the JSON, not "java.lang.Exception: ..."
|
|
30
|
+
val result = exception.toString()
|
|
31
|
+
assertEquals(errorJson, result)
|
|
32
|
+
assertFalse(result.startsWith("java.lang.Exception:"))
|
|
33
|
+
assertFalse(result.contains("\tat "))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
fun `test thrown OpenIapException message is clean`() {
|
|
38
|
+
val errorJson = """{"code":"network-error","message":"Network error occurred","responseCode":-1}"""
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
throw OpenIapException(errorJson)
|
|
42
|
+
} catch (e: Exception) {
|
|
43
|
+
// Verify the caught exception message is clean
|
|
44
|
+
assertEquals(errorJson, e.message)
|
|
45
|
+
assertEquals(errorJson, e.toString())
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun `test OpenIapException with complex JSON structure`() {
|
|
51
|
+
val errorJson = """{"code":"purchase-error","message":"Purchase failed","responseCode":3,"debugMessage":"Item unavailable","productId":"com.test.product"}"""
|
|
52
|
+
val exception = OpenIapException(errorJson)
|
|
53
|
+
|
|
54
|
+
assertEquals(errorJson, exception.message)
|
|
55
|
+
assertEquals(errorJson, exception.toString())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test
|
|
59
|
+
fun `test multiple OpenIapException instances are independent`() {
|
|
60
|
+
val error1 = """{"code":"error-1","message":"First error"}"""
|
|
61
|
+
val error2 = """{"code":"error-2","message":"Second error"}"""
|
|
62
|
+
|
|
63
|
+
val exception1 = OpenIapException(error1)
|
|
64
|
+
val exception2 = OpenIapException(error2)
|
|
65
|
+
|
|
66
|
+
assertEquals(error1, exception1.message)
|
|
67
|
+
assertEquals(error2, exception2.message)
|
|
68
|
+
assertNotEquals(exception1.message, exception2.message)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test
|
|
72
|
+
fun `test OpenIapException with empty JSON`() {
|
|
73
|
+
val errorJson = """{}"""
|
|
74
|
+
val exception = OpenIapException(errorJson)
|
|
75
|
+
|
|
76
|
+
assertEquals(errorJson, exception.message)
|
|
77
|
+
assertEquals(errorJson, exception.toString())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun `test OpenIapException message does not contain stack trace keywords`() {
|
|
82
|
+
val errorJson = """{"code":"test-error","message":"Test message"}"""
|
|
83
|
+
val exception = OpenIapException(errorJson)
|
|
84
|
+
|
|
85
|
+
val result = exception.toString()
|
|
86
|
+
|
|
87
|
+
// Verify no stack trace keywords are present
|
|
88
|
+
assertFalse("Should not contain 'at ' (stack trace)", result.contains("\tat "))
|
|
89
|
+
assertFalse("Should not contain 'java.lang.Exception:'", result.contains("java.lang.Exception:"))
|
|
90
|
+
assertFalse("Should not contain '.kt:' (Kotlin file reference)", result.contains(".kt:"))
|
|
91
|
+
assertFalse("Should not contain '.java:' (Java file reference)", result.contains(".java:"))
|
|
92
|
+
}
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
4
|
+
const pkg = require('../../package.json');
|
|
5
|
+
// Global flag to prevent duplicate logs
|
|
6
|
+
let hasLoggedPluginExecution = false;
|
|
7
|
+
const addLineToGradle = (content, anchor, lineToAdd, offset = 1) => {
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
const index = lines.findIndex((line) => line.match(anchor));
|
|
10
|
+
if (index === -1) {
|
|
11
|
+
console.warn(`Anchor "${anchor}" not found in build.gradle. Appending to end.`);
|
|
12
|
+
lines.push(lineToAdd);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
lines.splice(index + offset, 0, lineToAdd);
|
|
16
|
+
}
|
|
17
|
+
return lines.join('\n');
|
|
18
|
+
};
|
|
19
|
+
const modifyAppBuildGradle = (gradle) => {
|
|
20
|
+
let modified = gradle;
|
|
21
|
+
// Add billing library dependencies to app-level build.gradle
|
|
22
|
+
const billingDep = ` implementation "com.android.billingclient:billing-ktx:8.0.0"`;
|
|
23
|
+
const gmsDep = ` implementation "com.google.android.gms:play-services-base:18.1.0"`;
|
|
24
|
+
let hasAddedDependency = false;
|
|
25
|
+
if (!modified.includes(billingDep)) {
|
|
26
|
+
modified = addLineToGradle(modified, /dependencies\s*{/, billingDep);
|
|
27
|
+
hasAddedDependency = true;
|
|
28
|
+
}
|
|
29
|
+
if (!modified.includes(gmsDep)) {
|
|
30
|
+
modified = addLineToGradle(modified, /dependencies\s*{/, gmsDep, 1);
|
|
31
|
+
hasAddedDependency = true;
|
|
32
|
+
}
|
|
33
|
+
// Log only once and only if we actually added dependencies
|
|
34
|
+
if (hasAddedDependency && !hasLoggedPluginExecution) {
|
|
35
|
+
console.log('đ ī¸ react-native-iap: Added billing dependencies to build.gradle');
|
|
36
|
+
}
|
|
37
|
+
return modified;
|
|
38
|
+
};
|
|
39
|
+
const withIapAndroid = (config) => {
|
|
40
|
+
// Add IAP dependencies to app build.gradle
|
|
41
|
+
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
42
|
+
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
|
|
43
|
+
return config;
|
|
44
|
+
});
|
|
45
|
+
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
46
|
+
const manifest = config.modResults;
|
|
47
|
+
if (!manifest.manifest['uses-permission']) {
|
|
48
|
+
manifest.manifest['uses-permission'] = [];
|
|
49
|
+
}
|
|
50
|
+
const permissions = manifest.manifest['uses-permission'];
|
|
51
|
+
const billingPerm = { $: { 'android:name': 'com.android.vending.BILLING' } };
|
|
52
|
+
const alreadyExists = permissions.some((p) => p.$['android:name'] === 'com.android.vending.BILLING');
|
|
53
|
+
if (!alreadyExists) {
|
|
54
|
+
permissions.push(billingPerm);
|
|
55
|
+
if (!hasLoggedPluginExecution) {
|
|
56
|
+
console.log('â
Added com.android.vending.BILLING to AndroidManifest.xml');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (!hasLoggedPluginExecution) {
|
|
61
|
+
console.log('âšī¸ com.android.vending.BILLING already exists in AndroidManifest.xml');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return config;
|
|
65
|
+
});
|
|
66
|
+
return config;
|
|
67
|
+
};
|
|
68
|
+
const withIAP = (config, _props) => {
|
|
69
|
+
try {
|
|
70
|
+
const result = withIapAndroid(config);
|
|
71
|
+
// Set flag after first execution to prevent duplicate logs
|
|
72
|
+
hasLoggedPluginExecution = true;
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `react-native-iap plugin encountered an error: ${error}`);
|
|
77
|
+
console.error('react-native-iap plugin error:', error);
|
|
78
|
+
return config;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withIAP, pkg.name, pkg.version);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/withiap.ts"],"version":"5.9.2"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/
|
|
1
|
+
{"root":["./src/withiap.ts"],"version":"5.9.2"}
|