Pro and purchases
verified on the server.
Sell Pro (monthly, yearly or lifetime), Remove ads and an AI report pack through Google Play and the App Store. Every purchase is checked with the store by the backend before anything is granted.
01Products
| Product | Default product id | Store type | What the user gets |
|---|---|---|---|
| Pro monthly | pro_monthly | Auto-renewable subscription | Pro until the subscription's expiry |
| Pro yearly | pro_yearly | Auto-renewable subscription | Pro until the subscription's expiry |
| Pro lifetime | pro_lifetime | One-time (non-consumable) | Pro for good |
| Remove ads | remove_ads | One-time (non-consumable) | No ads |
| AI report pack | ai_pack_10 | Consumable | AI credits (10 per pack by default, 1–1000), one per AI coin report |
You can change every product id in the console; use the same ids in both stores. You set the prices in Play Console and App Store Connect; they are not in the code.
What Pro gives
| Limit | Free (default) | Pro (default) |
|---|---|---|
| Portfolios | 1 | 20 |
| Active price alerts (enforced by the server) | 3 | 100 |
| Tracked wallets | 1 | 20 |
| AI coin reports per day (with AI on) | 0 free, 1 by rewarded ad | 20 |
| Ads | As configured | None |
The perks list on the Pro screen is editable (Console → Pro and purchases → Perks). Only list what Pro really gives.
02Switch it on
- Create the products in both storesWith the product ids above (or your own). Subscriptions in Play Console need a base plan; in App Store Connect put them in one subscription group.
- Set up verificationGoogle Play and Apple, below. Without it the API answers
503 iap_unconfiguredand grants nothing. - Fill in the consoleConsole → Pro and purchases: product ids, AI pack size, then turn on Offer Pro and Sell in-app purchases.
- TestWith a Play license tester and an App Store sandbox account (set App Store environment to Sandbox for TestFlight, back to Production for release).
03Google Play verification
The backend calls the Google Play Developer API (purchases.subscriptionsv2 and purchases.products) and reads the order for the price.
- Package nameConsole → Pro and purchases → Android package name (your application id, owner only).
- Credentials, option A: service account JSONIn Google Cloud, create a service account in your project and a JSON key. In Play Console → Users and permissions, invite the service account's e-mail with access to the app and the permission View financial data (the permission the code comment in
firebase/functions/src/iap.tsnames). Paste the whole JSON into Google Play service account (JSON) (write-only; the test checks only the file's shape). - Credentials, option B: no JSONLeave the JSON empty and invite the Cloud Functions runtime service account (shown in Google Cloud → IAM) in Play Console the same way.
New Play permissions can take a while to apply. If verification fails right after setup, wait and try again.
04App Store verification
The backend calls the App Store Server API with an In-App Purchase key and decodes the signed transaction from Apple.
- Create the keyApp Store Connect → Users and Access → Integrations → In-App Purchase → generate a key. Download the
.p8file (only once) and note the Key ID and the Issuer ID. - Enter themConsole → Pro and purchases: iOS bundle id, App Store Connect issuer id, In-app purchase key id, and paste the whole
.p8file including the BEGIN/END lines (write-only; the test only checks that it parses). - EnvironmentSandbox for TestFlight and sandbox testers, Production for the App Store.
05How a purchase is granted
- The app buys through Flutter's official
in_app_purchaseplugin and sends the purchase toPOST /v1/purchases/verify. The app never grants anything itself. - The backend verifies with the store, then grants Pro, Remove ads or AI credits. Each order is recorded once (
purchases/{platform}_{orderId}), so a repeated request never grants twice and a pack is never credited twice. - Restore purchases (on the paywall and in Settings) sends the store's restored purchases through the same check.
- A subscription grants Pro until the expiry the store reports. A newer verified expiry extends it. There are no server-to-server store notifications in 2.0.0: a renewal is picked up when the app sends the renewed purchase again, for example after Restore purchases.
- You can also grant Pro (until a date or for life), Remove ads or AI credits by hand on the console's Users page, with a reason. Console grants are separate from store grants.
Source: firebase/functions/src/api/commerce.ts, firebase/functions/src/iap.ts, app/lib/iap/purchase_service.dart.
06Revenue records
When the store reports the price of a new verified order, the backend writes a revenue event with the amount and currency exactly as reported (source store). It is shown in the Revenue centre. A subscription is recorded once, with its first verified order; renewals extend Pro but are not added as new revenue events. Store fees and taxes are not deducted. Your store payout reports remain the authoritative figures.
07In the web demo
Purchases are simulated and labelled "Demo — no charge". Nothing is bought or charged.