title: Android - Google Play Billing Library error in production
body: <p>I have an Android app released on the Google Play Store, and last week I released a new update, just to fix some small issues. Starting with the day when I added the updated version on the Play Store, I could see on Firebase Crashlytics that there are issues when someone is trying to purchase an app feature.</p>
  <p>Before I released the updated version in production, I added the app on the Alpha Testing so I can make sure that the InAppPurchase work, and it does.</p>
  <p>When someone else is trying to purchase an app feature I can see that this Fatal Exception is thrown:</p>
  <pre><code>Fatal Exception: java.lang.IllegalArgumentException: SKU cannot be null.
     at com.android.billingclient.api.BillingFlowParams$Builder.build(com.android.billingclient:billing@@3.0.0:23)
  </code></pre>
  <p>The SKU's are still active on my &quot;Managed Products&quot; list.</p>
  <p>This is the code that I use to initialize the billing client (within a fragment):</p>
  <pre><code>        billingClient = BillingClient.newBuilder(getActivity())
              .enablePendingPurchases()
              .setListener(purchasesUpdatedListener)
              .build();
  </code></pre>
  <p>This is the code that I use to start the connection:</p>
  <pre><code>billingClient.startConnection(new BillingClientStateListener() {
          @Override
          public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
              Log.d(TAG, &quot;Connection finished&quot;);
              if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                  // The BillingClient is ready. You can query purchases here.
                  List&lt;String&gt; skuList = new ArrayList&lt;&gt;();
                  skuList.add(&quot;unlock_keyboard&quot;);
                  SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
                  params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
                  billingClient.querySkuDetailsAsync(params.build(),
                          new SkuDetailsResponseListener() {
                              @Override
                              public void onSkuDetailsResponse(@NonNull BillingResult billingResult,
                                                               List&lt;SkuDetails&gt; skuDetailsList) {
                                  // Process the result.
                                  if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK &amp;&amp; skuDetailsList != null) {
                                      for (Object skuDetailsObject : skuDetailsList) {
                                          skuDetails = (SkuDetails) skuDetailsObject;
                                          sku = skuDetails.getSku();
  
  
                                      }
                                      Log.d(TAG, &quot;i got response&quot;);
                                      Log.d(TAG, String.valueOf(billingResult.getResponseCode()));
                                      Log.d(TAG, billingResult.getDebugMessage());
                                  }
                              }
                          });
              }
          }
  </code></pre>
  <p>This is the code that I use to handle the purchase:</p>
  <pre><code>PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
          @Override
          public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List&lt;Purchase&gt; list) {
              if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK &amp;&amp; list != null) {
                  for (Purchase purchase : list) {
                      handlePurchase(purchase);
                      Log.d(TAG, &quot;Purchase completed&quot; + billingResult.getResponseCode());
                  }
              } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
                  Log.d(TAG, &quot;User Canceled&quot; + billingResult.getResponseCode());
              } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
                  if (&quot;unlock_keyboard&quot;.equals(sku)) {
                      KeyboardAlreadyPurchasedConfirmation();
                  }
                  Log.d(TAG, &quot;Item Already owned&quot; + billingResult.getResponseCode());
              }
          }
      };
  </code></pre>
  <p>In order to launch the billing flow, the user must click on a button within a dialog. Here is the code:</p>
  <pre><code>        builder.setPositiveButton(
              getString(R.string.purchase_keyboard),
              new DialogInterface.OnClickListener() {
                  public void onClick(DialogInterface dialog, int id) {
  
                      sku = &quot;unlock_keyboard&quot;;
                      BillingFlowParams flowParams = BillingFlowParams.newBuilder()
                              .setSkuDetails(skuDetails)
                              .build();
                      billingClient.launchBillingFlow(Objects.requireNonNull(getActivity()), flowParams);
  
                  }
  
              });
  </code></pre>
  <p>In the previous version of my app, this situation never happened, it just started after the new update. I'll just need to know what can cause this issue, could it be a problem with my code or just a issue with Google Play Services? I'll have to specify that this was happening on different devices with different Android versions.</p>
  <p>Thanks a lot in advance.</p>