An implementation guide to Flutter in-app purchases, subscription entitlements, AI usage credits, and reliable backend accounting.
A Flutter AI app can complete a purchase successfully and still get billing wrong. A duplicated callback might grant credits twice. Two simultaneous requests might spend the same balance. A failed generation might leave a customer charged for an output they never received.
These problems arise because payments, subscription access, and AI usage are separate systems.
A reliable implementation connects them through verified transactions and a server-controlled credit ledger. Flutter handles the purchasing experience and displays account state; the backend decides what the customer owns and what each AI request can spend.
1. Define What the Subscription and Credits Actually Buy
Start with a product model that users and engineers can explain consistently.
| Component | Purpose | Illustrative example |
|---|---|---|
| Subscription | Provides recurring access to paid features | Access to premium models and saved projects |
| Included allowance | Supplies usage under the subscription’s terms | 500 credits per paid billing period |
| Credit pack | Adds usage through a separate purchase | A consumable pack containing 200 credits |
| Promotional grant | Provides trial or support-related usage | 20 introductory credits |
These quantities are examples, not recommended pricing.
Define what happens when a subscription ends, a renewal fails, or a customer purchases a top-up. Avoid combining every grant into an unexplained balance if the underlying terms differ.
Also distinguish application credits from model tokens. Credits are a product unit. The backend translates an operation into its credit cost using a versioned pricing rule.
2. Choose the Purchase Route Before Writing the Paywall
For mobile apps selling digital functionality, store billing rules affect the architecture.
Apple’s guidelines generally require in-app purchase for unlocking digital features. Google Play also generally requires its billing system for in-app digital goods and services, subject to specified exceptions and eligible alternative billing or external-offer programs. The applicable storefront, program enrollment, and distribution route must be checked before choosing an external checkout.
Flutter developers have two broad implementation options:
- Direct store integration: Use Flutter’s
in_app_purchasepackage and implement verification, lifecycle handling, and account reconciliation. - Managed purchase infrastructure: Use a provider’s Flutter SDK to simplify parts of purchase validation, entitlements, and subscription management.
Flutter’s official plugin supports consumables, permanent upgrades, and subscriptions through the underlying stores. It provides a common API while retaining store-specific configuration requirements.
A managed SDK can reduce integration work. It does not automatically define the business rules for charging AI requests.
3. Separate Subscription Entitlements From Credit Balances
An entitlement answers:
Can this account access this feature?
A credit balance answers:
How much usage can this account spend?
Store them separately. A customer might have an active subscription with no remaining allowance, or purchased credits that remain usable after subscription access ends, depending on the product’s disclosed terms.
A practical backend model includes:
| Record | Important fields |
|---|---|
| Subscription | Account, product, status, effective dates, store reference |
| Credit grant | Source, granted units, applicable expiration, purchase reference |
| Ledger entry | Grant, debit, reversal, or adjustment with a unique reference |
| AI job | Account, request ID, pricing version, reservation, execution state |
| Billing event | Provider event ID, processing state, transaction reference |
Use integer credit units. If fractional credits are necessary, define a smaller integer denomination rather than relying on floating-point arithmetic.
4. Verify Purchases Before Granting Value
The Flutter app should never tell the backend how many credits a purchase deserves.
Instead, the backend should verify the store transaction or trusted provider event, identify the product, and resolve its grant from a server-side catalog.
Recommended processing steps are:
- Receive the purchase evidence through an authenticated request or trusted event.
- Verify its authenticity, product, environment, and applicable purchase state.
- Check whether the transaction has already been processed.
- Persist the entitlement or credit grant.
- Complete the required store acknowledgment or finishing process through the selected integration.
Google’s billing documentation requires checking that a purchase is in the PURCHASED state before granting entitlement. Pending payments should remain pending in the application.
Use a unique transaction constraint so repeated delivery cannot create additional grants. A renewal must be distinguishable from the original purchase and from a replay of an already processed renewal.
5. Treat Flutter Purchase Callbacks as Recoverable Events
Subscribe to purchase updates early in the application lifecycle. Updates can arrive after an app restart, so the listener should not exist only while the paywall is visible.
The interface should represent states such as:
- Waiting for store confirmation.
- Verifying the purchase.
- Purchase confirmed.
- Verification temporarily unavailable.
- Purchase canceled or failed.
After verified delivery, finish the transaction according to the integration’s documented flow. The official Flutter plugin warns that incomplete purchases can cause refunds on Android or repeated unfinished transactions on Apple platforms.
If using a managed billing SDK, follow that SDK’s transaction lifecycle. Avoid adding a second independent completion mechanism without understanding which layer owns it.
6. Reserve Credits Before Starting an AI Request
A balance check followed by a later debit is vulnerable to concurrent requests.
Suppose an account has ten credits. Two requests each read that balance and both start an operation costing eight credits. A client-side check cannot prevent the overspend.
A recommended backend workflow is:
Reserve → execute → settle or release.
During reservation, an atomic database operation checks available credits and records a hold against the job. Only a successful reservation allows execution to proceed.
For fixed-price operations, settle the agreed charge after the defined success condition. For variable-price operations, reserve a disclosed maximum and settle the measured amount, releasing the unused portion.
The customer-facing charge policy should specify what happens for partial output, cancellation, and failed generation. Provider costs and customer charges need not follow identical rules.
7. Make Retries Safe Without Promising Impossible Guarantees
Assign one request ID to one user action and preserve it across retries.
For example, the following conceptual request contract lets the server identify a repeated submission:
{
"requestId": "generation-7e41",
"operation": "summarize_document",
"documentId": "document-128",
"quoteId": "quote-904"
}The authenticated account comes from the session. The server resolves the quote and verifies its operation, validity, and price.
A repeated request should return the existing job rather than create another reservation.
However, database idempotency alone cannot guarantee that an external AI provider runs an operation exactly once. If the provider accepts a request and the connection times out, execution may be uncertain.
Keep that job in a reconciliation state. Use provider idempotency or job-status APIs where available, and avoid blindly starting another generation.
8. Design Credit Expiration Carefully
Credit expiration is a product and store-policy decision.
Apple’s general guideline says purchased in-app credits may not expire. Apple also documents an entitlement for qualifying generative AI apps to offer expiring consumables. That entitlement has conditions, including a minimum 12-month expiration term unless local law requires otherwise, clear disclosure, balance visibility, spending the soonest-expiring credits first, and expiration reminders.
Therefore, a monthly “reset all credits” job is an unsafe default.
Represent purchased packs, subscription-related allowances, and promotional grants separately. Confirm the applicable terms for each category before configuring expiration or rollover.
9. Handle Renewals, Refunds, and Device Changes
Billing state can change while the app is closed.
Apple’s App Store Server Notifications V2 supports server-side handling of purchase lifecycle events, including refunds. Refund processing should update the relevant account records and communicate resulting actions to the customer.
A robust implementation should also reconcile subscription state periodically rather than depending exclusively on event arrival order.
For credit-pack refunds, connect the adjustment to the original grant. If some credits have already been spent, apply a documented policy instead of silently removing unrelated purchases.
Cross-device recovery needs account-backed state. Flutter’s purchase documentation explains that consumed purchases are not restored like ordinary owned products; restoring remaining consumable balances requires server-side persistence.
10. What Five Companies Demonstrate in Practice
The following companies publish relevant implementation work or Flutter billing capabilities. They occupy different roles: engineering partner, billing infrastructure, paywall tooling, and app development platform.
Public evidence does not establish that all five use the complete AI credit architecture proposed in this article.
| Company | Documented practice | Relevant engineering lesson |
|---|---|---|
| GeekyAnts | Its portfolio includes subscription billing projects and Flutter applications with memberships and payments. | Billing needs to connect with backend operations and the wider product experience. These examples do not establish a published Flutter AI credit ledger. |
| RevenueCat | Offers a Flutter purchase SDK and In-App Currency capabilities, including grants from verified purchases and balance access. | Purchase infrastructure can support credits, while spending still requires trusted backend integration. |
| Adapty | Documents Flutter integration for products including subscriptions, consumables, and lifetime access, with custom or managed paywalls. | Product retrieval and purchase handling can be separated from paywall presentation. |
| Superwall | Provides a Flutter SDK and documentation for paywalls, campaigns, and subscription-related integrations. | Paywall experimentation is a distinct layer from authoritative usage accounting. |
| FlutterFlow | Documents a RevenueCat integration for mobile purchase, entitlement, and restoration workflows. | Visual Flutter development can integrate managed billing, with custom backend logic for AI usage. |
These examples are supported by the companies’ public portfolios and documentation.
RevenueCat specifically documents that depositing or spending In-App Currency through its Developer API uses a secret key that must remain outside the application. That reinforces the backend boundary for a Flutter AI app.
11. Test the Failure Paths Before Launch
A successful sandbox purchase is only the beginning.
| Scenario | Expected behavior |
|---|---|
| Duplicate purchase event | One grant |
| Two concurrent AI requests | Atomic reservations prevent overspending |
| App closes after payment | Verified value appears after recovery |
| AI request times out | Existing job is reconciled before retrying execution |
| Renewal notification is delayed | State is reconciled without duplicate allowance grants |
| Credit pack is refunded | Adjustment references the original purchase |
| Customer changes devices | Account-backed balances remain consistent |
| Model pricing changes | Existing quotes and jobs retain their pricing version |
The strongest Flutter billing implementation makes ownership clear at every stage: the store confirms payment, trusted backend logic grants value, and the AI service consumes only authorized usage.
That separation lets the paywall remain simple while the system handles the complexity behind it.


















