I’m developing a free Flutter app (Android and iOS), that offers premium content (in-app subscription) using official Flutter in-app-purchase plugin – latest version 2.0.1.
We’d like to use Google Play Promo codes to give premium content for free (with no extra charges) to some users.
What I’ve tried so far:
Promo codes for SUBSCRIPTION: They only give a longer free trial (and charge user afterwards if they don’t cancel). The Promo codes worked fine but this is not really suitable for us because it can charge users.
Promo codes for PRODUCT: A friend suggested to create an in-app Product that unlocks premium content and promo code would give this product for free. I’ve generated promo codes for it and started testing it through Internal testing. The Google Play documentation says it’s possible to Redeem the code from A) Google Play or B) from your app.
Problems with both options:
A) Google play: When we Redeem the code from Google Play, the code is accepted and message is displayed saying the product was applied. However:
we don’t get any purchase notification in our backend system (there was no received message in play_billing Pub sub topic where we usually receive purchase notifications from Google Play).
The purchase is also not visible in Google Play Developer Console > Orders.
When we try to buy the product from the app (for same Google account) we get Google in-app purchase dialog with Error: You already own this item. We tried clearing Google play app’s cache and data but that didn’t help.
there is no method in Flutter in-app-purchase plugin that we can use to query if user has already purchased the product (and just unlock the premium content then)
B) From the app: I don’t think this is supported by official Flutter in-app-purchase plugin. The plugin documentation only mentions Apple store Promo codes. Also when we press the button to buy the product the Google in-app purchase dialog doesn’t contain any option to use Promo code. And no, I didn’t use Licence testing (test card) but my real private credit card.
I feel so stuck!!! Any help is much appreciated! I need to start a promotion campaign next week to get as many user feedback as possible (to apply for funding).
——-UPDATE———————–
Thanks to another developer we were able to identify the reason why “Redeem code” option didn’t appear in the billing dialog.
B) From the app: The Redeem code option in the billing bottom-sheet dialog appears only
when in production mode
for new users (it’s not visible if the user applied the one-time code before or if the user is a tester user)
Okay, this is a tricky situation but very understandable. You\’ve explored the obvious approaches and hit the limitations. Here\’s a breakdown of the problem, a proposed solution, and considerations, keeping in mind the need for a quick launch of your promotion campaign:\n\n**Understanding the Core Problem**\n\n* **Subscription Promo Code Limitations:** These aren\’t suitable because they always lead to an eventual charge if not canceled, which you want to avoid.\n* **One-Time Product Promo Code Challenges:** The core issue is Google Play\’s handling of redeemed promo codes and how that intersects with the Flutter in-app-purchase plugin:\n\n * **Silent Redemption:** Google Play *does* redeem the code (as evidenced by the \”You already own this item\” error), but it doesn\’t trigger the expected purchase flow that would notify your backend or the plugin. This is likely an optimization by Google to avoid unnecessary processing for free redemptions. It\’s designed for the user to receive the benefit without a full purchase transaction.\n * **Lack of Query API:** The `flutter_inapp_purchase` plugin lacks a direct method to query if a user *has already redeemed* a promo code for a specific product, distinct from a regular purchase. Standard purchase history queries won\’t help because these silent redemptions aren\’t treated as typical purchases.\n * **Redeem code only for new user:** In the end the Google redeem flow is only possible for new users (not suitable for testing).\n\n**Proposed Solution: A Hybrid Approach with Server-Side Validation (Most Reliable)**\n\nThe best solution will involve validating the promo code and granting premium access on *your server*, independent of the standard purchase flow as much as possible.\n\n1. **Backend Changes:**\n\n * **Promo Code Redemption Endpoint:** Create a new API endpoint on your backend specifically for redeeming promo codes. This endpoint will:\n\n * **Receive:** The user\’s unique identifier (e.g., Firebase UID, custom user ID), and the promo code.\n * **Validate (Server-Side):** Send the promo code and the package name of your app to the Google Play Developer API\’s `Monetization.Products.Subscriptionsv2.Acknowledge` and `Monetization.Products.Purchases.Acknowledge` endpoint. (The specific endpoint depends on whether you\’re treating this as a subscription *equivalent* or a one-time product.) Use the Google Play Developer API to validate the code. This is crucial because it\’s the only reliable way to confirm the code\’s validity *and* that it hasn\’t been used before.\n * **Grant Access:** If the code is valid and unused, mark it as used in your backend database and grant the user premium access (e.g., set a flag in their user profile, update their subscription status in your own system, etc.).\n * **Return Success/Failure:** Return a success or failure message to the app.\n\n2. **Flutter App Changes:**\n\n * **Promo Code Redemption Screen:** Create a simple UI in your app where users can enter the promo code.\n * **Call the Backend API:** When the user submits the code, send it to your backend\’s redemption endpoint along with the user\’s ID.\n * **Handle Response:**\n\n * **Success:** If the backend returns success, update the app\’s state to reflect premium access (e.g., show premium content, disable ads, etc.). Store a flag locally (e.g., using `shared_preferences`) to remember that the user has redeemed a code, so you don\’t need to call the API every time the app starts.\n * **Failure:** Display an error message to the user (e.g., \”Invalid code,\” \”Code already used,\” \”An error occurred, please try again later.\”).\n\n3. **Generating Promo Codes:**\n\n * Continue generating promo codes as you were doing for one-time products (if that fits your model). The important thing is that *your backend* is now responsible for validating and tracking their usage.\n\n**Why This Approach Is Best:**\n\n* **Reliable Validation:** Using the Google Play Developer API for validation is the most reliable way to ensure the code is legitimate and hasn\’t been abused.\n* **Centralized Control:** Your backend has full control over granting and revoking premium access.\n* **Works Around Plugin Limitations:** This bypasses the limitations of the `flutter_inapp_purchase` plugin in handling silent promo code redemptions.\n* **Flexibility:** You can easily adapt this system to other promotional methods in the future.\n* **Fast Launch:** While it requires backend work, it\’s a focused effort to implement a dedicated promo code system.\n\n**Implementation Steps:**\n\n1. **Backend Development (Highest Priority):** Focus on building the promo code redemption endpoint on your backend. This is the critical piece. Prioritize secure validation and proper error handling.\n2. **Flutter UI:** Create the promo code entry screen in your Flutter app.\n3. **API Integration:** Connect the Flutter app to your backend\’s API.\n4. **Testing:** Thoroughly test the entire flow, including error cases.\n\n**Important Considerations:**\n\n* **Security:** Protect your API endpoint from abuse. Implement rate limiting and consider requiring user authentication.\n* **Error Handling:** Provide clear and helpful error messages to the user. Log errors on your backend for debugging.\n* **Google Play Developer API Setup:** You\’ll need to enable the Google Play Developer API for your project and set up authentication credentials. This can be a bit involved, so follow the official Google documentation carefully.\n* **Abuse Prevention:** Implement mechanisms to prevent users from creating multiple accounts to redeem multiple promo codes. This could include device fingerprinting or requiring email verification.\n* **Documentation:** Clearly document the promo code redemption process for your internal team.\n\n**Alternative (Less Recommended, Faster to Implement Initially):**\n\nIf you are extremely short on time and resources, and you are willing to accept some risk, you could try a simpler approach:\n\n1. **Generate a Unique \”Unlock Code\” in your App:** Instead of relying on Google Play\’s promo codes, generate your own unique unlock codes.\n2. **Store Valid Codes on your Server:** Create a database table to store these valid unlock codes.\n3. **Backend API:** Create an API endpoint that validates the unlock code against your database and grants access if valid.\n4. **Flutter App:** Implement a UI where users can enter the unlock code and call your API.\n\n**Drawbacks of the Alternative Approach:**\n\n* **No Google Play Validation:** You\’re not leveraging Google Play\’s built-in promo code system, so you lose any potential benefits they offer (e.g., fraud prevention).\n* **More Vulnerable to Abuse:** It\’s easier for users to share or guess your unlock codes.\n* **Less Scalable:** Managing your own unlock codes can become cumbersome as your user base grows.\n\n**Why the Hybrid Server-Side Validation is Strongly Recommended:**\n\nWhile the alternative is faster to implement *initially*, the long-term benefits of using Google Play\’s promo code system, combined with server-side validation, outweigh the initial development effort. It provides better security, scalability, and integration with the Google Play ecosystem.\n\n**Prioritize!**\n\nGiven your tight timeline, focus on getting the backend promo code redemption endpoint working first. That\’s the foundation of the solution. You can then build the Flutter UI and integrate it with the API.\n\nThis hybrid approach gives you the best balance of leveraging Google Play\’s features while maintaining control and reliability in your promo code system. Good luck with your promotion campaign!\n