@capgo/native-purchases 7.8.8 → 7.9.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.
@@ -32,12 +32,14 @@ import java.util.concurrent.CountDownLatch;
32
32
  import java.util.concurrent.Phaser;
33
33
  import java.util.concurrent.TimeUnit;
34
34
  import java.util.concurrent.TimeoutException;
35
+ import java.util.concurrent.atomic.AtomicBoolean;
36
+ import java.util.concurrent.atomic.AtomicInteger;
35
37
  import org.json.JSONArray;
36
38
 
37
39
  @CapacitorPlugin(name = "NativePurchases")
38
40
  public class NativePurchasesPlugin extends Plugin {
39
41
 
40
- public final String PLUGIN_VERSION = "0.0.25";
42
+ private final String PLUGIN_VERSION = "7.9.1";
41
43
  public static final String TAG = "NativePurchases";
42
44
  private static final Phaser semaphoreReady = new Phaser(1);
43
45
  private BillingClient billingClient;
@@ -666,115 +668,144 @@ public class NativePurchasesPlugin extends Plugin {
666
668
  final boolean hasAccountFilter = accountFilter != null && !accountFilter.isEmpty();
667
669
  Log.d(TAG, "Account filter provided: " + (hasAccountFilter ? "[REDACTED]" : "none"));
668
670
 
671
+ final boolean queryInApp = productType == null || productType.equals("inapp");
672
+ final boolean querySubs = productType == null || productType.equals("subs");
673
+
674
+ if (!queryInApp && !querySubs) {
675
+ Log.d(TAG, "Unknown product type filter provided, returning empty result");
676
+ JSObject result = new JSObject();
677
+ result.put("purchases", new JSONArray());
678
+ call.resolve(result);
679
+ return;
680
+ }
681
+
669
682
  this.initBillingClient(null);
670
683
 
671
684
  JSONArray allPurchases = new JSONArray();
685
+ AtomicInteger pendingQueries = new AtomicInteger((queryInApp ? 1 : 0) + (querySubs ? 1 : 0));
686
+ AtomicBoolean finished = new AtomicBoolean(false);
687
+
688
+ Runnable maybeFinish = () -> {
689
+ int remaining = pendingQueries.decrementAndGet();
690
+ Log.d(TAG, "Pending purchase queries remaining: " + remaining);
691
+ if (remaining <= 0 && finished.compareAndSet(false, true)) {
692
+ JSObject result = new JSObject();
693
+ result.put("purchases", allPurchases);
694
+ Log.d(TAG, "Returning " + allPurchases.length() + " purchases");
695
+ closeBillingClient();
696
+ call.resolve(result);
697
+ }
698
+ };
672
699
 
673
700
  try {
674
- // Query in-app purchases if no filter or if filter is "inapp"
675
- if (productType == null || productType.equals("inapp")) {
701
+ if (queryInApp) {
676
702
  Log.d(TAG, "Querying in-app purchases");
677
703
  QueryPurchasesParams queryInAppParams = QueryPurchasesParams.newBuilder()
678
704
  .setProductType(BillingClient.ProductType.INAPP)
679
705
  .build();
680
706
 
681
707
  billingClient.queryPurchasesAsync(queryInAppParams, (billingResult, purchases) -> {
682
- Log.d(TAG, "In-app purchases query result: " + billingResult.getResponseCode());
683
- if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
684
- for (Purchase purchase : purchases) {
685
- Log.d(TAG, "Processing in-app purchase: " + purchase.getOrderId());
686
- AccountIdentifiers accountIdentifiers = purchase.getAccountIdentifiers();
687
- String purchaseAccountId = accountIdentifiers != null ? accountIdentifiers.getObfuscatedAccountId() : null;
688
- if (hasAccountFilter) {
689
- if (purchaseAccountId == null || !purchaseAccountId.equals(accountFilter)) {
708
+ try {
709
+ Log.d(TAG, "In-app purchases query result: " + billingResult.getResponseCode());
710
+ if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
711
+ for (Purchase purchase : purchases) {
712
+ Log.d(TAG, "Processing in-app purchase: " + purchase.getOrderId());
713
+ AccountIdentifiers accountIdentifiers = purchase.getAccountIdentifiers();
714
+ String purchaseAccountId = accountIdentifiers != null ? accountIdentifiers.getObfuscatedAccountId() : null;
715
+ if (hasAccountFilter && (purchaseAccountId == null || !purchaseAccountId.equals(accountFilter))) {
690
716
  Log.d(TAG, "Skipping in-app purchase due to account filter mismatch");
691
717
  continue;
692
718
  }
719
+ JSObject purchaseData = new JSObject();
720
+ purchaseData.put("transactionId", purchase.getPurchaseToken());
721
+ purchaseData.put(
722
+ "productIdentifier",
723
+ purchase.getProducts().isEmpty() ? null : purchase.getProducts().get(0)
724
+ );
725
+ purchaseData.put(
726
+ "purchaseDate",
727
+ new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.US).format(
728
+ new java.util.Date(purchase.getPurchaseTime())
729
+ )
730
+ );
731
+ purchaseData.put("quantity", purchase.getQuantity());
732
+ purchaseData.put("productType", "inapp");
733
+ purchaseData.put("orderId", purchase.getOrderId());
734
+ purchaseData.put("purchaseToken", purchase.getPurchaseToken());
735
+ purchaseData.put("isAcknowledged", purchase.isAcknowledged());
736
+ purchaseData.put("purchaseState", String.valueOf(purchase.getPurchaseState()));
737
+ purchaseData.put("appAccountToken", purchaseAccountId);
738
+ purchaseData.put("willCancel", null);
739
+ synchronized (allPurchases) {
740
+ allPurchases.put(purchaseData);
741
+ }
693
742
  }
694
- JSObject purchaseData = new JSObject();
695
- purchaseData.put("transactionId", purchase.getPurchaseToken());
696
- purchaseData.put("productIdentifier", purchase.getProducts().get(0));
697
- purchaseData.put(
698
- "purchaseDate",
699
- new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.US).format(
700
- new java.util.Date(purchase.getPurchaseTime())
701
- )
702
- );
703
- purchaseData.put("quantity", purchase.getQuantity());
704
- purchaseData.put("productType", "inapp");
705
- purchaseData.put("orderId", purchase.getOrderId());
706
- purchaseData.put("purchaseToken", purchase.getPurchaseToken());
707
- purchaseData.put("isAcknowledged", purchase.isAcknowledged());
708
- purchaseData.put("purchaseState", String.valueOf(purchase.getPurchaseState()));
709
- purchaseData.put("appAccountToken", purchaseAccountId);
710
- // Add cancellation information - ALWAYS set willCancel
711
- // Note: Android doesn't provide direct cancellation information in the Purchase object
712
- purchaseData.put("willCancel", null); // Default to null, would need API call to determine actual cancellation date
713
- allPurchases.put(purchaseData);
743
+ } else {
744
+ Log.d(TAG, "In-app purchase query failed: " + billingResult.getDebugMessage());
714
745
  }
746
+ } catch (Exception ex) {
747
+ Log.d(TAG, "Error processing in-app purchase query: " + ex.getMessage());
748
+ } finally {
749
+ maybeFinish.run();
715
750
  }
716
-
717
- // Query subscriptions if no filter or if filter is "subs"
718
- assert productType != null;
719
- // Only querying in-app, return result now
720
- JSObject result = new JSObject();
721
- result.put("purchases", allPurchases);
722
- Log.d(TAG, "Returning " + allPurchases.length() + " in-app purchases");
723
- closeBillingClient();
724
- call.resolve(result);
725
751
  });
726
- } else if (productType.equals("subs")) {
727
- // Only query subscriptions
752
+ }
753
+
754
+ if (querySubs) {
728
755
  Log.d(TAG, "Querying only subscription purchases");
729
756
  QueryPurchasesParams querySubsParams = QueryPurchasesParams.newBuilder()
730
757
  .setProductType(BillingClient.ProductType.SUBS)
731
758
  .build();
732
759
 
733
760
  billingClient.queryPurchasesAsync(querySubsParams, (billingResult, purchases) -> {
734
- Log.d(TAG, "Subscription purchases query result: " + billingResult.getResponseCode());
735
- if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
736
- for (Purchase purchase : purchases) {
737
- Log.d(TAG, "Processing subscription purchase: " + purchase.getOrderId());
738
- AccountIdentifiers accountIdentifiers = purchase.getAccountIdentifiers();
739
- String purchaseAccountId = accountIdentifiers != null ? accountIdentifiers.getObfuscatedAccountId() : null;
740
- if (hasAccountFilter) {
741
- if (purchaseAccountId == null || !purchaseAccountId.equals(accountFilter)) {
761
+ try {
762
+ Log.d(TAG, "Subscription purchases query result: " + billingResult.getResponseCode());
763
+ if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
764
+ for (Purchase purchase : purchases) {
765
+ Log.d(TAG, "Processing subscription purchase: " + purchase.getOrderId());
766
+ AccountIdentifiers accountIdentifiers = purchase.getAccountIdentifiers();
767
+ String purchaseAccountId = accountIdentifiers != null ? accountIdentifiers.getObfuscatedAccountId() : null;
768
+ if (hasAccountFilter && (purchaseAccountId == null || !purchaseAccountId.equals(accountFilter))) {
742
769
  Log.d(TAG, "Skipping subscription purchase due to account filter mismatch");
743
770
  continue;
744
771
  }
772
+ JSObject purchaseData = new JSObject();
773
+ purchaseData.put("transactionId", purchase.getPurchaseToken());
774
+ purchaseData.put(
775
+ "productIdentifier",
776
+ purchase.getProducts().isEmpty() ? null : purchase.getProducts().get(0)
777
+ );
778
+ purchaseData.put(
779
+ "purchaseDate",
780
+ new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.US).format(
781
+ new java.util.Date(purchase.getPurchaseTime())
782
+ )
783
+ );
784
+ purchaseData.put("quantity", purchase.getQuantity());
785
+ purchaseData.put("productType", "subs");
786
+ purchaseData.put("orderId", purchase.getOrderId());
787
+ purchaseData.put("purchaseToken", purchase.getPurchaseToken());
788
+ purchaseData.put("isAcknowledged", purchase.isAcknowledged());
789
+ purchaseData.put("purchaseState", String.valueOf(purchase.getPurchaseState()));
790
+ purchaseData.put("appAccountToken", purchaseAccountId);
791
+ purchaseData.put("willCancel", null);
792
+ synchronized (allPurchases) {
793
+ allPurchases.put(purchaseData);
794
+ }
745
795
  }
746
- JSObject purchaseData = new JSObject();
747
- purchaseData.put("transactionId", purchase.getPurchaseToken());
748
- purchaseData.put("productIdentifier", purchase.getProducts().get(0));
749
- purchaseData.put(
750
- "purchaseDate",
751
- new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.US).format(
752
- new java.util.Date(purchase.getPurchaseTime())
753
- )
754
- );
755
- purchaseData.put("quantity", purchase.getQuantity());
756
- purchaseData.put("productType", "subs");
757
- purchaseData.put("orderId", purchase.getOrderId());
758
- purchaseData.put("purchaseToken", purchase.getPurchaseToken());
759
- purchaseData.put("isAcknowledged", purchase.isAcknowledged());
760
- purchaseData.put("purchaseState", String.valueOf(purchase.getPurchaseState()));
761
- purchaseData.put("appAccountToken", purchaseAccountId);
762
- // Add cancellation information - ALWAYS set willCancel
763
- // Note: Android doesn't provide direct cancellation information in the Purchase object
764
- purchaseData.put("willCancel", null); // Default to null, would need API call to determine actual cancellation date
765
- allPurchases.put(purchaseData);
796
+ } else {
797
+ Log.d(TAG, "Subscription purchase query failed: " + billingResult.getDebugMessage());
766
798
  }
799
+ } catch (Exception ex) {
800
+ Log.d(TAG, "Error processing subscription purchase query: " + ex.getMessage());
801
+ } finally {
802
+ maybeFinish.run();
767
803
  }
768
-
769
- JSObject result = new JSObject();
770
- result.put("purchases", allPurchases);
771
- Log.d(TAG, "Returning " + allPurchases.length() + " subscription purchases");
772
- closeBillingClient();
773
- call.resolve(result);
774
804
  });
775
805
  }
776
806
  } catch (Exception e) {
777
807
  Log.d(TAG, "Exception during getPurchases: " + e.getMessage());
808
+ finished.set(true);
778
809
  closeBillingClient();
779
810
  call.reject(e.getMessage());
780
811
  }
@@ -20,7 +20,7 @@ public class NativePurchasesPlugin: CAPPlugin, CAPBridgedPlugin {
20
20
  CAPPluginMethod(name: "getPurchases", returnType: CAPPluginReturnPromise)
21
21
  ]
22
22
 
23
- private let PLUGIN_VERSION = "0.0.25"
23
+ private let PLUGIN_VERSION: String = "7.9.1"
24
24
  private var transactionUpdatesTask: Task<Void, Never>?
25
25
 
26
26
  @objc func getPluginVersion(_ call: CAPPluginCall) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/native-purchases",
3
- "version": "7.8.8",
3
+ "version": "7.9.1",
4
4
  "description": "In-app Subscriptions Made Easy",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",