expo-iap 3.0.0 → 3.0.2-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.
Files changed (38) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +11 -1
  3. package/android/build.gradle +1 -1
  4. package/bun.lock +137 -340
  5. package/coverage/clover.xml +358 -0
  6. package/coverage/coverage-final.json +7 -0
  7. package/coverage/lcov-report/base.css +224 -0
  8. package/coverage/lcov-report/block-navigation.js +87 -0
  9. package/coverage/lcov-report/favicon.png +0 -0
  10. package/coverage/lcov-report/index.html +176 -0
  11. package/coverage/lcov-report/prettify.css +1 -0
  12. package/coverage/lcov-report/prettify.js +2 -0
  13. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  14. package/coverage/lcov-report/sorter.js +196 -0
  15. package/coverage/lcov-report/src/ExpoIap.types.ts.html +1396 -0
  16. package/coverage/lcov-report/src/helpers/index.html +116 -0
  17. package/coverage/lcov-report/src/helpers/subscription.ts.html +532 -0
  18. package/coverage/lcov-report/src/index.html +116 -0
  19. package/coverage/lcov-report/src/index.ts.html +1945 -0
  20. package/coverage/lcov-report/src/modules/android.ts.html +496 -0
  21. package/coverage/lcov-report/src/modules/index.html +131 -0
  22. package/coverage/lcov-report/src/modules/ios.ts.html +1012 -0
  23. package/coverage/lcov-report/src/types/ExpoIapAndroid.types.ts.html +502 -0
  24. package/coverage/lcov-report/src/types/index.html +116 -0
  25. package/coverage/lcov-report/src/useIAP.ts.html +1654 -0
  26. package/coverage/lcov-report/src/utils/constants.ts.html +127 -0
  27. package/coverage/lcov-report/src/utils/errorMapping.ts.html +427 -0
  28. package/coverage/lcov-report/src/utils/index.html +116 -0
  29. package/coverage/lcov.info +685 -0
  30. package/ios/ExpoIapModule.swift +24 -21
  31. package/ios/expoiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  32. package/ios/expoiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  33. package/package.json +2 -2
  34. package/plugin/build/withIAP.js +17 -11
  35. package/plugin/build/withLocalOpenIAP.js +11 -7
  36. package/plugin/src/withIAP.ts +20 -16
  37. package/plugin/src/withLocalOpenIAP.ts +11 -9
  38. package/plugin/tsconfig.tsbuildinfo +1 -1
@@ -84,7 +84,7 @@ public class ExpoIapModule: Module {
84
84
 
85
85
  // MARK: - Product Management
86
86
 
87
- AsyncFunction("fetchProducts") { (params: [String: Any]) async throws -> [[String: Any?]] in
87
+ AsyncFunction("fetchProducts") { (params: [String: Any]) async throws -> [[String: Any]] in
88
88
  try await ensureConnection()
89
89
  logDebug("fetchProducts raw params: \(params)")
90
90
 
@@ -144,7 +144,8 @@ public class ExpoIapModule: Module {
144
144
  for product in products {
145
145
  logDebug("Product: \(product.id) - \(product.title) - \(product.displayPrice)")
146
146
  }
147
- return OpenIapSerialization.products(products)
147
+ // Ensure non-optional values for Expo bridge
148
+ return OpenIapSerialization.products(products).map { $0.compactMapValues { $0 } }
148
149
  }
149
150
 
150
151
  // MARK: - Purchase Operations
@@ -220,7 +221,7 @@ public class ExpoIapModule: Module {
220
221
 
221
222
  // MARK: - Purchase History
222
223
 
223
- AsyncFunction("getAvailablePurchases") { (options: [String: Any?]?) async throws -> [[String: Any?]] in
224
+ AsyncFunction("getAvailablePurchases") { (options: [String: Any?]?) async throws -> [[String: Any]] in
224
225
  try await ensureConnection()
225
226
  logDebug("getAvailablePurchases called")
226
227
 
@@ -232,11 +233,11 @@ public class ExpoIapModule: Module {
232
233
  )
233
234
  }
234
235
  let purchases = try await OpenIapModule.shared.getAvailablePurchases(purchaseOptions)
235
- return OpenIapSerialization.purchases(purchases)
236
+ return OpenIapSerialization.purchases(purchases).map { $0.compactMapValues { $0 } }
236
237
  }
237
238
 
238
239
  // Legacy function for backward compatibility
239
- AsyncFunction("getAvailableItems") { (alsoPublishToEventListener: Bool, onlyIncludeActiveItems: Bool) async throws -> [[String: Any?]] in
240
+ AsyncFunction("getAvailableItems") { (alsoPublishToEventListener: Bool, onlyIncludeActiveItems: Bool) async throws -> [[String: Any]] in
240
241
  try await ensureConnection()
241
242
  logDebug("getAvailableItems called (legacy)")
242
243
 
@@ -245,15 +246,15 @@ public class ExpoIapModule: Module {
245
246
  onlyIncludeActiveItemsIOS: onlyIncludeActiveItems
246
247
  )
247
248
  let purchases = try await OpenIapModule.shared.getAvailablePurchases(purchaseOptions)
248
- return OpenIapSerialization.purchases(purchases)
249
+ return OpenIapSerialization.purchases(purchases).map { $0.compactMapValues { $0 } }
249
250
  }
250
251
 
251
- AsyncFunction("getPendingTransactionsIOS") { () async throws -> [[String: Any?]] in
252
+ AsyncFunction("getPendingTransactionsIOS") { () async throws -> [[String: Any]] in
252
253
  try await ensureConnection()
253
254
  logDebug("getPendingTransactionsIOS called")
254
255
 
255
256
  let pendingTransactions = try await OpenIapModule.shared.getPendingTransactionsIOS()
256
- return OpenIapSerialization.purchases(pendingTransactions)
257
+ return OpenIapSerialization.purchases(pendingTransactions).map { $0.compactMapValues { $0 } }
257
258
  }
258
259
 
259
260
  AsyncFunction("clearTransactionIOS") { () async throws -> Bool in
@@ -285,21 +286,22 @@ public class ExpoIapModule: Module {
285
286
  return try await OpenIapModule.shared.getReceiptDataIOS() ?? ""
286
287
  }
287
288
 
288
- AsyncFunction("validateReceiptIOS") { (sku: String) async throws -> [String: Any?] in
289
+ AsyncFunction("validateReceiptIOS") { (sku: String) async throws -> [String: Any] in
289
290
  try await ensureConnection()
290
291
  logDebug("validateReceiptIOS called for sku: \(sku)")
291
292
  do {
292
293
  // Use OpenIapReceiptValidationProps to keep naming parity with OpenIAP
293
294
  let props = OpenIapReceiptValidationProps(sku: sku)
294
295
  let result = try await OpenIapModule.shared.validateReceiptIOS(props)
295
- return [
296
+ let dict: [String: Any?] = [
296
297
  "isValid": result.isValid,
297
298
  "receiptData": result.receiptData,
298
299
  "jwsRepresentation": result.jwsRepresentation,
299
300
  // Populate unified purchaseToken for iOS as alias of JWS
300
301
  "purchaseToken": result.jwsRepresentation,
301
- "latestTransaction": result.latestTransaction.map { OpenIapSerialization.purchase($0) },
302
+ "latestTransaction": result.latestTransaction.map { OpenIapSerialization.purchase($0).compactMapValues { $0 } },
302
303
  ]
304
+ return dict.compactMapValues { $0 }
303
305
  } catch {
304
306
  throw OpenIapError.make(code: OpenIapError.E_RECEIPT_FAILED)
305
307
  }
@@ -314,11 +316,12 @@ public class ExpoIapModule: Module {
314
316
  return true
315
317
  }
316
318
 
317
- AsyncFunction("showManageSubscriptionsIOS") { () async throws -> [[String: Any?]] in
319
+ AsyncFunction("showManageSubscriptionsIOS") { () async throws -> [[String: Any]] in
318
320
  try await ensureConnection()
319
321
  logDebug("showManageSubscriptionsIOS called")
322
+ // OpenIAP 1.1.9 returns already-serialized dictionaries here.
320
323
  let purchases = try await OpenIapModule.shared.showManageSubscriptionsIOS()
321
- return OpenIapSerialization.purchases(purchases)
324
+ return purchases.map { $0.compactMapValues { $0 } }
322
325
  }
323
326
 
324
327
  AsyncFunction("deepLinkToSubscriptionsIOS") { () async throws in
@@ -339,7 +342,7 @@ public class ExpoIapModule: Module {
339
342
  return try await OpenIapModule.shared.beginRefundRequestIOS(sku: sku)
340
343
  }
341
344
 
342
- AsyncFunction("getPromotedProductIOS") { () async throws -> [String: Any?]? in
345
+ AsyncFunction("getPromotedProductIOS") { () async throws -> [String: Any]? in
343
346
  try await ensureConnection()
344
347
  logDebug("getPromotedProductIOS called")
345
348
 
@@ -347,7 +350,7 @@ public class ExpoIapModule: Module {
347
350
  // Fetch full product info by SKU to conform to OpenIapProduct
348
351
  let request = OpenIapProductRequest(skus: [promoted.productIdentifier], type: .all)
349
352
  let products = try await OpenIapModule.shared.fetchProducts(request)
350
- let serialized = OpenIapSerialization.products(products)
353
+ let serialized = OpenIapSerialization.products(products).map { $0.compactMapValues { $0 } }
351
354
  return serialized.first
352
355
  }
353
356
  return nil
@@ -384,7 +387,7 @@ public class ExpoIapModule: Module {
384
387
  return await OpenIapModule.shared.isEligibleForIntroOfferIOS(groupID: groupID)
385
388
  }
386
389
 
387
- AsyncFunction("subscriptionStatusIOS") { (sku: String) async throws -> [[String: Any?]]? in
390
+ AsyncFunction("subscriptionStatusIOS") { (sku: String) async throws -> [[String: Any]]? in
388
391
  try await ensureConnection()
389
392
  logDebug("subscriptionStatusIOS called for sku: \(sku)")
390
393
 
@@ -405,18 +408,18 @@ public class ExpoIapModule: Module {
405
408
  dict["renewalInfo"] = renewalInfo
406
409
  }
407
410
 
408
- return dict
411
+ return dict.compactMapValues { $0 }
409
412
  }
410
413
  }
411
414
  return nil
412
415
  }
413
416
 
414
- AsyncFunction("currentEntitlementIOS") { (sku: String) async throws -> [String: Any?]? in
417
+ AsyncFunction("currentEntitlementIOS") { (sku: String) async throws -> [String: Any]? in
415
418
  try await ensureConnection()
416
419
  logDebug("currentEntitlementIOS called for sku: \(sku)")
417
420
  do {
418
421
  if let entitlement = try await OpenIapModule.shared.currentEntitlementIOS(sku: sku) {
419
- return OpenIapSerialization.purchase(entitlement)
422
+ return OpenIapSerialization.purchase(entitlement).compactMapValues { $0 }
420
423
  }
421
424
  return nil
422
425
  } catch {
@@ -424,12 +427,12 @@ public class ExpoIapModule: Module {
424
427
  }
425
428
  }
426
429
 
427
- AsyncFunction("latestTransactionIOS") { (sku: String) async throws -> [String: Any?]? in
430
+ AsyncFunction("latestTransactionIOS") { (sku: String) async throws -> [String: Any]? in
428
431
  try await ensureConnection()
429
432
  logDebug("latestTransactionIOS called for sku: \(sku)")
430
433
  do {
431
434
  if let transaction = try await OpenIapModule.shared.latestTransactionIOS(sku: sku) {
432
- return OpenIapSerialization.purchase(transaction)
435
+ return OpenIapSerialization.purchase(transaction).compactMapValues { $0 }
433
436
  }
434
437
  return nil
435
438
  } catch {
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "self:">
6
+ </FileRef>
7
+ </Workspace>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>IDEDidComputeMac32BitWarning</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "3.0.0",
3
+ "version": "3.0.2-rc.1",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  "lint:ci": "bun run lint:tsc && bun run lint:eslint && bun run lint:prettier && bun run lint:kt",
16
16
  "test": "jest",
17
17
  "test:coverage": "jest --coverage",
18
- "prepare": "expo-module prepare && husky install",
18
+ "prepare": "expo-module prepare && sh -c 'command -v husky >/dev/null 2>&1 && husky install || { echo \"husky not installed; skipping\"; exit 0; }'",
19
19
  "expo-module": "expo-module",
20
20
  "open:ios": "xed example/ios",
21
21
  "open:android": "open -a \"Android Studio\" example/android",
@@ -65,21 +65,26 @@ const addLineToGradle = (content, anchor, lineToAdd, offset = 1) => {
65
65
  };
66
66
  const modifyAppBuildGradle = (gradle, language) => {
67
67
  let modified = gradle;
68
- // Add OpenIAP dependency to app-level build.gradle(.kts)
68
+ // Ensure OpenIAP dependency exists at desired version in app-level build.gradle(.kts)
69
69
  const impl = (ga, v) => language === 'kotlin'
70
70
  ? ` implementation("${ga}:${v}")`
71
71
  : ` implementation "${ga}:${v}"`;
72
72
  // Pin OpenIAP Google library to 1.1.0
73
73
  const openiapDep = impl('io.github.hyochan.openiap:openiap-google', '1.1.0');
74
- const hasGA = (ga) => new RegExp(String.raw `\b(?:implementation|api)\s*\(?["']${ga}:`, 'm').test(modified);
75
- let hasAddedDependency = false;
76
- if (!hasGA('io.github.hyochan.openiap:openiap-google')) {
77
- modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 0);
78
- hasAddedDependency = true;
74
+ // Remove any existing openiap-google lines (any version, groovy/kotlin, implementation/api)
75
+ const openiapAnyLine = /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
76
+ const hadExisting = openiapAnyLine.test(modified);
77
+ if (hadExisting) {
78
+ modified = modified.replace(openiapAnyLine, '').replace(/\n{3,}/g, '\n\n');
79
+ }
80
+ // Ensure the desired dependency line is present
81
+ if (!new RegExp(String.raw `io\.github\.hyochan\.openiap:openiap-google:1\.1\.0`).test(modified)) {
82
+ // Insert just after the opening `dependencies {` line
83
+ modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 1);
84
+ logOnce(hadExisting
85
+ ? '🛠️ expo-iap: Replaced OpenIAP dependency with 1.1.0'
86
+ : '🛠️ expo-iap: Added OpenIAP dependency (1.1.0) to build.gradle');
79
87
  }
80
- // Log only once and only if we actually added dependencies
81
- if (hasAddedDependency)
82
- logOnce('🛠️ expo-iap: Added OpenIAP dependency to build.gradle');
83
88
  return modified;
84
89
  };
85
90
  const withIapAndroid = (config, props) => {
@@ -141,11 +146,12 @@ const withIapIOS = (config) => {
141
146
  };
142
147
  const withIap = (config, options) => {
143
148
  try {
144
- const isLocalDev = !!(options?.enableLocalDev || options?.localPath);
149
+ // Respect explicit flag; fall back to presence of localPath only when flag is unset
150
+ const isLocalDev = options?.enableLocalDev ?? !!options?.localPath;
145
151
  // Apply Android modifications (skip adding deps when linking local module)
146
152
  let result = withIapAndroid(config, { addDeps: !isLocalDev });
147
153
  // iOS: choose one path to avoid overlap
148
- if (options?.enableLocalDev || options?.localPath) {
154
+ if (isLocalDev) {
149
155
  if (!options?.localPath) {
150
156
  config_plugins_1.WarningAggregator.addWarningIOS('expo-iap', 'enableLocalDev is true but no localPath provided. Skipping local OpenIAP integration.');
151
157
  }
@@ -177,10 +177,12 @@ const withLocalOpenIAP = (config, props) => {
177
177
  }
178
178
  const gradle = config.modResults;
179
179
  const dependencyLine = ` implementation project(':openiap-google')`;
180
- // Remove any previously injected external deps that can conflict with the local module
180
+ // Remove any previously added Maven deps for openiap-google to avoid duplicate classes
181
181
  const removalPatterns = [
182
- /\n\s*implementation\s+"com\.android\.billingclient:billing-ktx:[^"]+"\s*\n/g,
183
- /\n\s*implementation\s+"com\.google\.android\.gms:play-services-base:[^"]+"\s*\n/g,
182
+ // Groovy DSL: implementation "io.github.hyochan.openiap:openiap-google:x.y.z" or api "..."
183
+ /^\s*(?:implementation|api)\s+["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*$/gm,
184
+ // Kotlin DSL: implementation("io.github.hyochan.openiap:openiap-google:x.y.z") or api("...")
185
+ /^\s*(?:implementation|api)\s*\(\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)\s*$/gm,
184
186
  ];
185
187
  let contents = gradle.contents;
186
188
  let removedAny = false;
@@ -192,7 +194,7 @@ const withLocalOpenIAP = (config, props) => {
192
194
  }
193
195
  if (removedAny) {
194
196
  gradle.contents = contents;
195
- console.log('🧹 Removed external Play Billing/GMS deps to use local :openiap-google');
197
+ console.log('🧹 Removed Maven openiap-google to use local :openiap-google');
196
198
  }
197
199
  if (!gradle.contents.includes(dependencyLine)) {
198
200
  const anchor = /dependencies\s*\{/m;
@@ -216,8 +218,10 @@ const withLocalOpenIAP = (config, props) => {
216
218
  if (fs.existsSync(appBuildGradle)) {
217
219
  let contents = fs.readFileSync(appBuildGradle, 'utf8');
218
220
  const patterns = [
219
- /\n\s*implementation\s+"com\.android\.billingclient:billing-ktx:[^"]+"\s*\n/g,
220
- /\n\s*implementation\s+"com\.google\.android\.gms:play-services-base:[^"]+"\s*\n/g,
221
+ // Groovy DSL
222
+ /^\s*(?:implementation|api)\s+["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*$/gm,
223
+ // Kotlin DSL
224
+ /^\s*(?:implementation|api)\s*\(\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)\s*$/gm,
221
225
  ];
222
226
  let changed = false;
223
227
  for (const p of patterns) {
@@ -228,7 +232,7 @@ const withLocalOpenIAP = (config, props) => {
228
232
  }
229
233
  if (changed) {
230
234
  fs.writeFileSync(appBuildGradle, contents);
231
- console.log('🧹 expo-iap: Cleaned app/build.gradle billing/gms deps for local :openiap-google');
235
+ console.log('🧹 expo-iap: Cleaned Maven openiap-google for local :openiap-google');
232
236
  }
233
237
  }
234
238
  }
@@ -49,7 +49,7 @@ const modifyAppBuildGradle = (
49
49
  ): string => {
50
50
  let modified = gradle;
51
51
 
52
- // Add OpenIAP dependency to app-level build.gradle(.kts)
52
+ // Ensure OpenIAP dependency exists at desired version in app-level build.gradle(.kts)
53
53
  const impl = (ga: string, v: string) =>
54
54
  language === 'kotlin'
55
55
  ? ` implementation("${ga}:${v}")`
@@ -57,21 +57,24 @@ const modifyAppBuildGradle = (
57
57
  // Pin OpenIAP Google library to 1.1.0
58
58
  const openiapDep = impl('io.github.hyochan.openiap:openiap-google', '1.1.0');
59
59
 
60
- const hasGA = (ga: string) =>
61
- new RegExp(String.raw`\b(?:implementation|api)\s*\(?["']${ga}:`, 'm').test(
62
- modified,
63
- );
64
-
65
- let hasAddedDependency = false;
66
-
67
- if (!hasGA('io.github.hyochan.openiap:openiap-google')) {
68
- modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 0);
69
- hasAddedDependency = true;
60
+ // Remove any existing openiap-google lines (any version, groovy/kotlin, implementation/api)
61
+ const openiapAnyLine =
62
+ /^\s*(?:implementation|api)\s*\(?\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)?\s*$/gm;
63
+ const hadExisting = openiapAnyLine.test(modified);
64
+ if (hadExisting) {
65
+ modified = modified.replace(openiapAnyLine, '').replace(/\n{3,}/g, '\n\n');
70
66
  }
71
67
 
72
- // Log only once and only if we actually added dependencies
73
- if (hasAddedDependency)
74
- logOnce('🛠️ expo-iap: Added OpenIAP dependency to build.gradle');
68
+ // Ensure the desired dependency line is present
69
+ if (!new RegExp(String.raw`io\.github\.hyochan\.openiap:openiap-google:1\.1\.0`).test(modified)) {
70
+ // Insert just after the opening `dependencies {` line
71
+ modified = addLineToGradle(modified, /dependencies\s*{/, openiapDep, 1);
72
+ logOnce(
73
+ hadExisting
74
+ ? '🛠️ expo-iap: Replaced OpenIAP dependency with 1.1.0'
75
+ : '🛠️ expo-iap: Added OpenIAP dependency (1.1.0) to build.gradle',
76
+ );
77
+ }
75
78
 
76
79
  return modified;
77
80
  };
@@ -173,12 +176,13 @@ const withIap: ConfigPlugin<ExpoIapPluginOptions | void> = (
173
176
  options,
174
177
  ) => {
175
178
  try {
176
- const isLocalDev = !!(options?.enableLocalDev || options?.localPath);
179
+ // Respect explicit flag; fall back to presence of localPath only when flag is unset
180
+ const isLocalDev = options?.enableLocalDev ?? !!options?.localPath;
177
181
  // Apply Android modifications (skip adding deps when linking local module)
178
182
  let result = withIapAndroid(config, {addDeps: !isLocalDev});
179
183
 
180
184
  // iOS: choose one path to avoid overlap
181
- if (options?.enableLocalDev || options?.localPath) {
185
+ if (isLocalDev) {
182
186
  if (!options?.localPath) {
183
187
  WarningAggregator.addWarningIOS(
184
188
  'expo-iap',
@@ -196,10 +196,12 @@ const withLocalOpenIAP: ConfigPlugin<{localPath?: LocalPathOption} | void> = (
196
196
  const gradle = config.modResults;
197
197
  const dependencyLine = ` implementation project(':openiap-google')`;
198
198
 
199
- // Remove any previously injected external deps that can conflict with the local module
199
+ // Remove any previously added Maven deps for openiap-google to avoid duplicate classes
200
200
  const removalPatterns = [
201
- /\n\s*implementation\s+"com\.android\.billingclient:billing-ktx:[^"]+"\s*\n/g,
202
- /\n\s*implementation\s+"com\.google\.android\.gms:play-services-base:[^"]+"\s*\n/g,
201
+ // Groovy DSL: implementation "io.github.hyochan.openiap:openiap-google:x.y.z" or api "..."
202
+ /^\s*(?:implementation|api)\s+["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*$/gm,
203
+ // Kotlin DSL: implementation("io.github.hyochan.openiap:openiap-google:x.y.z") or api("...")
204
+ /^\s*(?:implementation|api)\s*\(\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)\s*$/gm,
203
205
  ];
204
206
  let contents = gradle.contents;
205
207
  let removedAny = false;
@@ -211,9 +213,7 @@ const withLocalOpenIAP: ConfigPlugin<{localPath?: LocalPathOption} | void> = (
211
213
  }
212
214
  if (removedAny) {
213
215
  gradle.contents = contents;
214
- console.log(
215
- '🧹 Removed external Play Billing/GMS deps to use local :openiap-google',
216
- );
216
+ console.log('🧹 Removed Maven openiap-google to use local :openiap-google');
217
217
  }
218
218
  if (!gradle.contents.includes(dependencyLine)) {
219
219
  const anchor = /dependencies\s*\{/m;
@@ -244,8 +244,10 @@ const withLocalOpenIAP: ConfigPlugin<{localPath?: LocalPathOption} | void> = (
244
244
  if (fs.existsSync(appBuildGradle)) {
245
245
  let contents = fs.readFileSync(appBuildGradle, 'utf8');
246
246
  const patterns = [
247
- /\n\s*implementation\s+"com\.android\.billingclient:billing-ktx:[^"]+"\s*\n/g,
248
- /\n\s*implementation\s+"com\.google\.android\.gms:play-services-base:[^"]+"\s*\n/g,
247
+ // Groovy DSL
248
+ /^\s*(?:implementation|api)\s+["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*$/gm,
249
+ // Kotlin DSL
250
+ /^\s*(?:implementation|api)\s*\(\s*["']io\.github\.hyochan\.openiap:openiap-google:[^"']+["']\s*\)\s*$/gm,
249
251
  ];
250
252
  let changed = false;
251
253
  for (const p of patterns) {
@@ -257,7 +259,7 @@ const withLocalOpenIAP: ConfigPlugin<{localPath?: LocalPathOption} | void> = (
257
259
  if (changed) {
258
260
  fs.writeFileSync(appBuildGradle, contents);
259
261
  console.log(
260
- '🧹 expo-iap: Cleaned app/build.gradle billing/gms deps for local :openiap-google',
262
+ '🧹 expo-iap: Cleaned Maven openiap-google for local :openiap-google',
261
263
  );
262
264
  }
263
265
  }
@@ -1 +1 @@
1
- {"root":["./src/withIAP.ts","./src/withLocalOpenIAP.ts"],"version":"5.9.2"}
1
+ {"root":["./src/withiap.ts","./src/withlocalopeniap.ts"],"version":"5.9.2"}