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.
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.
When someone else is trying to purchase an app feature I can see that this Fatal Exception is thrown:
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)
The SKU's are still active on my "Managed Products" list.
This is the code that I use to initialize the billing client (within a fragment):
billingClient = BillingClient.newBuilder(getActivity())
.enablePendingPurchases()
.setListener(purchasesUpdatedListener)
.build();
This is the code that I use to start the connection:
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
Log.d(TAG, "Connection finished");
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
List<String> skuList = new ArrayList<>();
skuList.add("unlock_keyboard");
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<SkuDetails> skuDetailsList) {
// Process the result.
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
for (Object skuDetailsObject : skuDetailsList) {
skuDetails = (SkuDetails) skuDetailsObject;
sku = skuDetails.getSku();
}
Log.d(TAG, "i got response");
Log.d(TAG, String.valueOf(billingResult.getResponseCode()));
Log.d(TAG, billingResult.getDebugMessage());
}
}
});
}
}
This is the code that I use to handle the purchase:
PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null) {
for (Purchase purchase : list) {
handlePurchase(purchase);
Log.d(TAG, "Purchase completed" + billingResult.getResponseCode());
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Log.d(TAG, "User Canceled" + billingResult.getResponseCode());
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
if ("unlock_keyboard".equals(sku)) {
KeyboardAlreadyPurchasedConfirmation();
}
Log.d(TAG, "Item Already owned" + billingResult.getResponseCode());
}
}
};
In order to launch the billing flow, the user must click on a button within a dialog. Here is the code:
builder.setPositiveButton(
getString(R.string.purchase_keyboard),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
sku = "unlock_keyboard";
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
billingClient.launchBillingFlow(Objects.requireNonNull(getActivity()), flowParams);
}
});
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.
Thanks a lot in advance.