@capgo/native-purchases 7.16.2 → 7.17.0

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.
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
12
12
  s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.exclude_files = '**/node_modules/**/*', '**/examples/**/*'
15
- s.ios.deployment_target = '14.0'
15
+ s.ios.deployment_target = '15.0'
16
16
  s.dependency 'Capacitor'
17
17
  s.swift_version = '5.1'
18
18
  end
package/Package.swift CHANGED
@@ -3,14 +3,14 @@ import PackageDescription
3
3
 
4
4
  let package = Package(
5
5
  name: "CapgoNativePurchases",
6
- platforms: [.iOS(.v14)],
6
+ platforms: [.iOS(.v15)],
7
7
  products: [
8
8
  .library(
9
9
  name: "CapgoNativePurchases",
10
10
  targets: ["NativePurchasesPlugin"])
11
11
  ],
12
12
  dependencies: [
13
- .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
14
14
  ],
15
15
  targets: [
16
16
  .target(
@@ -1,8 +1,8 @@
1
1
  ext {
2
2
  junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
- androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
4
- androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
5
- androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
6
  }
7
7
 
8
8
  buildscript {
@@ -11,18 +11,18 @@ buildscript {
11
11
  mavenCentral()
12
12
  }
13
13
  dependencies {
14
- classpath 'com.android.tools.build:gradle:8.7.3'
14
+ classpath 'com.android.tools.build:gradle:8.13.0'
15
15
  }
16
16
  }
17
17
 
18
18
  apply plugin: 'com.android.library'
19
19
 
20
20
  android {
21
- namespace "ee.forgr.nativepurchases"
22
- compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
21
+ namespace = "ee.forgr.nativepurchases"
22
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
23
23
  defaultConfig {
24
- minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
25
- targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
26
26
  versionCode 1
27
27
  versionName "1.0"
28
28
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -34,7 +34,7 @@ android {
34
34
  }
35
35
  }
36
36
  lintOptions {
37
- abortOnError false
37
+ abortOnError = false
38
38
  }
39
39
  compileOptions {
40
40
  sourceCompatibility JavaVersion.VERSION_21
@@ -50,7 +50,7 @@ repositories {
50
50
 
51
51
  dependencies {
52
52
  implementation "com.google.guava:guava:33.5.0-android"
53
- def billing_version = "8.1.0"
53
+ def billing_version = "8.2.0"
54
54
  implementation "com.android.billingclient:billing:$billing_version"
55
55
  implementation fileTree(dir: 'libs', include: ['*.jar'])
56
56
  implementation project(':capacitor-android')
@@ -42,10 +42,12 @@ import org.json.JSONArray;
42
42
  @CapacitorPlugin(name = "NativePurchases")
43
43
  public class NativePurchasesPlugin extends Plugin {
44
44
 
45
- private final String pluginVersion = "7.16.2";
45
+ private final String pluginVersion = "7.17.0";
46
46
  public static final String TAG = "NativePurchases";
47
47
  private static final Phaser semaphoreReady = new Phaser(1);
48
48
  private BillingClient billingClient;
49
+ private PluginCall pendingCall = null;
50
+ private BillingResult lastBillingError = null;
49
51
 
50
52
  @PluginMethod
51
53
  public void isBillingSupported(PluginCall call) {
@@ -110,6 +112,13 @@ public class NativePurchasesPlugin extends Plugin {
110
112
  } else {
111
113
  Log.d(TAG, "Billing client was already null");
112
114
  }
115
+
116
+ // Clear pending call and error state
117
+ if (pendingCall != null) {
118
+ Log.w(TAG, "Warning: Clearing pending call that was never resolved/rejected");
119
+ pendingCall = null;
120
+ }
121
+ lastBillingError = null;
113
122
  }
114
123
 
115
124
  private void handlePurchase(Purchase purchase, PluginCall purchaseCall) {
@@ -233,6 +242,12 @@ public class NativePurchasesPlugin extends Plugin {
233
242
 
234
243
  private void initBillingClient(PluginCall purchaseCall) {
235
244
  Log.d(TAG, "initBillingClient() called");
245
+ Log.d(TAG, "purchaseCall is null: " + (purchaseCall == null));
246
+
247
+ // Store the pending call so we can reject it if billing setup fails
248
+ this.pendingCall = purchaseCall;
249
+ this.lastBillingError = null;
250
+
236
251
  semaphoreWait();
237
252
  closeBillingClient();
238
253
  semaphoreUp();
@@ -278,9 +293,43 @@ public class NativePurchasesPlugin extends Plugin {
278
293
  if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
279
294
  Log.d(TAG, "Billing setup successful, client is ready");
280
295
  // The BillingClient is ready. You can query purchases here.
296
+ lastBillingError = null;
281
297
  semaphoreReady.countDown();
282
298
  } else {
283
- Log.d(TAG, "Billing setup failed");
299
+ Log.e(TAG, "Billing setup failed with code: " + billingResult.getResponseCode());
300
+ Log.e(TAG, "Error message: " + billingResult.getDebugMessage());
301
+
302
+ // Store the error for later use
303
+ lastBillingError = billingResult;
304
+
305
+ // Release the latch so the waiting thread can continue
306
+ semaphoreReady.countDown();
307
+
308
+ // Reject the pending call if there is one
309
+ if (pendingCall != null) {
310
+ Log.d(TAG, "Rejecting pending call due to billing setup failure");
311
+ String errorMessage = "Billing service unavailable";
312
+ switch (billingResult.getResponseCode()) {
313
+ case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE:
314
+ errorMessage =
315
+ "Billing service unavailable. Please check your internet connection and Google Play Services.";
316
+ break;
317
+ case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE:
318
+ errorMessage = "Billing is not available on this device.";
319
+ break;
320
+ case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
321
+ errorMessage = "This billing feature is not supported.";
322
+ break;
323
+ case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
324
+ errorMessage = "Billing service disconnected. Please try again.";
325
+ break;
326
+ default:
327
+ errorMessage = "Billing setup failed: " + billingResult.getDebugMessage();
328
+ break;
329
+ }
330
+ pendingCall.reject("BILLING_SETUP_FAILED", errorMessage);
331
+ pendingCall = null;
332
+ }
284
333
  }
285
334
  }
286
335
 
@@ -295,10 +344,26 @@ public class NativePurchasesPlugin extends Plugin {
295
344
  try {
296
345
  Log.d(TAG, "Waiting for billing client setup to finish");
297
346
  semaphoreReady.await();
298
- Log.d(TAG, "Billing client setup completed");
347
+ Log.d(TAG, "Billing client setup wait completed");
348
+
349
+ // Check if billing setup failed
350
+ if (lastBillingError != null) {
351
+ Log.e(TAG, "Billing setup failed, throwing exception");
352
+ throw new RuntimeException("Billing setup failed: " + lastBillingError.getDebugMessage());
353
+ }
354
+
355
+ Log.d(TAG, "Billing client setup completed successfully");
299
356
  } catch (InterruptedException e) {
300
- Log.d(TAG, "InterruptedException while waiting for billing setup: " + e.getMessage());
357
+ Log.e(TAG, "InterruptedException while waiting for billing setup: " + e.getMessage());
301
358
  e.printStackTrace();
359
+ if (pendingCall != null) {
360
+ pendingCall.reject("BILLING_INTERRUPTED", "Billing setup was interrupted");
361
+ pendingCall = null;
362
+ }
363
+ } catch (RuntimeException e) {
364
+ Log.e(TAG, "RuntimeException during billing setup: " + e.getMessage());
365
+ // Don't reject here - already rejected in onBillingSetupFinished
366
+ throw e;
302
367
  }
303
368
  }
304
369
 
@@ -383,7 +448,14 @@ public class NativePurchasesPlugin extends Plugin {
383
448
  );
384
449
  QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder().setProductList(productList).build();
385
450
  Log.d(TAG, "Initializing billing client for purchase");
386
- this.initBillingClient(call);
451
+ try {
452
+ this.initBillingClient(call);
453
+ } catch (RuntimeException e) {
454
+ Log.e(TAG, "Failed to initialize billing client: " + e.getMessage());
455
+ closeBillingClient();
456
+ // Call already rejected in initBillingClient
457
+ return;
458
+ }
387
459
  try {
388
460
  Log.d(TAG, "Querying product details for purchase");
389
461
  billingClient.queryProductDetailsAsync(
@@ -434,9 +506,8 @@ public class NativePurchasesPlugin extends Plugin {
434
506
  }
435
507
  productDetailsParamsList.add(productDetailsParams.build());
436
508
  }
437
- BillingFlowParams.Builder billingFlowBuilder = BillingFlowParams.newBuilder().setProductDetailsParamsList(
438
- productDetailsParamsList
439
- );
509
+ BillingFlowParams.Builder billingFlowBuilder = BillingFlowParams.newBuilder()
510
+ .setProductDetailsParamsList(productDetailsParamsList);
440
511
  if (accountIdentifier != null && !accountIdentifier.isEmpty()) {
441
512
  billingFlowBuilder.setObfuscatedAccountId(accountIdentifier);
442
513
  }
@@ -520,7 +591,14 @@ public class NativePurchasesPlugin extends Plugin {
520
591
  public void restorePurchases(PluginCall call) {
521
592
  Log.d(TAG, "restorePurchases() called");
522
593
  Log.d(NativePurchasesPlugin.TAG, "restorePurchases");
523
- this.initBillingClient(null);
594
+ try {
595
+ this.initBillingClient(call);
596
+ } catch (RuntimeException e) {
597
+ Log.e(TAG, "Failed to initialize billing client: " + e.getMessage());
598
+ closeBillingClient();
599
+ // Call already rejected in initBillingClient
600
+ return;
601
+ }
524
602
  this.processUnfinishedPurchases();
525
603
  call.resolve();
526
604
  Log.d(TAG, "restorePurchases() completed");
@@ -541,7 +619,14 @@ public class NativePurchasesPlugin extends Plugin {
541
619
 
542
620
  QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder().setProductList(productList).build();
543
621
  Log.d(TAG, "Initializing billing client for single product query");
544
- this.initBillingClient(call);
622
+ try {
623
+ this.initBillingClient(call);
624
+ } catch (RuntimeException e) {
625
+ Log.e(TAG, "Failed to initialize billing client: " + e.getMessage());
626
+ closeBillingClient();
627
+ // Call already rejected in initBillingClient
628
+ return;
629
+ }
545
630
  try {
546
631
  Log.d(TAG, "Querying product details");
547
632
  billingClient.queryProductDetailsAsync(
@@ -612,7 +697,7 @@ public class NativePurchasesPlugin extends Plugin {
612
697
  Log.d(
613
698
  TAG,
614
699
  "Formatted price: " +
615
- selectedOfferDetails.getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()
700
+ selectedOfferDetails.getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()
616
701
  );
617
702
  Log.d(
618
703
  TAG,
@@ -655,7 +740,14 @@ public class NativePurchasesPlugin extends Plugin {
655
740
  Log.d(TAG, "Total products in query list: " + productList.size());
656
741
  QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder().setProductList(productList).build();
657
742
  Log.d(TAG, "Initializing billing client for product query");
658
- this.initBillingClient(call);
743
+ try {
744
+ this.initBillingClient(call);
745
+ } catch (RuntimeException e) {
746
+ Log.e(TAG, "Failed to initialize billing client: " + e.getMessage());
747
+ closeBillingClient();
748
+ // Call already rejected in initBillingClient
749
+ return;
750
+ }
659
751
  try {
660
752
  Log.d(TAG, "Querying product details");
661
753
  billingClient.queryProductDetailsAsync(
@@ -727,12 +819,12 @@ public class NativePurchasesPlugin extends Plugin {
727
819
  Log.d(
728
820
  TAG,
729
821
  "Formatted price: " +
730
- selectedOfferDetails.getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()
822
+ selectedOfferDetails.getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice()
731
823
  );
732
824
  Log.d(
733
825
  TAG,
734
826
  "Currency: " +
735
- selectedOfferDetails.getPricingPhases().getPricingPhaseList().get(0).getPriceCurrencyCode()
827
+ selectedOfferDetails.getPricingPhases().getPricingPhaseList().get(0).getPriceCurrencyCode()
736
828
  );
737
829
  }
738
830
  product.put("isFamilyShareable", false);
@@ -817,7 +909,14 @@ public class NativePurchasesPlugin extends Plugin {
817
909
  return;
818
910
  }
819
911
 
820
- this.initBillingClient(null);
912
+ try {
913
+ this.initBillingClient(call);
914
+ } catch (RuntimeException e) {
915
+ Log.e(TAG, "Failed to initialize billing client: " + e.getMessage());
916
+ closeBillingClient();
917
+ // Call already rejected in initBillingClient
918
+ return;
919
+ }
821
920
 
822
921
  JSONArray allPurchases = new JSONArray();
823
922
  AtomicInteger pendingQueries = new AtomicInteger((queryInApp ? 1 : 0) + (querySubs ? 1 : 0));
@@ -982,7 +1081,14 @@ public class NativePurchasesPlugin extends Plugin {
982
1081
  }
983
1082
 
984
1083
  Log.d(TAG, "Manually acknowledging purchase with token: " + purchaseToken);
985
- this.initBillingClient(null);
1084
+ try {
1085
+ this.initBillingClient(call);
1086
+ } catch (RuntimeException e) {
1087
+ Log.e(TAG, "Failed to initialize billing client: " + e.getMessage());
1088
+ closeBillingClient();
1089
+ // Call already rejected in initBillingClient
1090
+ return;
1091
+ }
986
1092
 
987
1093
  try {
988
1094
  AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
@@ -24,7 +24,7 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
24
24
  CAPPluginMethod(name: "isEntitledToOldBusinessModel", returnType: CAPPluginReturnPromise)
25
25
  ]
26
26
 
27
- private let pluginVersion: String = "7.16.2"
27
+ private let pluginVersion: String = "7.17.0"
28
28
  private var transactionUpdatesTask: Task<Void, Never>?
29
29
 
30
30
  @objc func getPluginVersion(_ call: CAPPluginCall) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/native-purchases",
3
- "version": "7.16.2",
3
+ "version": "7.17.0",
4
4
  "description": "In-app Subscriptions Made Easy",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -24,6 +24,7 @@
24
24
  "bugs": {
25
25
  "url": "https://github.com/Cap-go/capacitor-native-purchases/issues"
26
26
  },
27
+ "homepage": "https://capgo.app/docs/plugins/native-purchases/",
27
28
  "keywords": [
28
29
  "capacitor",
29
30
  "plugin",
@@ -42,7 +43,7 @@
42
43
  "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
43
44
  "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
44
45
  "eslint": "eslint . --ext ts",
45
- "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
46
+ "prettier": "prettier-pretty-check \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
46
47
  "swiftlint": "node-swiftlint",
47
48
  "docgen": "docgen --api NativePurchasesPlugin --output-readme README.md --output-json dist/docs.json",
48
49
  "clean": "rimraf ./dist",
@@ -50,27 +51,28 @@
50
51
  "prepublishOnly": "npm run build"
51
52
  },
52
53
  "devDependencies": {
53
- "@capacitor/android": "^7.0.0",
54
- "@capacitor/cli": "^7.0.0",
55
- "@capacitor/core": "^7.0.0",
56
- "@capacitor/docgen": "^0.3.0",
57
- "@capacitor/ios": "^7.0.0",
54
+ "@capacitor/android": "^8.0.0",
55
+ "@capacitor/cli": "^8.0.0",
56
+ "@capacitor/core": "^8.0.0",
57
+ "@capacitor/docgen": "^0.3.1",
58
+ "@capacitor/ios": "^8.0.0",
58
59
  "@ionic/eslint-config": "^0.4.0",
59
60
  "@ionic/prettier-config": "^4.0.0",
60
61
  "@ionic/swiftlint-config": "^2.0.0",
61
- "@types/node": "^24.0.0",
62
- "eslint": "^8.57.0",
62
+ "@types/node": "^24.10.1",
63
+ "eslint": "^8.57.1",
63
64
  "eslint-plugin-import": "^2.31.0",
64
65
  "husky": "^9.1.7",
65
- "prettier": "^3.4.2",
66
- "prettier-plugin-java": "^2.6.7",
67
- "rimraf": "^6.0.1",
68
- "rollup": "^4.34.6",
66
+ "prettier": "^3.6.2",
67
+ "prettier-plugin-java": "^2.7.7",
68
+ "prettier-pretty-check": "^0.2.0",
69
+ "rimraf": "^6.1.0",
70
+ "rollup": "^4.53.2",
69
71
  "swiftlint": "^2.0.0",
70
- "typescript": "^5.7.3"
72
+ "typescript": "^5.9.3"
71
73
  },
72
74
  "peerDependencies": {
73
- "@capacitor/core": ">=7.0.0"
75
+ "@capacitor/core": ">=8.0.0"
74
76
  },
75
77
  "prettier": "@ionic/prettier-config",
76
78
  "swiftlint": "@ionic/swiftlint-config",