Skip to content

Commit 4166c2b

Browse files
committed
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.
1 parent 9d9c619 commit 4166c2b

File tree

7 files changed

+110
-74
lines changed

7 files changed

+110
-74
lines changed

src/lib/acode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ export default class Acode {
343343
);
344344
return helpers.promisify(
345345
iap.purchase,
346-
product.json,
346+
product.productId,
347347
);
348348
});
349349
}

src/lib/installPlugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ async function resolveDep(manifest) {
343343

344344
iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, onerror));
345345
loaderDialog.setMessage(strings["loading..."]);
346-
await helpers.promisify(iap.purchase, product.json);
346+
await helpers.promisify(iap.purchase, product.productId);
347347

348348
async function onpurchase(e) {
349349
const purchase = await getPurchase(product.productId);

src/lib/removeAds.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function removeAds() {
1313
iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, reject));
1414

1515
iap.purchase(
16-
product.json,
16+
product.productId,
1717
(code) => {
1818
// ignore
1919
},

src/pages/donate/product.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
{{#author}}
1414
<p class="author">- {{author}}</p>
1515
{{/author}}
16-
<button class="donate-button" action="donate" value="{{json}}">
16+
<button class="donate-button" action="donate" value="{{productId}}">
1717
{{donate}}
1818
</button>
1919
</div>

src/pages/plugin/plugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ export default async function PluginInclude(
240240

241241
iap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, onerror));
242242
$button.textContent = strings["loading..."];
243-
await helpers.promisify(iap.purchase, product.json);
243+
await helpers.promisify(iap.purchase, product.productId);
244244

245245
async function onpurchase(e) {
246246
const purchase = await getPurchase(product.productId);

src/plugins/iap/src/com/foxdebug/iap/Iap.java

Lines changed: 104 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,24 @@
77
import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
88
import com.android.billingclient.api.BillingClient;
99
import com.android.billingclient.api.BillingClient.BillingResponseCode;
10-
import com.android.billingclient.api.BillingClient.SkuType;
1110
import com.android.billingclient.api.BillingClientStateListener;
1211
import com.android.billingclient.api.BillingFlowParams;
1312
import com.android.billingclient.api.BillingResult;
1413
import com.android.billingclient.api.ConsumeParams;
1514
import com.android.billingclient.api.ConsumeResponseListener;
15+
import com.android.billingclient.api.PendingPurchasesParams;
16+
import com.android.billingclient.api.ProductDetails;
17+
import com.android.billingclient.api.ProductDetailsResponseListener;
1618
import com.android.billingclient.api.Purchase;
1719
import com.android.billingclient.api.PurchasesResponseListener;
1820
import com.android.billingclient.api.PurchasesUpdatedListener;
21+
import com.android.billingclient.api.QueryProductDetailsParams;
22+
import com.android.billingclient.api.QueryProductDetailsResult;
1923
import com.android.billingclient.api.QueryPurchasesParams;
20-
import com.android.billingclient.api.SkuDetails;
21-
import com.android.billingclient.api.SkuDetailsParams;
22-
import com.android.billingclient.api.SkuDetailsResponseListener;
24+
import java.lang.ref.WeakReference;
2325
import java.util.ArrayList;
2426
import java.util.Arrays;
2527
import java.util.List;
26-
import java.lang.ref.WeakReference;
2728
import org.apache.cordova.CallbackContext;
2829
import org.apache.cordova.CordovaInterface;
2930
import org.apache.cordova.CordovaPlugin;
@@ -103,9 +104,10 @@ public void run() {
103104
}
104105

105106
private BillingClient getBillingClient() {
106-
return BillingClient
107-
.newBuilder(this.contextRef.get())
108-
.enablePendingPurchases()
107+
return BillingClient.newBuilder(this.contextRef.get())
108+
.enablePendingPurchases(
109+
PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()
110+
)
109111
.setListener(
110112
new PurchasesUpdatedListener() {
111113
public void onPurchasesUpdated(
@@ -143,8 +145,7 @@ private void setPurchaseUpdatedListener(CallbackContext callbackContext) {
143145
}
144146

145147
private void consume(String token, CallbackContext callbackContext) {
146-
ConsumeParams consumeParams = ConsumeParams
147-
.newBuilder()
148+
ConsumeParams consumeParams = ConsumeParams.newBuilder()
148149
.setPurchaseToken(token)
149150
.build();
150151
billingClient.consumeAsync(
@@ -200,37 +201,49 @@ private void getProducts(
200201
callbackContext.error("Billing client is not connected");
201202
return;
202203
}
203-
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
204-
params.setSkusList(idList).setType(SkuType.INAPP);
204+
List<QueryProductDetailsParams.Product> productList = new ArrayList<>();
205+
for (String productId : idList) {
206+
productList.add(
207+
QueryProductDetailsParams.Product.newBuilder()
208+
.setProductId(productId)
209+
.setProductType(BillingClient.ProductType.INAPP)
210+
.build()
211+
);
212+
}
213+
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
214+
.setProductList(productList)
215+
.build();
205216

206-
billingClient.querySkuDetailsAsync(
207-
params.build(),
208-
new SkuDetailsResponseListener() {
209-
public void onSkuDetailsResponse(
217+
billingClient.queryProductDetailsAsync(
218+
params,
219+
new ProductDetailsResponseListener() {
220+
public void onProductDetailsResponse(
210221
BillingResult billingResult,
211-
List<SkuDetails> skuDetailsList
222+
QueryProductDetailsResult queryProductDetailsResult
212223
) {
213224
try {
214225
int responseCode = billingResult.getResponseCode();
215226
if (responseCode == BillingResponseCode.OK) {
227+
List<ProductDetails> productDetailsList = queryProductDetailsResult.getProductDetailsList();
216228
JSONArray products = new JSONArray();
217-
Log.d("IAP", "Got " + skuDetailsList.size() + " products");
218-
for (SkuDetails skuDetails : skuDetailsList) {
229+
for (ProductDetails productDetails : productDetailsList) {
219230
JSONObject product = new JSONObject();
220-
product.put("json", skuDetails.getOriginalJson());
221-
product.put("productId", skuDetails.getSku());
222-
product.put("title", skuDetails.getTitle());
223-
product.put("description", skuDetails.getDescription());
224-
product.put("price", skuDetails.getPrice());
225-
product.put(
226-
"priceAmountMicros",
227-
skuDetails.getPriceAmountMicros()
228-
);
229-
product.put(
230-
"priceCurrencyCode",
231-
skuDetails.getPriceCurrencyCode()
232-
);
233-
product.put("type", skuDetails.getType());
231+
ProductDetails.OneTimePurchaseOfferDetails offerDetails = productDetails.getOneTimePurchaseOfferDetails();
232+
if (offerDetails != null) {
233+
product.put("productId", productDetails.getProductId());
234+
product.put("title", productDetails.getTitle());
235+
product.put("description", productDetails.getDescription());
236+
product.put("price", offerDetails.getFormattedPrice());
237+
product.put(
238+
"priceAmountMicros",
239+
offerDetails.getPriceAmountMicros()
240+
);
241+
product.put(
242+
"priceCurrencyCode",
243+
offerDetails.getPriceCurrencyCode()
244+
);
245+
product.put("type", productDetails.getProductType());
246+
}
234247
products.put(product);
235248
}
236249
callbackContext.success(products);
@@ -245,21 +258,64 @@ public void onSkuDetailsResponse(
245258
);
246259
}
247260

248-
private void purchase(String json, CallbackContext callbackContext) {
261+
private void purchase(String productIdOrJson, CallbackContext callbackContext) {
249262
try {
250-
SkuDetails skuDetails = new SkuDetails(json);
251-
BillingResult result = billingClient.launchBillingFlow(
252-
activityRef.get(),
253-
BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build()
254-
);
255-
int responseCode = result.getResponseCode();
256-
if (responseCode == BillingResponseCode.OK) {
257-
callbackContext.success();
258-
} else {
259-
callbackContext.error(responseCode);
263+
if (productIdOrJson == null || productIdOrJson.trim().isEmpty()) {
264+
callbackContext.error("Product ID cannot be null or empty");
265+
return;
260266
}
261-
} catch (JSONException e) {
262-
callbackContext.error(e.getMessage());
267+
268+
final String productId = productIdOrJson;
269+
270+
List<QueryProductDetailsParams.Product> productList = new ArrayList<>();
271+
productList.add(
272+
QueryProductDetailsParams.Product.newBuilder()
273+
.setProductId(productId)
274+
.setProductType(BillingClient.ProductType.INAPP)
275+
.build()
276+
);
277+
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
278+
.setProductList(productList)
279+
.build();
280+
281+
billingClient.queryProductDetailsAsync(
282+
params,
283+
new ProductDetailsResponseListener() {
284+
public void onProductDetailsResponse(
285+
BillingResult billingResult,
286+
QueryProductDetailsResult queryProductDetailsResult
287+
) {
288+
if (billingResult.getResponseCode() == BillingResponseCode.OK) {
289+
List<ProductDetails> productDetailsList = queryProductDetailsResult.getProductDetailsList();
290+
if (!productDetailsList.isEmpty()) {
291+
ProductDetails productDetails = productDetailsList.get(0);
292+
BillingResult result = billingClient.launchBillingFlow(
293+
activityRef.get(),
294+
BillingFlowParams.newBuilder().setProductDetailsParamsList(
295+
Arrays.asList(
296+
BillingFlowParams.ProductDetailsParams.newBuilder()
297+
.setProductDetails(productDetails)
298+
.build()
299+
)
300+
).build()
301+
);
302+
int responseCode = result.getResponseCode();
303+
if (responseCode == BillingResponseCode.OK) {
304+
callbackContext.success();
305+
} else {
306+
callbackContext.error(responseCode);
307+
}
308+
} else {
309+
callbackContext.error("No product details found for: " + productId);
310+
}
311+
} else {
312+
callbackContext.error(billingResult.getResponseCode());
313+
}
314+
}
315+
}
316+
);
317+
} catch (Exception e) {
318+
callbackContext.error("Purchase error: " + e.getMessage());
263319
}
264320
}
265321

@@ -270,8 +326,7 @@ private void getPurchases(CallbackContext callbackContext) {
270326
return;
271327
}
272328

273-
QueryPurchasesParams params = QueryPurchasesParams
274-
.newBuilder()
329+
QueryPurchasesParams params = QueryPurchasesParams.newBuilder()
275330
.setProductType(BillingClient.ProductType.INAPP)
276331
.build();
277332
billingClient.queryPurchasesAsync(
@@ -310,8 +365,7 @@ private void acknowledgePurchase(
310365
return;
311366
}
312367

313-
AcknowledgePurchaseParams params = AcknowledgePurchaseParams
314-
.newBuilder()
368+
AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder()
315369
.setPurchaseToken(purchaseToken)
316370
.build();
317371

@@ -339,10 +393,8 @@ private JSONObject purchaseToJson(Purchase purchase) throws JSONException {
339393
}
340394
item.put("productIds", skuArray);
341395
item.put("orderId", purchase.getOrderId());
342-
item.put("sate", purchase.getPurchaseState());
343396
item.put("signature", purchase.getSignature());
344397
item.put("purchaseTime", purchase.getPurchaseTime());
345-
item.put("isAcknowledged", purchase.isAcknowledged());
346398
item.put("purchaseToken", purchase.getPurchaseToken());
347399
item.put("purchaseState", purchase.getPurchaseState());
348400
item.put("isAcknowledged", purchase.isAcknowledged());
@@ -365,22 +417,6 @@ private String getString(JSONArray args, int index) {
365417
}
366418
}
367419

368-
private int getInt(JSONArray args, int index) {
369-
try {
370-
return args.getInt(index);
371-
} catch (JSONException e) {
372-
return 0;
373-
}
374-
}
375-
376-
private boolean getBoolean(JSONArray args, int index) {
377-
try {
378-
return args.getBoolean(index);
379-
} catch (JSONException e) {
380-
return false;
381-
}
382-
}
383-
384420
private List<String> getStringList(JSONArray args, int index) {
385421
try {
386422
JSONArray array = args.getJSONArray(index);

src/sidebarApps/extensions/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ function ListItem({ icon, name, id, version, downloads, installed, source }) {
534534
iap.setPurchaseUpdatedListener(
535535
...purchaseListener(onpurchase, onerror),
536536
);
537-
await helpers.promisify(iap.purchase, product.json);
537+
await helpers.promisify(iap.purchase, product.productId);
538538

539539
async function onpurchase(e) {
540540
const purchase = await getPurchase(product.productId);

0 commit comments

Comments
 (0)