26

I am trying to set up paid products in my app.

I have followed all the guides for the Flutter_Inapp_Purchase plugin and they all say:

List<IAPItem> items = await FlutterInappPurchase.getProducts([iapId]);

Where iapId is the "id of my app". All of my other code surrounding this implementation works fine, because when I use 'android.test.purchased' as my iapId string, the test product is found and loaded into the app perfectly. So the issue is the string that I am using maybe, because no other explanation or examples are given about this anywhere.

I have a product in my store called remove_ads.

So am I using the wrong iapId here? I can't imagine what else it could be asking for.

Edit:

Purchasing the items.

I have updated the code below, as it had errors already. It now fails at the lines: _verifyPurchase and _deliverPurchase below, as these are not things. The official documentation for this seems to say "you go ahead and work all this stuff out from here", with no indication how to even begin.

  Future<Null> _queryPastPurchases() async {
    final QueryPurchaseDetailsResponse response = await InAppPurchaseConnection.instance.queryPastPurchases();
    if (response.error != null) {
      // Handle the error.
    }
    for (PurchaseDetails purchase in response.pastPurchases) {
      _verifyPurchase(purchase);  // Verify the purchase following the best practices for each storefront.
      _deliverPurchase(purchase); // Deliver the purchase to the user in your app.
      if (Platform.isIOS) {
        // Mark that you've delivered the purchase. Only the App Store requires
        // this final confirmation.
        InAppPurchaseConnection.instance.completePurchase(purchase);
      }
    }
  }
Bisclavret
  • 1,327
  • 9
  • 37
  • 65

8 Answers8

21

This answer is somewhat of a recommendation, however, it should take you to your goal.

The Flutter team has recently finished an official plugin for in-app purchases. It is the in_app_purchase plugin.

  1. I assume that you have already read through the Android Developers guide for configuring your remove_ads purchase.

  2. You need to add in_app_purchase as a dependency in your pubspec.yaml file:

dependencies:
  in_app_purchase: ^0.3.1 # For newer versions, check the Pub page.
  1. In your Flutter app, you now need to import 'package:in_app_purchase/in_app_purchase.dart':
import 'package:in_app_purchase/in_app_purchase.dart';
  1. To load your product, you can use the following code:
// Set literals require Dart 2.2. Alternatively, remove `const` and use `<String>['remove_ads'].toSet()`.
const Set<String> _kIds = {'remove_ads'};
final ProductDetailsResponse response = await InAppPurchaseConnection.instance.queryProductDetails(_kIds);
if (!response.notFoundIds.isEmpty()) {
    // Handle the error.
} else {
  List<ProductDetails> products = response.productDetails;
  for (ProductDetails product in products) {
    print('${product.title}: ${product.description} (cost is ${product.price})');
  }
  // Example: purchasing the first available item.
  final PurchaseParam purchaseParam = PurchaseParam(productDetails: products[0]);
  InAppPurchaseConnection.instance.buyNonConsumable(purchaseParam: purchaseParam);
}

For more information and instructions, read the plugin's README and checkout the example app.

  1. You need to follow the steps explained in the example's README. You will need to create a remove_ads SKU ID instead of what they mention because their SKU IDs only apply to the example.
Martins
  • 508
  • 4
  • 12
creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402
  • Thank you so much for responding! I was reading through that repo last night and to be honest, none of it is in English, so thank you for trying to make some sense out of it. I have pasted a screenshot of the immediate error I get when I try to use this code. Do you know how to fix this? – Bisclavret Jun 18 '19 at 08:57
  • Thanks again mate! I am sorry to report back unsuccessful but after these changes the app is on fire. Have posted the errors above. Have never seen so many! Can't work out what any of it might mean, unless it is crying because I am not in AndroidX? – Bisclavret Jun 18 '19 at 09:15
  • *gulp* Last time I tried to migrate to AndroidX it bricked my entire app and took me 2 days to roll it all back. The instructions for migration are so poor. If there is no other way, I will try and do this tonight in a few hours time once my wee ones have gone to bed. – Bisclavret Jun 18 '19 at 09:43
  • Hi mate, thanks again. Just about to try this AndroidX migration. The compileSdkVersion is something I have seen many, many times in all my searches for various errors, but it is a complete lie. I have compileSdkVersion 28, minSdkVersion 16, and targetSdkVersion 28 in my android\app\build.gradle file. I have not been able to work out ever why I keep getting messages about compileSdkVersion being less than 28 when it just isn't. The error messaging is bad. Something else is going on. – Bisclavret Jun 18 '19 at 14:20
  • So I have converted to AndroidX and all the errors have gone away. The app now runs but I have absolutely no idea if this is working or not. After you have got in your code: List products = response.productDetails; I have put a print(products); What I get returned is: [Instance of 'ProductDetails']. Is this working? What can I do with this? I am trying to follow the instructions here: https://pub.dev/packages/in_app_purchase but it is so badly written. Literally every line has errors, and most of it doesn't actually explain how to use it. – Bisclavret Jun 18 '19 at 15:25
  • I just want to get the products from the store and then break out the Title, Description and Price of the product, so that I can display it in the store. – Bisclavret Jun 18 '19 at 15:27
  • @Bisclavret Hey, I deleted some comments to make this more readable again. It is working wonderfully for you! You now have a list and it has one element of instace [`ProductDetails`](https://pub.dev/documentation/in_app_purchase/latest/in_app_purchase/ProductDetails-class.html). I linked to the API reference, where you can see that the `ProductDetails` class contains the title, description and price of the product. I updated the answer to show you an example of how to use it. – creativecreatorormaybenot Jun 18 '19 at 16:38
  • This is all starting to work now mate! One quick one though, will the price return the price of the product in the native user's currency? Or will it always return the currency as set by me in the store? – Bisclavret Jun 19 '19 at 12:59
  • @Bisclavret It should return the price based on the platform, i.e. apply a price locale. – creativecreatorormaybenot Jun 19 '19 at 13:14
  • 1
    Oh man, this rabbit hole just keeps going! The documentation is so utterly awful for this. Updated question again with the next section from the documentation that either doesn't work or is incomplete. The products are loading, but I need the user to be able to actually purchase them.... – Bisclavret Jun 19 '19 at 13:28
  • @Bisclavret What exactly is not clear about the ["Making purchases" section](https://pub.dev/packages/in_app_purchase#making-a-purchase)? You use the `ProductDetails` object you got from querying available items and then purchase it. I updated my answer to give an example, however, I am not sure for how long you want this to go on. The documentation has a few mistakes, I agree, however, it should be quite easy to figure that out because there is only a single flow that makes sense and the class names indicate what they actually do. – creativecreatorormaybenot Jun 19 '19 at 14:53
  • Where it basically falls down, and why there are so many forums of confused users looking for help with this, and why everyone has turned to a 3rd party solution (flutter_inapp_purchase) is because the documentation gives you a vague direction, riddled with errors and typos, rather than what we actually need which is a clear, complete, simplistic working example to follow. What I am reading instead is "_verifyPurchase(purchase); // Verify the purchase following the best practices for each storefront." which means absolutely nothing. I have no idea where to even begin with this. I... 1/2 – Bisclavret Jun 19 '19 at 23:47
  • ...I truly appreciate your assistance with this, I really do. I have been telling everyone I know about the help you are giving me and everyone is excited about the prospect of getting this working. What this would then become is an actual working, complete, easy to follow example of the flutter code so that people can actually use it in their applications. The sort that every other plugin provides. Thank you again so much, you have no idea how much your assistance on this means to us. – Bisclavret Jun 19 '19 at 23:49
  • @Bisclavret I think that the plugin does not implement purchase verification, meaning that you would need to handle that yourself. However, this is not different for native iOS or Android either. The example is pretty complete in contrast. It has a [`verifyPurchase` method](https://github.com/flutter/plugins/blob/d106c60260b295a359c6072e5e9b1cf7451c2f70/packages/in_app_purchase/example/lib/main.dart#L349), however, as you can tell, it is not implemented because the implementation of verification is up to you, which it is with any other plugin as well. – creativecreatorormaybenot Jun 20 '19 at 08:52
  • I am now getting "This version of the application is not configured for billing through Google Play." Even though the test store works perfectly. Just trying to work through this mess now, starting with this post: https://stackoverflow.com/questions/11068686/this-version-of-the-application-is-not-configured-for-billing-through-google-pla but there is a massive amount to unpack here, and the purchasing works fine from the test store. – Bisclavret Jun 20 '19 at 12:43
  • @Bisclavret Did you follow step 5, i.e. push your app to the alpha test track with correct signing and enroll the Google account you use in the Play Store of your test device as a tester? The answer to the question you linked also explains the same, it is just that the documentation also states this (in the example's README) and I pointed that out as well. – creativecreatorormaybenot Jun 20 '19 at 13:33
  • It was breaking because I had tried to refactor the store ID. I am upgrading an app which used to be company01.appname, i started the new app under company02.appname and tried to refactor the app back to the original store so I could use all the past data etc. It just won't let me do that. Changing it all back again to company02.appname seemed to fix these issues and I will just have to carry on from here on the new store, unfrotunately. Accepting this answer at this time because it appears to be working. It says "error" on the store but I assume that's because it isn't published yet. – Bisclavret Jun 22 '19 at 06:24
  • quick question here: do you guys know why I still getting my product in one language when I change it properly on my app? I have the product translate on my console – Dani Feb 26 '20 at 10:26
  • when i print response.productDetails. I just get an empty list even though I have created products in the google play console, how i can debug app release ? – Jubei Yagyu Aug 21 '20 at 16:05
  • I'm facing same problem. And I realized the In-app purchases won't appear util the app is published. Just saving on draft won't work. We can publish in alpha or beta. Hope can help any one get some problem like me – Luke Le Sep 02 '20 at 01:44
  • In my case, ProductDetailsResponse.notFoundIds.isNotEmpty. What is the reason for this ?? I pulished my app in alpha and it is live. – K Pradeep Kumar Reddy Sep 12 '20 at 10:27
  • Hey @creativecreatorormaybenot I'd really appreciate it if you could check out a similar question of mine which deals with checking app purchase. Here's the link: https://stackoverflow.com/questions/66292533/how-to-check-if-my-app-was-bought-from-the-google-playstore-programatically-in-f – sambam Feb 20 '21 at 17:51
7

You need to use a reserved SKU for the test: android.test.purchased

When using in_app_purchase:

const List<String> _kProductIds = <String>[
  'android.test.purchased'
];
ProductDetailsResponse productDetailResponse =
  await _connection.queryProductDetails(_kProductIds.toSet());
Mihail P
  • 111
  • 1
  • 3
  • so do you mean use 'android.test.purchased' during testing? but once officially released then your own ids will work? – 68060 Sep 14 '21 at 10:54
5

I just had this same problem (notFoundIds) while using flutter plugin in_app_purchase. So two things to be ensured:

  1. Ensure productId is registered in PlayStore/AppStore as specified by plugin readme;

  2. Before calling queryProductDetails, call isAppPurchaseAvailable which will initialise and wait a bit until it is ready, then queryProductDetails will work. Sample code below:

       Future<List<ProductDetails>> loadProductsForSale() async {
         if(await isAppPurchaseAvailable()) {
           const Set<String> _kIds = {APP_PRODUCTID01};
           final ProductDetailsResponse response =
           await InAppPurchaseConnection.instance.queryProductDetails(_kIds);
           if (response.notFoundIDs.isNotEmpty) {
             debugPrint(
                 '#PurchaseService.loadProductsForSale() notFoundIDs: ${response
                     .notFoundIDs}');
           }
           if (response.error != null) {
             debugPrint(
                 '#PurchaseService.loadProductsForSale() error: ${response.error
                     .code + ' - ' + response.error.message}');
           }
           List<ProductDetails> products = response.productDetails;
           return products;
         } else{
           debugPrint('#PurchaseService.loadProductsForSale() store not available');
           return null;
         }
       }
    
     Future<bool> isAppPurchaseAvailable() async {
     final bool available = await InAppPurchaseConnection.instance.isAvailable();
    
     debugPrint('#PurchaseService.isAppPurchaseAvailable() => $available');
    
     return available;
     if (!available) {
       // The store cannot be reached or accessed. Update the UI accordingly.
    
       return false;
     }
     }
    
jeanadam
  • 419
  • 5
  • 5
  • Facing the issue product not found for ios even if a product id is as created in AppStore and in android, it is working fine with the same code @jeanadam – Mittal Varsani Aug 20 '21 at 12:46
  • There are a lot of steps to make a product available in the AppStore. You may even need bank account approved, beyond other things. I recommend you to review if all the steps to enable purchase and product in App Store Connect were taken. – jeanadam Aug 23 '21 at 03:20
  • I'm facing a product not found issue in flutter only and also tested the same with the ios native project it's working fine @jeanadam – Mittal Varsani Aug 23 '21 at 05:39
5

I have the same problem (specific to IOS). In my case, to receive the Apple products I needed to fill the form o Agreements, Tax and Banking (https://appstoreconnect.apple.com/agreements/). After filling the form and it be approved the products returned.

2

In my case I had to wait several hours in order for product to become available and appeared in list of products in ProductDetailsResponse. Also I believe setting it to Active in Google Play Console also had a role in making it accessible. Because I've created the product yesterday and only after I set it to Active in the Console and waited some hours it started working. So if you're sure you did all correct, try to just wait for some time, till Google services start serving your product to your app.

Konstantin Kozirev
  • 944
  • 1
  • 10
  • 23
  • This is true. Wait for some time before it shows up (as long as you've configured everything correctly) – Aldy J Feb 24 '22 at 11:48
1

In my case In-App purchase working fine in Android but not worked in ios

After lots of struggle found that bundle id project.pbxproj in this file is different than I define in runner

this file location is

projectdirectory -> ios -> Runner.xcodeproj

check bundle id that assigns to PRODUCT_BUNDLE_IDENTIFIER

I hope this may save someone time

Mittal Varsani
  • 5,601
  • 2
  • 15
  • 27
0

On Android: In my case I had to publish the app (was enough to publish an internal testing version) in order to see the IPAs. I also cold booted the emulator and reinstalled the app.

Julian Re
  • 1,023
  • 1
  • 8
  • 10
-1

Had a similar issue, but in my case querying for my store products failed immediately after the app started, but succeeded a little while afterwards. Using @jeanadam's suggestion, specifically, calling InAppPurchaseConnection.instance.isAvailable() before querying for products solved the issue in my case.

I find the method name isAvailable() very confusing, as apparently it doesn't just specify if the relevant store is available or not, but also waits for the store connection to be established.

matanb
  • 484
  • 4
  • 14
  • Are you developing for both Android and iOS? Did you manage to make "isAvailable" return TRUE somehow? In Android, I can query for products but there is no way whatsoever that I can make that function return true... Is returning true for that function really needed ?! – MagisterMundus Apr 02 '22 at 19:29