I knew i have added similar question before regrding the issue for not able to launch the paymentflow dialog , but here i have changed the code accrding to the IAB/IAP v6.01 , previous one was v4.0.0
so when im clicking the btn to show the paymentflow dialog , the toast regading the billin client is not ready is being aprerd accrding to this
if(billingclient.isReady())
means its going to else
idk what is wrong here, i have added the product details properly the product id and the licece base64 key but dont know what is cuasing this
private final String premiumProductId = <Product ID>;
private final String baseKey64 = <Licence Base 64>;
OnCreateView {
// Initialize BillingClient
billingClient = BillingClient.newBuilder(requireContext()).setListener(purchasesUpdatedListener).enablePendingPurchases().build();
// Buy Premium Btn Click
Premium_ImgPostTab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (billingClient.isReady()){
Toast.makeText(requireContext(),"Billing Client Ready",Toast.LENGTH_SHORT).show();
PurchaseDialog();
}else {
Toast.makeText(reqverifyuireContext(),"Billing Client Not Ready",Toast.LENGTH_SHORT).show();
}
}
});
return view;
}
private void PurchaseDialog() {
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(ImmutableList.of(QueryProductDetailsParams.Product.newBuilder().setProductId(premiumProductId).setProductType(BillingClient.ProductType.INAPP).build())).build();
billingClient.queryProductDetailsAsync(queryProductDetailsParams, new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
for (ProductDetails productDetails : productDetailsList) {
String offerToken = productDetails.getSubscriptionOfferDetails().get(0).getOfferToken();
ImmutableList productDetailsParamsList = ImmutableList.of(BillingFlowParams.ProductDetailsParams.newBuilder()
// retrieve a value for "productDetails" by calling queryProductDetailsAsync()
.setProductDetails(productDetails)
// to get an offer token, call ProductDetails.getSubscriptionOfferDetails()
// for a list of offers that are available to the user
.setOfferToken(offerToken).build());
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(productDetailsParamsList).build();
// Launch the billing flow
billingResult = billingClient.launchBillingFlow(requireActivity(), billingFlowParams);
}
}
});
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
void handlePurchase(Purchase purchase) {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
Toast.makeText(requireContext(), "Error: Invaild Purchase", Toast.LENGTH_SHORT).show();
return;
}
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase);
}
} else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) {
Toast.makeText(requireContext(), "Purchase is Pending", Toast.LENGTH_SHORT).show();
} else if (purchase.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE) {
Toast.makeText(requireContext(), "Purchase Handing: Unexpected Error", Toast.LENGTH_SHORT).show();
}
}
private final PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
// To be implemented in a later section.
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) {
handlePurchase(purchase);
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
// Handle an error caused by a user cancelling the purchase flow.
} else {
// Handle any other error codes.
}
}
};
private boolean verifyValidSignature(String signedData, String signature) {
try {
// To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
String base64Key = baseKey64;
return Security.verifyPurchase(base64Key, signedData, signature);
} catch (IOException e) {
return false;
}
}
AcknowledgePurchaseResponseListener ackPurchase = new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// Acknowledgment successful
Toast.makeText(requireContext(), "Acknowledgment successful", Toast.LENGTH_SHORT).show();
} else {
// Handle acknowledgment error
Toast.makeText(requireContext(), "Acknowledgment unsuccessful", Toast.LENGTH_SHORT).show();
}
}
};
public void onDestroy() {
super.onDestroy();
if (billingClient != null) {
billingClient.endConnection();
}
}
after sometime i thought maybe loading the connection before the onclick can be helpfull , like this
// Buy Premium Btn Click
getConnection();
Premium_ImgPostTab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (billingClient.isReady()){
Toast.makeText(requireContext(),"Billing Client Ready",Toast.LENGTH_SHORT).show();
PurchaseDialog();
}else {
Toast.makeText(requireContext(),"Billing Client Not Ready",Toast.LENGTH_SHORT).show();
}
}
});
private void getConnection() {
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
QueryProductDetailsParams queryProductDetailsParams = QueryProductDetailsParams.newBuilder().setProductList(ImmutableList.of(QueryProductDetailsParams.Product.newBuilder().setProductId(premiumProductId).setProductType(BillingClient.ProductType.INAPP).build())).build();
billingClient.queryProductDetailsAsync(queryProductDetailsParams, new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
for (ProductDetails productDetails : productDetailsList) {
String offerToken = productDetails.getSubscriptionOfferDetails().get(0).getOfferToken();
ImmutableList productDetailsParamsList = ImmutableList.of(BillingFlowParams.ProductDetailsParams.newBuilder()
// retrieve a value for "productDetails" by calling queryProductDetailsAsync()
.setProductDetails(productDetails)
// to get an offer token, call ProductDetails.getSubscriptionOfferDetails()
// for a list of offers that are available to the user
.setOfferToken(offerToken).build());
}
}
});
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
}
here i didnt add the launchBillingFlow , in this case the codintion billingclient.isReady()
gets true and show the toast inside it , but didnt able to luanch the billingflow in the end
Edit:- I thought maybe adding the .csv export from play console product details here is the file
the only concern here is the price section its shows 400000000 value , which is no corrrect its INR 400 not 400000000 , but its showing correct in the console