From 43d7c6a216c15e5b44365df39476d5bab26a82b3 Mon Sep 17 00:00:00 2001 From: Ajit Kumar Date: Wed, 2 Jul 2025 03:13:29 +0530 Subject: [PATCH 1/2] chore(update IAP library to lateset version) --- package-lock.json | 4 ++-- package.json | 2 +- src/plugins/iap/plugin.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ffde819d0..47ea2b3fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "com.foxdebug.acode", - "version": "1.11.3", + "version": "1.11.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "com.foxdebug.acode", - "version": "1.11.3", + "version": "1.11.4", "license": "MIT", "dependencies": { "@deadlyjack/ajax": "^1.2.6", diff --git a/package.json b/package.json index fe0ad27fa..8f4cd24d1 100644 --- a/package.json +++ b/package.json @@ -118,4 +118,4 @@ "yargs": "^17.7.2" }, "browserslist": "cover 100%,not android < 5" -} +} \ No newline at end of file diff --git a/src/plugins/iap/plugin.xml b/src/plugins/iap/plugin.xml index 8cfdfdf54..f1d6300c1 100644 --- a/src/plugins/iap/plugin.xml +++ b/src/plugins/iap/plugin.xml @@ -22,6 +22,6 @@ - + \ No newline at end of file From 4166c2b89696708016ebb0614a2feb1abe17efee Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:20:24 +0530 Subject: [PATCH 2/2] feat: migrate Google Play Billing Library to v8+ API Replace deprecated querySkuDetailsAsync with queryProductDetailsAsync for compatibility with Google Play Billing Library v8 and higher. The migration ensures proper compatibility with modern Google Play Billing while maintaining backward compatibility for existing purchase flows. --- src/lib/acode.js | 2 +- src/lib/installPlugin.js | 2 +- src/lib/removeAds.js | 2 +- src/pages/donate/product.hbs | 2 +- src/pages/plugin/plugin.js | 2 +- src/plugins/iap/src/com/foxdebug/iap/Iap.java | 172 +++++++++++------- src/sidebarApps/extensions/index.js | 2 +- 7 files changed, 110 insertions(+), 74 deletions(-) diff --git a/src/lib/acode.js b/src/lib/acode.js index 8c6409c07..ea024037e 100644 --- a/src/lib/acode.js +++ b/src/lib/acode.js @@ -343,7 +343,7 @@ export default class Acode { ); return helpers.promisify( iap.purchase, - product.json, + product.productId, ); }); } diff --git a/src/lib/installPlugin.js b/src/lib/installPlugin.js index 8521c014d..e7a651aa4 100644 --- a/src/lib/installPlugin.js +++ b/src/lib/installPlugin.js @@ -343,7 +343,7 @@ async function resolveDep(manifest) { iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, onerror)); loaderDialog.setMessage(strings["loading..."]); - await helpers.promisify(iap.purchase, product.json); + await helpers.promisify(iap.purchase, product.productId); async function onpurchase(e) { const purchase = await getPurchase(product.productId); diff --git a/src/lib/removeAds.js b/src/lib/removeAds.js index 9bbe87486..2188e810e 100644 --- a/src/lib/removeAds.js +++ b/src/lib/removeAds.js @@ -13,7 +13,7 @@ export default function removeAds() { iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, reject)); iap.purchase( - product.json, + product.productId, (code) => { // ignore }, diff --git a/src/pages/donate/product.hbs b/src/pages/donate/product.hbs index e33fe74f9..9b5a5a4b9 100644 --- a/src/pages/donate/product.hbs +++ b/src/pages/donate/product.hbs @@ -13,7 +13,7 @@ {{#author}}

- {{author}}

{{/author}} - diff --git a/src/pages/plugin/plugin.js b/src/pages/plugin/plugin.js index f72be3277..c5be9e394 100644 --- a/src/pages/plugin/plugin.js +++ b/src/pages/plugin/plugin.js @@ -240,7 +240,7 @@ export default async function PluginInclude( iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, onerror)); $button.textContent = strings["loading..."]; - await helpers.promisify(iap.purchase, product.json); + await helpers.promisify(iap.purchase, product.productId); async function onpurchase(e) { const purchase = await getPurchase(product.productId); diff --git a/src/plugins/iap/src/com/foxdebug/iap/Iap.java b/src/plugins/iap/src/com/foxdebug/iap/Iap.java index 979b84661..469443986 100644 --- a/src/plugins/iap/src/com/foxdebug/iap/Iap.java +++ b/src/plugins/iap/src/com/foxdebug/iap/Iap.java @@ -7,23 +7,24 @@ import com.android.billingclient.api.AcknowledgePurchaseResponseListener; import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingClient.BillingResponseCode; -import com.android.billingclient.api.BillingClient.SkuType; import com.android.billingclient.api.BillingClientStateListener; import com.android.billingclient.api.BillingFlowParams; import com.android.billingclient.api.BillingResult; import com.android.billingclient.api.ConsumeParams; import com.android.billingclient.api.ConsumeResponseListener; +import com.android.billingclient.api.PendingPurchasesParams; +import com.android.billingclient.api.ProductDetails; +import com.android.billingclient.api.ProductDetailsResponseListener; import com.android.billingclient.api.Purchase; import com.android.billingclient.api.PurchasesResponseListener; import com.android.billingclient.api.PurchasesUpdatedListener; +import com.android.billingclient.api.QueryProductDetailsParams; +import com.android.billingclient.api.QueryProductDetailsResult; import com.android.billingclient.api.QueryPurchasesParams; -import com.android.billingclient.api.SkuDetails; -import com.android.billingclient.api.SkuDetailsParams; -import com.android.billingclient.api.SkuDetailsResponseListener; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.lang.ref.WeakReference; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPlugin; @@ -103,9 +104,10 @@ public void run() { } private BillingClient getBillingClient() { - return BillingClient - .newBuilder(this.contextRef.get()) - .enablePendingPurchases() + return BillingClient.newBuilder(this.contextRef.get()) + .enablePendingPurchases( + PendingPurchasesParams.newBuilder().enableOneTimeProducts().build() + ) .setListener( new PurchasesUpdatedListener() { public void onPurchasesUpdated( @@ -143,8 +145,7 @@ private void setPurchaseUpdatedListener(CallbackContext callbackContext) { } private void consume(String token, CallbackContext callbackContext) { - ConsumeParams consumeParams = ConsumeParams - .newBuilder() + ConsumeParams consumeParams = ConsumeParams.newBuilder() .setPurchaseToken(token) .build(); billingClient.consumeAsync( @@ -200,37 +201,49 @@ private void getProducts( callbackContext.error("Billing client is not connected"); return; } - SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); - params.setSkusList(idList).setType(SkuType.INAPP); + List productList = new ArrayList<>(); + for (String productId : idList) { + productList.add( + QueryProductDetailsParams.Product.newBuilder() + .setProductId(productId) + .setProductType(BillingClient.ProductType.INAPP) + .build() + ); + } + QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder() + .setProductList(productList) + .build(); - billingClient.querySkuDetailsAsync( - params.build(), - new SkuDetailsResponseListener() { - public void onSkuDetailsResponse( + billingClient.queryProductDetailsAsync( + params, + new ProductDetailsResponseListener() { + public void onProductDetailsResponse( BillingResult billingResult, - List skuDetailsList + QueryProductDetailsResult queryProductDetailsResult ) { try { int responseCode = billingResult.getResponseCode(); if (responseCode == BillingResponseCode.OK) { + List productDetailsList = queryProductDetailsResult.getProductDetailsList(); JSONArray products = new JSONArray(); - Log.d("IAP", "Got " + skuDetailsList.size() + " products"); - for (SkuDetails skuDetails : skuDetailsList) { + for (ProductDetails productDetails : productDetailsList) { JSONObject product = new JSONObject(); - product.put("json", skuDetails.getOriginalJson()); - product.put("productId", skuDetails.getSku()); - product.put("title", skuDetails.getTitle()); - product.put("description", skuDetails.getDescription()); - product.put("price", skuDetails.getPrice()); - product.put( - "priceAmountMicros", - skuDetails.getPriceAmountMicros() - ); - product.put( - "priceCurrencyCode", - skuDetails.getPriceCurrencyCode() - ); - product.put("type", skuDetails.getType()); + ProductDetails.OneTimePurchaseOfferDetails offerDetails = productDetails.getOneTimePurchaseOfferDetails(); + if (offerDetails != null) { + product.put("productId", productDetails.getProductId()); + product.put("title", productDetails.getTitle()); + product.put("description", productDetails.getDescription()); + product.put("price", offerDetails.getFormattedPrice()); + product.put( + "priceAmountMicros", + offerDetails.getPriceAmountMicros() + ); + product.put( + "priceCurrencyCode", + offerDetails.getPriceCurrencyCode() + ); + product.put("type", productDetails.getProductType()); + } products.put(product); } callbackContext.success(products); @@ -245,21 +258,64 @@ public void onSkuDetailsResponse( ); } - private void purchase(String json, CallbackContext callbackContext) { + private void purchase(String productIdOrJson, CallbackContext callbackContext) { try { - SkuDetails skuDetails = new SkuDetails(json); - BillingResult result = billingClient.launchBillingFlow( - activityRef.get(), - BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build() - ); - int responseCode = result.getResponseCode(); - if (responseCode == BillingResponseCode.OK) { - callbackContext.success(); - } else { - callbackContext.error(responseCode); + if (productIdOrJson == null || productIdOrJson.trim().isEmpty()) { + callbackContext.error("Product ID cannot be null or empty"); + return; } - } catch (JSONException e) { - callbackContext.error(e.getMessage()); + + final String productId = productIdOrJson; + + List productList = new ArrayList<>(); + productList.add( + QueryProductDetailsParams.Product.newBuilder() + .setProductId(productId) + .setProductType(BillingClient.ProductType.INAPP) + .build() + ); + QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder() + .setProductList(productList) + .build(); + + billingClient.queryProductDetailsAsync( + params, + new ProductDetailsResponseListener() { + public void onProductDetailsResponse( + BillingResult billingResult, + QueryProductDetailsResult queryProductDetailsResult + ) { + if (billingResult.getResponseCode() == BillingResponseCode.OK) { + List productDetailsList = queryProductDetailsResult.getProductDetailsList(); + if (!productDetailsList.isEmpty()) { + ProductDetails productDetails = productDetailsList.get(0); + BillingResult result = billingClient.launchBillingFlow( + activityRef.get(), + BillingFlowParams.newBuilder().setProductDetailsParamsList( + Arrays.asList( + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(productDetails) + .build() + ) + ).build() + ); + int responseCode = result.getResponseCode(); + if (responseCode == BillingResponseCode.OK) { + callbackContext.success(); + } else { + callbackContext.error(responseCode); + } + } else { + callbackContext.error("No product details found for: " + productId); + } + } else { + callbackContext.error(billingResult.getResponseCode()); + } + } + } + ); + } catch (Exception e) { + callbackContext.error("Purchase error: " + e.getMessage()); } } @@ -270,8 +326,7 @@ private void getPurchases(CallbackContext callbackContext) { return; } - QueryPurchasesParams params = QueryPurchasesParams - .newBuilder() + QueryPurchasesParams params = QueryPurchasesParams.newBuilder() .setProductType(BillingClient.ProductType.INAPP) .build(); billingClient.queryPurchasesAsync( @@ -310,8 +365,7 @@ private void acknowledgePurchase( return; } - AcknowledgePurchaseParams params = AcknowledgePurchaseParams - .newBuilder() + AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder() .setPurchaseToken(purchaseToken) .build(); @@ -339,10 +393,8 @@ private JSONObject purchaseToJson(Purchase purchase) throws JSONException { } item.put("productIds", skuArray); item.put("orderId", purchase.getOrderId()); - item.put("sate", purchase.getPurchaseState()); item.put("signature", purchase.getSignature()); item.put("purchaseTime", purchase.getPurchaseTime()); - item.put("isAcknowledged", purchase.isAcknowledged()); item.put("purchaseToken", purchase.getPurchaseToken()); item.put("purchaseState", purchase.getPurchaseState()); item.put("isAcknowledged", purchase.isAcknowledged()); @@ -365,22 +417,6 @@ private String getString(JSONArray args, int index) { } } - private int getInt(JSONArray args, int index) { - try { - return args.getInt(index); - } catch (JSONException e) { - return 0; - } - } - - private boolean getBoolean(JSONArray args, int index) { - try { - return args.getBoolean(index); - } catch (JSONException e) { - return false; - } - } - private List getStringList(JSONArray args, int index) { try { JSONArray array = args.getJSONArray(index); diff --git a/src/sidebarApps/extensions/index.js b/src/sidebarApps/extensions/index.js index f540d3672..2199f0bf0 100644 --- a/src/sidebarApps/extensions/index.js +++ b/src/sidebarApps/extensions/index.js @@ -534,7 +534,7 @@ function ListItem({ icon, name, id, version, downloads, installed, source }) { iap.setPurchaseUpdatedListener( ...purchaseListener(onpurchase, onerror), ); - await helpers.promisify(iap.purchase, product.json); + await helpers.promisify(iap.purchase, product.productId); async function onpurchase(e) { const purchase = await getPurchase(product.productId);