Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Billing; how to resolve "The item you were attempting to purchase could not be found"

With all the recent changes to the Google Billing library and the Developer Console it's hard to apply answers from 2014, so I am posting on this topic again with the hope of finding a modern answer.

I am using Google Billing library 2.2.0

The Issue: enter image description here


I've set up the licenses: subscriptions


I've also set up License testing: license testing


I've also published a release build to the internal test track (signed with release cert): release build


Relevant Code Sample:

private final String SKU_TEST_PROD_1YR = "test_prod_id_1_year";
private final String SKU_TEST_PROD_6MN = "test_prod_id_6_month";
private final String SKU_TEST_PROD_1MN = "test_prod_id_1_month";
private BillingClient billingClient;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    List<String> iapProdIdList = Arrays.asList(SKU_TEST_PROD_1YR, SKU_TEST_PROD_6MN, SKU_TEST_PROD_1MN);
    setupBillingClient(this, iapProdIdList);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    billingClient.endConnection();
}

private void setupBillingClient(Context context, List<String> iapProdList) {
    billingClient = BillingClient.newBuilder(context).setListener(this).build();
    billingClient.startConnection(new BillingClientStateListener() {
        @Override
        public void onBillingSetupFinished(BillingResult billingResult) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                loadAllSubscriptionProductIdDetails(iapProdList);
            }
        }

        @Override
        public void onBillingServiceDisconnected() { }
    });
}

private void loadAllSubscriptionProductIdDetails(List<String> iapProdList) {
    if (billingClient.isReady()) {
        SkuDetailsParams params = SkuDetailsParams.newBuilder().setSkusList(iapProdList).setType(BillingClient.SkuType.SUBS).build();

        billingClient.querySkuDetailsAsync(params, (billingResult, prodDetailList) -> {
            if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && !prodDetailList.isEmpty()) {

                for (SkuDetails prodDetail : prodDetailList) {

                    String productSku = prodDetail.getSku();

                    switch(productSku) {
                        case SKU_TEST_PROD_1YR : {
                            final BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().setSkuDetails(prodDetail).build();
                            buttonIap1Yr.setOnClickListener(v -> {
                                // trigger purchase - Google needs the parent activity to overlay with their UI
                                billingClient.launchBillingFlow(this, billingFlowParams);
                            });
                            break;
                        }
                        case SKU_TEST_PROD_6MN : {
                            final BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().setSkuDetails(prodDetail).build();
                            buttonIap6mth.setOnClickListener(v -> {
                                // trigger purchase - Google needs the parent activity to overlay with their UI
                                billingClient.launchBillingFlow(this, billingFlowParams);
                            });
                            break;
                        }
                        case SKU_TEST_PROD_1MN : {
                            final BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().setSkuDetails(prodDetail).build();
                            buttonIap1mth.setOnClickListener(v -> {
                                // trigger purchase - Google needs the parent activity to overlay with their UI
                                billingClient.launchBillingFlow(this, billingFlowParams);
                            });
                            break;
                        }
                        default :
                            Toast.makeText(this, "Did not find Product in-app: "+ productSku, Toast.LENGTH_SHORT).show();
                    }
                }//end of FOR
            }//end of IF
        });
    } else {
        Toast.makeText(this, "billingClient is NOT ready", Toast.LENGTH_SHORT).show();
    }
}

@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> list) {

    if (billingResult != null && list != null) {
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
            for (Purchase purchase : list) {
                if(!purchase.isAcknowledged()) acknowledgePurchase(purchase.getPurchaseToken());
            }
        }
        else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
            Toast.makeText(this, "User Canceled", Toast.LENGTH_SHORT).show();
        }
        else if(billingResult.getResponseCode()== BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
            Toast.makeText(this, "Already Purchased", Toast.LENGTH_SHORT).show();
        }
    }
}

private void acknowledgePurchase(String purchaseToken) {
    AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchaseToken).build();
    billingClient.acknowledgePurchase(params, billingResult -> {
        if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
            Toast.makeText(this, "Purchase Acknowledged", Toast.LENGTH_SHORT).show();
        }
    });
}

When I click any of my [Buy Subscription] buttons the billingClient.launchBillingFlow(this, billingFlowParams); is correctly triggered and the SKU is correct.

As far as I am aware, all of the Dev Console is set up correctly. I want to demo IAP to my manager, however, "The item you were attempting to purchase could not be found" is cramping my style ! What am I doing wrong ?

like image 384
Someone Somewhere Avatar asked Oct 18 '25 12:10

Someone Somewhere


1 Answers

The code I posted in my question is fully functional. The problem was 100% due to the developer console configuration :

To fix it:

1) promote the app to alpha as a minimum (Internal test track doesn't work!)

2) click [Manage] on the Alpha track and email the Opt-in link to your testers

3) Your testers will say "when I click this link, the Play Store says the app isn't found". Tell them to relax for 30 minutes - Google Console needs 30 minutes to process this complexity.

Have them download the early release version and suddenly IAP works!

like image 50
Someone Somewhere Avatar answered Oct 20 '25 01:10

Someone Somewhere



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!