I am implementing subscription using flutter_inapp_purchase plugin.
All is working fine. I am facing case here that when App is not opened and i tried to resubscribe an expire subscription from play store it's showing confirm plan and giving option to open app.
How to manage this listener from app and complete purchase ?
Please refer below image that showing the option when i tried to resubscribe an expired subscription(https://i.stack.imgur.com/ORgR6.png)]
Any suggestion would be appreciated.
Thanks
I' not sure about the how that library provide methods or function for flutter but In android native we have library listener like
public interface PurchasesUpdatedListener {
void onPurchasesUpdated(#NonNull BillingResult var1, #Nullable List<Purchase> var2);
}
Can you check similar to available in flutter library in that you can verify your response and manage your navigation accordingly.
Related
On trying to migrate Google billing integration from version 4 to 5, I'm getting an error 'Client does not support ProductDetails' on calling queryProductDetailsAsync.
List<QueryProductDetailsParams.Product> productList = List.of(QueryProductDetailsParams.Product.newBuilder()
.setProductId("ppgapp1")
.setProductType(BillingClient.ProductType.SUBS)
.build());
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build();
billingClient.queryProductDetailsAsync(params, listener);
Are there any changes needed to be made on the console on migration?
And how long it'll take to complete review on submitting to closed or internal test track for Google billing integration?
I face same issue when my emulator PlayStore application version is too old (in my case it is 23.0.21...)
Update PlayStore application to newer version will solve the problem (30.9.0...)
Here is how to update the Play Store app
If you want to guide user to update the PlayStore app, you can do like
billingClient.queryProductDetailsAsync(productParams) { billingResult, productDetails ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED) {
Log.e("TAG", "Feature not supported ")
runOnUiThread {
Toast.makeText(this#MainActivity, "Please update PlayStore app", Toast.LENGTH_LONG).show()
// or AlertDialog or any error message
}
return#queryProductDetailsAsync
}
...
}
I had experienced the same problem. I couldn't find any information on why the problem is occurring. You can use it after checking whether the ProductDetail feature is supported.
BillingResult billingResult = billingClient.isFeatureSupported( BillingClient.FeatureType.PRODUCT_DETAILS );
if ( billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK ) {
// use billingClient.queryProductDetailsAsync()
}
Getting this error when I use google play version 21.2.12-21. When I use the 31.2.29-21 version of google play on another device, the item can be successfully queried. I can't use the 5.0 version of the billing library because it affects the user's payment
Issue resolved
Issue was occuring on one device and not occuring on another device with latest library.
Upon debuging I used old library on device where issue was occuring. And it was working fine on old library.
So after alot of trouble shoot I do following to resolve issue on that device with latest library code.
On device where issue is occurred
Do following
Update device OS update if there is any. In my case there was security patch update. Notification was there to update.
update play store if already not updated
Then clear data storage of play store app under setting of play store app. Then force stop play store app and then open play store app
uninstall and reinstall the android app again
After that my app shows product details as well as purchase pop up with latest billing library.
Thank you
I had to reinstall the app for the Billing client to recognize the supported product ID. I imagine that simply purged some stale cache behind the scenes.
I have an app with a subscription in Google Play.
When the user starts the app, I need to know if the user has an active subscription. This would seem an obvious thing to do, but from searching and trying to implement it, it seems impossible?
I am using Google's newer billing 2/3, following Google's tutorials,
class BillingManager implements PurchasesUpdatedListener
...
public void checkAsync() {
Log.e(TAG, "checkAsync");
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.SUBS, new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(BillingResult billingResult, List<PurchaseHistoryRecord> list) {
Log.e(TAG, "checkCached result: " + list);
if (list == null) {
return;
}
for (PurchaseHistoryRecord ps : list) {
//System.out.println("PAYMENT: " + ps.getSku() + " : " + ps.getPurchaseTime());
}
}
});
}
public void checkCached() {
Log.e(TAG, "checkCached");
List<Purchase> result = billingClient.queryPurchases(BillingClient.SkuType.SUBS).getPurchasesList();
Log.e(TAG, "checkCached result: " + result);
if (result == null) {
return;
}
for (Purchase purchase : result) {
handlePurchase(purchase);
}
}
This is how I think you're supposed to get a user's purchases. But it does not work at all, both calls return null always. It only returns the correct purchases when you reinstall the app or clear the data.
So how exactly is an app supposed to do this?
Purchasing works for the app once I enter internal testing, and download it through the Google Play link. (before that subscriptions do not work at all).
*** updated
So to further clarify:
I am using a valid test user, and subscriptions are working correctly. My question is on the what the API queryPurchases() or queryPurchaseHistoryAsync() are suppose to do.
What I am seeing, is that these only return purchases that have not be processed by the app. They seem to store that the purchase was processed in the apps data.
After the purchase these return null, after the app restarts these return null.
If I clear the app datam or reinstall the app then they return the purchase (once), then again null after restart.
From what I see, these are only useful to detect when a user reinstalls your app, or installs on a different phone. They cannot be used to determine the status of a subscription.
So my question is,
1 - is this something that just does not work in internal testing and will magically work differently when the app is release?
2 - is there a different API that your suppose to use to check the status of a subscription?
3 - are you suppose to manage subscriptions yourself in your app by storing a user preference/cookie when you acknowledge the subscription the first time so you know when the subscription expires?
You need "licenced testers". They would allow you to "sideload" your app on devices, even for debug builds. My interpretation of sideload in this case would cover installing from Android Studio build tools as well as adb install .... and other methods that don't involve the play store.
https://developer.android.com/google/play/billing/test
Ordinarily, the Google Play Billing API is blocked for apps that aren't signed and uploaded to Google Play. License testers can bypass this check, meaning you can sideload apps for testing, even for apps using debug builds with debug signatures without the need to upload to the new version of your app. Note that the package name must match that of the app that is configured for Google Play, and the Google account must be a license tester for the Google Play Console account.
I also don't see how you're using startConnection. Until that's completed successfully I wouldn't be sure you have the latest data. I wouldn't be surprised if that makes you get stale values. I would check that carefully to make sure there's no silent errors happening, by both looking at onBillingSetupFinished and onBillingServiceDisconnected. And for the time being avoid trusting queryPurchases():
https://medium.com/#NandagopalR/integrating-google-play-billing-into-an-android-application-b6eb6af176a7
The queryPurchases() method uses a cache of the Google Play Store app without initiating a network request. If you need to check the most recent purchase made by the user for each product ID, you can use queryPurchaseHistoryAsync(), passing the purchase type and a PurchaseHistoryResponseListener to handle the query result.
By the way what's the value of isReady() right before queryPurchaseHistoryAsync, and what's the value of BillingResult::getDebugMessage and BillingResult::getResponseCode?
Also, use isFeatureSupported, though it seems it's not like your problem is coming from here. But I'd advise not testing with subscriptions until you get all the moving parts working: https://developer.android.com/reference/com/android/billingclient/api/BillingClient#isFeatureSupported(java.lang.String)
Okay, figured it out, was my mistake.
I was calling queryPurchases() in my main activity onCreate(), but the BillingClient was not ready yet.
I moved it to onBillingSetupFinished() and it now returns the correct purchases.
Everything is now working as expected. You get the active subscriptions when you call queryPurchases() after an app restart.
I'm integrating AppsFlyer with Android Native Application. And I want to use Deferred Deep Linking, when user click landing page ads and download the app and upon first app open the user lands directly on the activity I want.
Link docs: https://support.appsflyer.com/hc/en-us/articles/207032096-Deferred-Deep-Linking-Getting-the-Conversion-Data
But I have not found a way to check that my code is running correctly.
Please help me with this problem
What was working for me is:
Add physical device as a test device in AppsFlyer (here's how to do it)
Enable Debug Mode in AppDelegate.swift in didFinishLaunchingWithOptions
AppsFlyerTracker.shared().isDebug = true
Add AppsFlyer methods in your AppDelegate.swift (as per article)
Remove app (or test build) from physical device
Open Deep Link from physical device, you will be redirected to App Store. Don't install app from the App Store!!! (just close it)
Install app via XCode
After it, on a first install it will call onConversionDataReceived method and the rest staff.
You're going to have to implement the onInstallConversionDataLoaded listener:
public interface AppsFlyerConversionListener {
void onInstallConversionDataLoaded(Map<String,String> conversionData);
void onInstallConversionFailure(String errorMessage);
}
This will return a map of all the parameters on the link that you clicked.
The parameter you need to pay attention to is the af_dp parameter.
This parameter should contain the URI scheme of the activity you want to route your users to. Make sure that you have set up this URI scheme properly in the manifest.
To create a tracking link you can use Link Management. It doesn't matter if it's a single platform link or a OneLink, as long as you have the af_dp parameter on the link, that parameter (along with all other parameters on the link) will be part of the response.
If you're still facing issues, feel free to reach out to support#appsflyer.com.
Until june 20th 2016 i was able to cancel test purchases done in my app.
Doing multiple in-app purchases (not consumable) from the same test account made it easy to develop and test the code without too much hazzle.
After 20th june 2016, the purchases did not show in my merchant account and
i was unable to do more than 1 purchase from my test account. All i got was the: "you already own this item" message.
I logged a request to the google developer support group and the answer was:
Beginning June 20, 2016, we changed test purchases for one-time in-app purchases (IAPs).
Previously, test purchases for one-time IAPs generated order IDs. Starting June 20, 2016, one-time IAPs do not generate official order IDs (if at all) and will not appear in the Merchant Center. This behavior already applies to subscription IAPs.
You can learn more about testing in-app billing in the Android Developers Help Center: https://developer.android.com/google/play/billing/billing_testing.html#testing-purchases
allright.. so i go to the mentioned link and theres a section there:
Canceling completed test purchases
which states:
Google Play accumulates completed test purchases for each user but does not pass them on to financial processing.
In some cases, you might want to manually cancel a test purchase to continue testing. To do so, open the app page in the Play Store. If the test purchase that you want to cancel is a subscription, you can also use the cancel() method of the Purchases.subscriptions API.
Important: The refund() and revoke() methods of the Purchases.subscriptions API don't support test purchases.
So I go to the app page in play store...and do what exactly? the webpage does not state what i am supposed to do there. anyone know?
it does say: you can also use the cancel() method of the Purchases.subscriptions API.
which indicates that using the cancel() method is not the only method.
How to solve this without adding additional code in my app?
I went into the main Google Play Console page and clicked on Order Management. Under that I was able to select all test purchases and Refund them. I'm the primary developer of the app so I have access. If you are a tester you'd probably have to contact the support team and request that they refund your order.
All managed in-app products are consumable.
as stated in the docs.
That means that you can consume an owned item instead of cancelling the purchase and buy it all over again.
I suggest querying the inventory at the app launch time:
mIabHelper.queryInventoryAsync(this);
You can then consume the owned item in the callback:
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Purchase purchase = inventory.getPurchase(MY_SKU);
boolean isBought = (purchase != null && verifyDeveloperPayload(purchase));
if (isBought) {
mIabHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
//Clear the purchase info from persistent storage
}
});
}
}
This is OK for testing the IAB flow but make sure to remove this code from the release version.
On Play Console, go to Developer Account -> Account Details, set the license testers (you are one by default)
Purchase items
Go to Order Management, choose those test orders, select: refund, but DON'T FORGET to check REVOKE when you are refunding. (I forgot to revoke, and now can't find a way to take them back)
Anyway, another test account will do.
I found a solution which isn't very convenient, but works. It seems like you can consume uncomsumable products and that way you can buy them again. I'm working with phonegap so I only have example code for the cordova-plugin-purchase plugin:
store.when("your.product.id").updated(product => {
if(product.owned) {
var transaction = product.transaction;
product.transaction = null;
store.inappbilling.consumePurchase(
function() { // success
alert("consume success");
},
function(err, code) { // error
alert("consume error " + err)
},
product.id,
transaction.id
);
}
});
The updated callback gets called when you call store.refresh() or buy the product. So depending on your use case you'd want to implement an additional method of checking when to consume the product.
I have no experience with the native Android in-app payments, but obviously you will be able to consume the products there as well.
Edit: Sorry, I just read that you didn't want to include additional code in your project. I don't think that's possible at the moment, but would like to keep my answer here because it might help other people trying to test in-app payments.
What worked for me was a combination of both:
Go to order management and refund
clear cache/data in Play Store app (as well as your app in you placed some shared prefs).
Also, in case you get an item already owned status, you can consume the purchase using the purchase token and calling billingClient.consumeAsync().
Didn't find a solution for this.
My workaround is simply remove the current test user from the test users list, make a real purchase, then cancel it using the merchant console.
The queryPurchaseHistoryAsync method still finds test orders I've made over the last year, despite having long ago consumed, refunded, and revoked them. One precaution I've found helpful is clearing the data in the Google Play Store app (settings/apps/google play store/storage/clear data). queryPurchaseHistoryAsync pulls obsolete purchase data from here, although only the non-networking (and completely unreliable I've found) queryPurchases is supposed to do this. You may have to add additional code to your app after all, but it doesn't have to be much.
With the dropping of support for Trivial Drive 2 (the link in the docs takes you to a '404 page does not exist' error, the github files are archived, and updating to billing:2.1.0 will give you a vending import compile error in the IabHelper class), answers to this popular question involving IabHelper might be considered obsolete. Billing is a lot simpler now if you follow the basic code pieces in the docs starting here https://developer.android.com/google/play/billing/billing_overview with no messy helper classes. One persistent issue is a 'Both methods have same erasure, yet neither overides the other' method clash error you may run into with this implementation, see my solution here 'Both methods have same erasure, yet neither overides the other' method clash error in SkuDetailsResponseListener().
Once you have the newest billing code implemented, you can create a hidden trigger in your production app to call queryPurchaseHistoryAsync to get a purchaseHistoryRecordList. Then call consumeAsync for each item in this list. Here is the barebones code to consume all test orders, allowing multiple tests of your nonconsumables:
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP,
new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(BillingResult billingResult,
List<PurchaseHistoryRecord> purchaseHistoryRecordList){
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchaseHistoryRecordList != null) {
for (PurchaseHistoryRecord purchaserecord : purchaseHistoryRecordList) {
if(purchaserecord!=null){
ConsumeParams consumeParams =
ConsumeParams.newBuilder()
.setPurchaseToken(purchaserecord.getPurchaseToken())
.setDeveloperPayload(purchaserecord.getDeveloperPayload())
.build();
billingClient.consumeAsync(consumeParams, consumelistener);
}}}}});
For people using a way based on the new TrivialDriveKotlin, consumable products are consumed during the installation of the app in the method
handleConsumablePurchasesAsync
If your purchase is not consumable, you can make it consumable by adding the corresponding sku into CONSUMABLE_SKUS in the GameSku object. Exemple:
val CONSUMABLE_SKUS = listOf(GAS, PREMIUM_CAR)
Then uninstall your app from your device and install it again, and your non consumable purchase is available again. Fast and simply.
Of course, don't forget to remove your
How do i check if a in app purchase has been done before?
So that my user doesnt need to repurchase the in app purchase upon uninstalling and reinstalling the app?
I have set my in app item to managable in the android market publish page.
i have read about RESTORE_TRANSACTION but I do not know what i need to look for inside this response and also how to test this.
Any help would be greatly appreaciated.
You need to restore the transactions, using the RESTORE_TRANSACTION flag you specified above. You should only do this once, when the application starts for the first time or if the user clears the data.
I would advice to make this process simpler for yourself, you looking into the AndroidBillingLibrary, which allows you to interface with the Android In App Billing in a much simpler manner.
Here is a snippet of how the transactions are restored:
private void restoreTransactions() {
if (!mBillingObserver.isTransactionsRestored()) {
BillingController.restoreTransactions(this);
Toast.makeText(this, R.string.restoring_transactions, Toast.LENGTH_LONG).show();
}
}