I am trying to implement Subscriptions for the first time. I am using BillingClient v3.0.1. It is contract so I am not the owner of the Google Play account. The account owner has set up financials for the account and has configured the API. I have created the subscription products in the Google Play console, added the billing library through Gradle and com.android.vending.BILLING in the manifest, and published a build on the Closed Alpha track for internal testing.
I initialize the billing client successfully
billingClient = BillingClient.newBuilder(mContext)
.enablePendingPurchases()
.setListener(this)
.build();
billingClient.startConnection(new BillingClientStateListener() {
#Override public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
...
}
#Override public void onBillingServiceDisconnected() {
...
}
});
and everything works, I retrieve the list of products
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(skuList)
.setType(BillingClient.SkuType.SUBS)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override public void onSkuDetailsResponse(#NonNull BillingResult billingResult, #Nullable List<SkuDetails> list) {
...
});
and that too is successful, but when I try to purchase the product on a real device with a build running the same version code as the Alpha testing track version using:
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(product)
.build();
BillingResult billingResult = billingClient.launchBillingFlow(mContext, billingFlowParams);
The pop-up shows an Error in the pop-up "The item you were attempting to purchase could not be found".
I have been going through everything I can find (short of receipt validation, which will be next up after I can actually complete a purchase), but am stuck.
Any advice / thoughts?
Thanks
Stephen
In 'desperation' -> Well actually because I had nothing better to try, I updated the build on GooglePlay alpha test track and ran the exact same build on my test device and now it is working, no other changes, but progress.
Related
I'm trying to convert android billing version 3 to android billing version 5.
But I have a problem in this function, I have no idea how to change it to make it work:
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
Purchase.PurchasesResult queryPurchase = billingClient.queryPurchases(INAPP);
List<Purchase> queryPurchases = queryPurchase.getPurchasesList();
if(queryPurchases!=null && queryPurchases.size()>0){
handlePurchases(queryPurchases);
}
//if purchase list is empty that means item is not purchased
//Or purchase is refunded or canceled
else{
savePurchaseValueToPref(false);
removepremium();
}
}
}
Cannot resolve: PurchasesResult, queryPurchases, getPurchasesList...
How can I change this to work in billing 5?
I was wondering could you help. I followed the instructions at https://developer.android.com/google/play/billing/integrate, but I cannot seem to get the purchase flow working. The billing seems to setup ok, but when I try to query for my in-app products, the list is always returning empty. Can someone please help?
In my app level build.gradle file, I have included the Google Billing SDK:
implementation 'com.android.billingclient:billing:3.0.0'
Then I have created an activity to test out the code. It first initialises the BillingClient and starts the connection. The connection seems to finish the setup correctly. Once setup correctly, I then try to query the products that I have available in my Google Play Console under 'Store presence' > 'In-app products' > 'Manage products'
The following is then the code in the Activity that should kick off the process and return the SkuDetails list, but unfortunately it is returning back empty.
private BillingClient billingClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_billing);
this.billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases()
.setListener(this.purchaseUpdateListener)
.build();
this.billingClient.startConnection(billingClientStateListener);
}
private PurchasesUpdatedListener purchaseUpdateListener = new PurchasesUpdatedListener() {
#Override
public void onPurchasesUpdated(#NonNull BillingResult billingResult, #Nullable List<Purchase> list) {
Log.d("Billing", "onPurchasesUpdated - List Size: " + list.size());
}
};
private BillingClientStateListener billingClientStateListener = new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.d("Billing", "onBillingSetupFinished - OK");
queryProducts();
} else {
Log.d("Billing", "onBillingSetupFinished - Something wrong response Code: " + billingResult.getResponseCode());
}
}
#Override
public void onBillingServiceDisconnected() {
Log.d("Billing", "Service disconnected");
}
};
private void queryProducts() {
List<String> productIdsList = new ArrayList<>();
productIdsList.add("test.billing.001");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(productIdsList).setType(BillingClient.SkuType.INAPP);
this.billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(#NonNull BillingResult billingResult, #Nullable List< SkuDetails > list) {
Log.d("Billing", "onSkuDetailsResponse - List Size: " + list.size());
}
});
}
So for anyone who is having similar issues, it seems that (well in my case anyways) that my app needed to be successfully published before I could retrieve the in-app products from the app. Once my app was published, I was then able to query and use the in-app products.
According to Maxim Alov comment for Billing 5.0.0, the problem is in invitations for testers.
My steps to fix this problem:
Open tester's invitation link and accept
Open app via link from previous step
After two these steps (in my case second helped) all products started to come
I'm using billing-lib-5.0.0 and also had the same issue -
queryProductDetails() was always empty on my release builds, let alone
debug builds. I'd actually added all my test gmail emails to list of
testers for Closed Testing, and also LICENSED all of them. No effect.
Eventually, I recognized that the link to my test app is not generated
on Play Console Closed Testing track page. I recreated the track, this
time for Internal Testing, and the link has appeared. Then I logged in
to each of my gmails and accepted to be a tester from that link. After
doing that, products started to come, in IDE
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 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 am developing an application which will allow user to purchase using In App Purchase and I want to remove ads after purchase. I can purchase succesfully with code below
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSku("android.test.purchased")
.setType(BillingClient.SkuType.INAPP)
.build();
mBillingClient.launchBillingFlow(getActivity(), flowParams);
But I cannot see the result from queryPurchaseHistoryAsync when I open app again and call this method below.
mBillingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP, new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(int responseCode, List<Purchase> purchasesList) {
purchasesList.size();
}
});
purchasesList.size() == 0
Is "queryPurchaseHistoryAsync" method cannot show test purchase or Am I doing something wrong?
Edit: Is queryPurchaseHistoryAsync method check purchase after delete and install app again.
Yes queryPurchaseHistoryAsync method check purchase after deleting and installing the app again against particular user
mBillingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP, new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(#NonNull BillingResult billingResult, #Nullable List<PurchaseHistoryRecord> list) {
}
});
Try this it will give all purchase items.
mBillingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
listener.onSkuDetailsResponse(responseCode, skuDetailsList);
}
});
mBillingClient.queryPurchases() is all you need. Call it at every app start and, for example, every time your main activity resumes. This way your (reinstalled) app will eventually detect all user's purchases.