@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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
695
|
-
|
|
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
|
-
}
|
|
727
|
-
|
|
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
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
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
|
-
|
|
747
|
-
|
|
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 = "
|
|
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) {
|