1717import com .google .android .gms .ads .MobileAds ;
1818
1919import java .util .ArrayList ;
20+ import java .util .Collections ;
2021import java .util .HashMap ;
2122import java .util .List ;
2223import java .util .Map ;
@@ -39,18 +40,21 @@ public SupportRepository(Context context) {
3940 public void initBillingClient (Runnable onConnected ) {
4041 billingClient = BillingClient .newBuilder (context )
4142 .setListener ((billingResult , purchases ) -> {
43+ // To be implemented in a later release
4244 })
4345 .enablePendingPurchases (
4446 PendingPurchasesParams .newBuilder ()
4547 .enableOneTimeProducts ()
4648 .build ())
49+ // Added as a best practice from the official documentation for v8+
50+ .enableAutoServiceReconnection ()
4751 .build ();
4852
4953 billingClient .startConnection (new BillingClientStateListener () {
5054 @ Override
5155 public void onBillingSetupFinished (@ NonNull BillingResult billingResult ) {
5256 if (billingResult .getResponseCode () == BillingClient .BillingResponseCode .OK ) {
53- // Billing service connected
57+ // The BillingClient is ready. You can query purchases here.
5458 if (onConnected != null ) {
5559 onConnected .run ();
5660 }
@@ -59,7 +63,9 @@ public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
5963
6064 @ Override
6165 public void onBillingServiceDisconnected () {
62- // Attempt reconnection or handle gracefully
66+ // Try to restart the connection on the next request to
67+ // Google Play by calling the startConnection() method.
68+ // With enableAutoServiceReconnection(), this is handled automatically.
6369 }
6470 });
6571 }
@@ -73,53 +79,76 @@ public void queryProductDetails(List<String> productIds, OnProductDetailsListene
7379 return ;
7480 }
7581
76- List <QueryProductDetailsParams .Product > products = new ArrayList <>();
82+ List <QueryProductDetailsParams .Product > productList = new ArrayList <>();
7783 for (String id : productIds ) {
78- products .add (QueryProductDetailsParams .Product .newBuilder ()
79- .setProductId (id )
80- .setProductType (BillingClient .ProductType .INAPP )
81- .build ());
84+ productList .add (
85+ QueryProductDetailsParams .Product .newBuilder ()
86+ .setProductId (id )
87+ .setProductType (BillingClient .ProductType .INAPP )
88+ .build ()
89+ );
8290 }
8391
8492 QueryProductDetailsParams params = QueryProductDetailsParams .newBuilder ()
85- .setProductList (products )
93+ .setProductList (productList )
8694 .build ();
8795
88- billingClient .queryProductDetailsAsync (params , result -> {
89- BillingResult billingResult = result .getBillingResult ();
96+ // **FIXED**: The lambda now correctly accepts a single QueryProductDetailsResult
97+ // object as the second parameter, directly matching the official documentation.
98+ billingClient .queryProductDetailsAsync (params , (billingResult , queryProductDetailsResult ) -> {
9099 if (billingResult .getResponseCode () == BillingClient .BillingResponseCode .OK ) {
91- List <ProductDetails > productDetailsList = result .getProductDetailsList ();
92- if (productDetailsList != null && !productDetailsList .isEmpty ()) {
100+
101+ // The list of products is retrieved from the QueryProductDetailsResult object.
102+ List <ProductDetails > productDetailsList = queryProductDetailsResult .getProductDetailsList ();
103+
104+ if (!productDetailsList .isEmpty ()) {
93105 for (ProductDetails productDetails : productDetailsList ) {
94106 productDetailsMap .put (productDetails .getProductId (), productDetails );
95107 }
96108 if (listener != null ) {
97109 listener .onProductDetailsRetrieved (productDetailsList );
98110 }
99111 }
112+ // Optionally handle unfetched products if needed:
113+ // List<UnfetchedProduct> unfetched = queryProductDetailsResult.getUnfetchedProductList();
100114 }
115+ // Handle other billingResult response codes here if necessary.
101116 });
102117 }
103118
119+
104120 /**
105121 * Launch the billing flow for a particular product.
106122 */
107123 public void initiatePurchase (Activity activity , String productId ) {
108- if (productDetailsMap .containsKey (productId )) {
109- ProductDetails details = productDetailsMap .get (productId );
110- if (details != null ) {
111- BillingFlowParams .ProductDetailsParams productParams =
112- BillingFlowParams .ProductDetailsParams .newBuilder ()
113- .setProductDetails (details )
114- .build ();
115- BillingFlowParams flowParams = BillingFlowParams .newBuilder ()
116- .setProductDetailsParamsList (List .of (productParams ))
117- .build ();
118- billingClient .launchBillingFlow (activity , flowParams );
124+ ProductDetails details = productDetailsMap .get (productId );
125+ if (details != null ) {
126+ // Note: In a real app, you would select a specific offer. For simplicity,
127+ // we're assuming there's only one or we're using the base plan.
128+ // For subscriptions, this would be ProductDetails.getSubscriptionOfferDetails()
129+ String offerToken = "" ;
130+ if (details .getOneTimePurchaseOfferDetails () != null ) {
131+ offerToken = details .getOneTimePurchaseOfferDetails ().getOfferToken ();
119132 }
133+
134+ assert offerToken != null ;
135+ List <BillingFlowParams .ProductDetailsParams > productDetailsParamsList =
136+ Collections .singletonList (
137+ BillingFlowParams .ProductDetailsParams .newBuilder ()
138+ .setProductDetails (details )
139+ .setOfferToken (offerToken )
140+ .build ()
141+ );
142+
143+ BillingFlowParams flowParams = BillingFlowParams .newBuilder ()
144+ .setProductDetailsParamsList (productDetailsParamsList )
145+ .build ();
146+
147+ billingClient .launchBillingFlow (activity , flowParams );
120148 }
121149 }
122150
151+
123152 /**
124153 * Initialize Mobile Ads (usually done once in your app, but
125154 * can be done here if needed for the support screen).
@@ -135,5 +164,4 @@ public void initMobileAds(ActivitySupportBinding binding) {
135164 public interface OnProductDetailsListener {
136165 void onProductDetailsRetrieved (List <ProductDetails > productDetailsList );
137166 }
138-
139167}
0 commit comments