I am executing the code described by the google billing library, but my device always connects to the billing client, also if I m in flight mode.
//Initiate billing client
bc = BillingClient.newBuilder(getApplicationContext()).setListener(this).
enablePendingPurchases().build();
bc.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
Toast.makeText(getApplicationContext(), "Connected", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
Toast.makeText(getApplicationContext(), "Failure", Toast.LENGTH_SHORT).show();
}
});
Google Play services cache purchases, so it is available offline
Related
I have kept a donate tab and want to let the users buy the items over and over again. I have implemented a code but it lets the user buy the specific item only once. I have used managed products in play console for products.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(billingClient.isReady()){
SkuDetailsParams params=SkuDetailsParams.newBuilder()
.setSkusList(Arrays.asList("purchase_aaa","purchase_bbb","purchase_ccc","purchase_ddd"))
.setType(BillingClient.SkuType.INAPP).build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
if(responseCode==BillingClient.BillingResponse.OK)
{
loadProductToRecyclerView(skuDetailsList);
}
else{
Toast.makeText(Donate.this, "Cannot query product", Toast.LENGTH_SHORT).show();
}
}
});
}
else
{
Toast.makeText(Donate.this, "Not ready", Toast.LENGTH_SHORT).show();
}
}
});
#Override
public void onPurchasesUpdated(int responseCode, #Nullable List<Purchase> purchases) {
if(purchases!=null){
Toast.makeText(this, "Purchased"+purchases.size(), Toast.LENGTH_SHORT).show();
}
}
That's by design and cannot be changed, in-app managed products can only be purchased once.
If you want the user who has paid more to have more features enabled, you will have to create as many in-app managed products as levels exist.
If it is a game in which, for example, the user is consuming items then when he no longer has any, you consume the in-app product so he can buy it again.
Or you can also consume the product immediately after the purchase and keep track of how many he has purchased through your own means, an own server or perhaps through firebase, but this already means that you will have to implement a user authentication system for your app.
Consume a purchase:
ConsumeResponseListener consumeListener = new ConsumeResponseListener() {
#Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
}
};
String token = purchase.getPurchaseToken();
ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(token).build();
billingClient.consumeAsync(consumeParams, consumeListener);
I am using google play subscription services for my app.But when any use or customer tries to pay for my subscription with any given payment method.It shows -
Unavailable for this purchase.
Only I can pay with Google play account money.
I tried all method on different mobiles. But problem is same.
I am using standard code given on google developers site.
private BillingClient billingClient=BillingClient.newBuilder(activity)
.setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingResponse.OK) {
}
}
#Override
public void onBillingServiceDisconnected() {
}
});
I want to enable all payment methods for my app.
Try to add enablePendingPurchases(), so
private BillingClient billing_client = BillingClient.newBuilder( this ).enablePendingPurchases().setListener(...
I have gone through the Play Billing Library
https://developer.android.com/google/play/billing/billing_library_overview
You must acknowledge all purchases within three days. Failure to properly acknowledge purchases results in those purchases being refunded.
The process is doesn't provide any clarity how to acknowledge purchases.
This is what i tried
Is this the correct way to do it.
Thanks in Advance
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
if(billingResult.getResponseCode()== BillingClient.BillingResponseCode.OK&&purchases!=null){
Toast.makeText(this, "Purchase Successful", Toast.LENGTH_SHORT).show();
for(Purchase purchase:purchases){
handlePurchase(purchase);
}
}else if(billingResult.getResponseCode()== BillingClient.BillingResponseCode.USER_CANCELED){
Toast.makeText(this, "Purchase Cancelled", Toast.LENGTH_SHORT).show();
}else if(billingResult.getResponseCode()== BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED){
Toast.makeText(this, "Already Purchased", Toast.LENGTH_SHORT).show();
} else{
Toast.makeText(this, billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
}
//in handlePurchase()
if(!purchase.isAcknowledged())
{
AcknowledgePurchaseParams acknowledgePurchaseParams
= AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.setDeveloperPayload(purchase.getDeveloperPayload())
.build();
client.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener() {
#Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
if(billingResult.getResponseCode()== BillingClient.BillingResponseCode.OK){
Toast.makeText(RemoveAdsActivity.this, "Purchase Acknowledged", Toast.LENGTH_SHORT).show();
}
}
});
}
It mentions acknowledging purchases near half way through that link. There are different ways to acknowledge the purchase depending on the type.
private BillingClient mBillingClient = BillingClient.newBuilder(mActivity).setListener(this).build();
//For non-consumables:
mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, new AcknowledgePurchaseResponseListener());
//For Consumables:
client.consumeAsync(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
The link I posted includes a sample on how to handle subscriptions.
UPDATE
Here's how to acknowledge both non-consumable and consumable purchases, staring with non-consumable:
First, create the AcknowledgePurchaseParams Class object. For this you need the purchase token which you should be able to get easily as you should be calling this in your onPurchasesUpdated method or another method that you passed purchase to after onPurchasesUpdated:
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
Next create your listener that will be used as the second parameter. This will allow you to do something after the purchase is acknowledged. I am displaying a snackbar message in this example (As per worbel's comment you can, and probably should, check the result of this billingResult):
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() {
#Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
getMessage("Purchase acknowledged");
}
};
With these created, use your BillingClient to call the acknowledgePurchase method:
mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
The purchase should be successfully acknowledged.
This uses acknowledgePurchase for non-consumable items.
Consumable purchases
This is similar only what they are called is changed - See the explanation for what they are in the above example:
First parameter - Params - set-up:
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
Second parameter - Listener - set-up:
ConsumeResponseListener consumeResponseListener = new ConsumeResponseListener() {
#Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
getMessage("Purchase acknowledged");
}
}
Now use your BillingClint and consumeAsync:
mBillingClient.consumeAsync(consumeParams, consumeResponseListener);
If you are new and using billing library 4.0.0 then above codes will not work since now all billing process has been put in background thread so make sure you do not call any ui updating code during billing process.
Use:
purchase.getSkus.contains("sku here");
instead of
purchase.getSku.equals("sku here");
I am upgrading the in-app billing in my app from Version 3 to newer code. Version 3 is working fine and it production in my app right now but I've read it will be deprecated eventually.
When I try to test the purchase flow using a static product id (android.test.purchased), the BillingResult result code only returns -1 with a debug message of "Service connection is disconnected". AFAIK, there is no service connection in the newer library but there was in Version 3.
If I use a real in-app product code it tells me I've already purchased it, which is correct but I need to test the actual purchase flow.
I am testing this on an actual device (Pixel 3 XL), not the emulator. I've tried testing it on a separate device that is logged in with a test account (not developer) but I get the same results.
UPDATE: I setup a real (test) in-app managed product in the Developer Console, then installed my app on a device that is logged in with a test (non-developer) account and I'm still getting the "Service connection is disconnected" error. I feel it's something outside of the code but not sure what.
UPDATE 2: I created a new project with nothing in it except the billing code and it worked so there's something in my app that is causing it to break.
UPDATE 3: I created a new project and imported the code from the broken app into it and still getting the same error message. I feel, now, there's something with my package name and Google's servers that's returning the error message.
UPDATE 4: I created a blank project but gave it the same package name as my broken app and the billing worked, so it's not the package name. Now my guess is there's something from the old AIDL billing code (Version 3) that is interfering.
SOLUTION!!! in the application node in the AndroidManifest.xml I had this: tools:node="replace". Removed that attribute and billing now works.
mBillingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == OK) {
final Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
if (purchasesResult.getResponseCode() == OK) {
final List<Purchase> purchases = purchasesResult.getPurchasesList();
for (final Purchase purchase : purchases) {
}
}
}
}
#Override
public void onBillingServiceDisconnected() {
CommonUtils.showToast(mActivity, "disconnected");
}
});
mUnlockPremiumButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final List<String> skuList = new ArrayList<> ();
skuList.add(getString(R.string.inapp_premium_product_id));
final SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.INAPP);
mBillingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
for (SkuDetails skuDetails : skuDetailsList) {
if (getString(R.string.inapp_premium_product_id).equals(skuDetails.getSku())) {
final BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build();
final BillingResult result = mBillingClient.launchBillingFlow(mActivity, flowParams);
if (result.getResponseCode() == ITEM_ALREADY_OWNED)
{
CommonUtils.showToast(mActivity, getString(R.string.alert_purchased));
}
else if (result.getResponseCode() != OK)
{
//always returns a getResponseCode of -1 (service disconnected)
}
}
}
}
});
}
});
As the error clearly indicates, the Billing Client is disconnected due to the following possible reasons.
You started the connection but the connection has not finished the setup yet.
Possible race condition since startConnection is an asynchronous process. You can use
billingClient.isReady() to check if it is available.
Your app may have lost the connection/internet after starting it.
Your device/emulator does not support Google Play Services.
The following is a working code in Billing API 3.0 with Android Version Targeted SDK 28.
As documented in Billing API Error Handling section (https://developer.android.com/google/play/billing/integrate) if the connection is lost, app must be responsible to reestablish the connection. I would recommend to use a Singleton class that holds the Billing API and use billingClient.isReady() method to check if the connection is successful, if not attempt to reestablish it. Provided you add logging in the BillingClientStateListener class override methods. I am skipping that code because it is straight forward and well documented in the link I provided above.
public class ApplicationBillingClient
{
static ApplicationBillingClient applicationBillingClient= null;
private static BillingClient billingClient;
private ApplicationBillingClient() {}
private static boolean isInitialized()
{
return applicationBillingClient != null && billingClient != null;
}
private static void initialize(Context applicationContext)
{
try
{
if(applicationContext != null)
{
applicationBillingClient = new ApplicationBillingClient();
BillingClient.Builder builder= BillingClient.newBuilder(applicationContext);
builder.setListener(new PurchaseActivityListener());
builder.enablePendingPurchases();
billingClient = builder.build();
}
LogUtil.info("Initializing the Billing Client");
}
catch (Exception ex)
{
LogUtil.error("Error while initializing billing client", ex);
}
}
public static ApplicationBillingClient getInstance(Context applicationContext)
{
if(isInitialized() == false)
{
initialize(applicationContext);
}
return applicationBillingClient;
}
public void startConnection()
{
billingClient.startConnection(new StateListener());
}
public boolean isReady()
{
return billingClient.isReady();
}
public void getMonthlySubscription()
{
try
{
if(billingClient.isReady())
{
SkuDetailsParams.Builder skuBuilder = SkuDetailsParams.newBuilder();
skuBuilder.setType(BillingClient.SkuType.SUBS);
skuBuilder.setSkusList(Arrays.asList(new String[]{MONTHLY_BILLING_SUBSCRIPTION_SKU}));
SkuDetailsParams params = skuBuilder.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsListener());
}
}
catch (Exception ex)
{
LogUtil.error("Error while querying async SKU for Monthly Subscription", ex);
}
}
}
//In your activity
ApplicationBillingClient appBillingClient =
ApplicationBillingClient.getInstance(applicationContext);
if (appBillingClient.isReady() == false)
{
appBillingClient.startConnection();
}
else
{
appBillingClient.getMonthlySubscription();
}
This was the application node, in the AndroidManifest, when the billing was breaking:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme"
tools:node="replace">
...
</application>
removing tools:node="replace" fixed the billing. Hopefully this will save someone the days I wasted.
I need to add a subscription support to my application, so that the user can buy a subscription for a year of service.
I just have created the subscription on Google Developer Console.
My problem is: I don't have any idea how I can check the user subscription.
I'm working to do this:
When my app is started by the user, if there is network, the app contacts the Play Store and checks if the user has bought the subscription and the payment date. These data are always saved on locale file, so if there is no network the app will use the local data for checking;
If the user has bought the subscription I check if it's been over a year. In fact I have read on internet that Play Store provides only the payment date and not the finish subscription date;
If the checking is true the app will work in Premium mode and not in Standard mode;
Now the problems:
How do I check the purchase? Can I use hasPurchase() method like in normal in-app?
If I need to use hasPurchase() on point 1) does this method return False if the user don't renew the subscription after a year?
How can I know the purchase date?
I copy a piece of code, this is a valid code to check normal in-app and I'd like to edit it to use it in subscription checking:
private void checkForPremium() {
final IabHelper buyHelper = new IabHelper(this, "MYKEY");
// initialize the InApp Billing system
buyHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
buyHelper.dispose();
Log.e("MYAPP", "Error: " + result);
return;
}
// Get a list of all products the user owns
buyHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (result.isFailure()) {
buyHelper.dispose();
Log.e("MYAPP", "Error: " + result);
} else {
boolean isPremium = inv.hasPurchase("MYSKU");
inv.getSkuDetails().
buyHelper.dispose();
// Forward to the currect activity depending on premium / demo mode
if (isPremium) {
if(menu != null){
MenuItem item = menu.findItem(R.id.action_premium);
item.setVisible(false);
}
Log.w("MYAPP", "PREMIUM");
} else {
if(menu != null){
MenuItem item = menu.findItem(R.id.action_premium);
item.setVisible(true);
}
Log.w("MYAPP", "NO PREMIUM");
}
}
}
});
}
});
}