77import com .android .billingclient .api .AcknowledgePurchaseResponseListener ;
88import com .android .billingclient .api .BillingClient ;
99import com .android .billingclient .api .BillingClient .BillingResponseCode ;
10- import com .android .billingclient .api .BillingClient .SkuType ;
1110import com .android .billingclient .api .BillingClientStateListener ;
1211import com .android .billingclient .api .BillingFlowParams ;
1312import com .android .billingclient .api .BillingResult ;
1413import com .android .billingclient .api .ConsumeParams ;
1514import 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 ;
1618import com .android .billingclient .api .Purchase ;
1719import com .android .billingclient .api .PurchasesResponseListener ;
1820import com .android .billingclient .api .PurchasesUpdatedListener ;
21+ import com .android .billingclient .api .QueryProductDetailsParams ;
22+ import com .android .billingclient .api .QueryProductDetailsResult ;
1923import 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 ;
2325import java .util .ArrayList ;
2426import java .util .Arrays ;
2527import java .util .List ;
26- import java .lang .ref .WeakReference ;
2728import org .apache .cordova .CallbackContext ;
2829import org .apache .cordova .CordovaInterface ;
2930import 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 );
0 commit comments