I am trying to implement in-app purchases in my Android app.
So basically, the user is supposed to click the 'Remove Ads' menu button which sets up the billingClient and eventually processes the in-app purchase.
Here's my code:
public class PlayersActivity extends AppCompatActivity {
SharedPreferences prefs;
boolean adFree;
BillingClient billingClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
prefs = this.getSharedPreferences("package_name", Context.MODE_PRIVATE);
adFree = prefs.getBoolean("adFree", false);
if (!adFree) {
MobileAds.initialize(this, new OnInitializationCompleteListener() {
@Override
public void onInitializationComplete(InitializationStatus initializationStatus) {
}
});
AdView mAdView = findViewById(R.id.listBannerAd);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_history, menu);
MenuItem item6 = menu.findItem(R.id.action_remove_ads);
if (!adFree) {
item6.setVisible(true);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_remove_ads:
setUpBillingClient();
return true;
}
return super.onOptionsItemSelected(item);
}
private void handlePurchase(Purchase purchase) {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
ConsumeResponseListener listener = (billingResult, purchaseToken) -> {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
prefs.edit().putBoolean("adFree", true).apply();
}
};
billingClient.consumeAsync(consumeParams, listener);
}
}
private final PurchasesUpdatedListener purchasesUpdatedListener = (billingResult, purchases) -> {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
}
};
private void setUpBillingClient() {
Log.v("InAppPurchase", "billingclientsetup");
billingClient = BillingClient.newBuilder(this)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
startConnection();
}
private void startConnection() {
Log.v("InAppPurchase", "startconnection");
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
Log.v("InAppPurchase", "billing setup finished");
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
queryAvailableProducts();
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
Log.v("InAppPurchase", "billing disconnected");
}
});
}
private void queryAvailableProducts() {
Log.v("InAppPurchase", "queryavailableproducts");
List<String> skuList = new ArrayList<>();
skuList.add("remove_ads");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(), (billingResult, list) -> {
Log.v("InAppPurchases", billingResult.getResponseCode() + "response");
Log.v("InAppPurchases", list + "list");
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& list != null && !list.isEmpty()) {
Log.v("InAppPurchase", list.get(0).getDescription().toString());
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(list.get(0))
.build();
billingClient.launchBillingFlow(PlayersActivity.this, billingFlowParams);
}
});
}
}
However, in the logs, I can see that an empty list is printed from the function in querySkuDetailsAsync
. The response code is 0.
I have tried the solutions in other StackOverflow questions, like ensuring that the app is published to the Google Play Store and making sure that the in-app purchase is set up properly in the Play Console.
Is there anything wrong in the way I've implemented it?