flutter_windows_iap 0.0.1
flutter_windows_iap: ^0.0.1 copied to clipboard
Microsoft Store in-app purchase plugin for Flutter Windows — products, purchases, licenses, and One Store validation IDs.
flutter_windows_iap #
A Flutter plugin that exposes Microsoft Store in-app purchase APIs for Windows — no bundled UI.
Fetch add-on products, initiate purchases, verify licenses, and resolve One Store purchase/collection IDs — then build your own paywall and entitlement logic.
Main highlight — native Microsoft Store integration #
Built on Windows.Services.Store (StoreContext) so your Flutter Windows app can use the same add-on catalog, purchase flow, and license checks as a native UWP/WinUI app — Consumable, Durable, and UnmanagedConsumable add-ons, including subscriptions configured as durable products in Partner Center.
| Platform | Windows (Microsoft Store / MSIX packaged app) |
|---|---|
| Dart | >=3.7.0 <4.0.0 |
| Flutter | >=3.16.0 |
| Version | 0.0.1 |
| Channel | com.moontechnolabs.flutter_windows_iap |
| License | MIT |
Features #
| Feature | Description |
|---|---|
| Product catalog | Load add-on metadata (title, description, price, kind) from the Microsoft Store by Store ID |
| Purchase flow | Trigger the system purchase UI via RequestPurchaseAsync |
| License checks | Verify whether the user owns a specific add-on or any active add-on |
| Add-on licenses | Read full license map (isActive, expiration, SKU, offer token) |
| One Store IDs | Resolve purchaseId, collectionId, and OAuth token for server-side validation |
| No bundled UI | Data APIs only — you own the widgets and paywall |
Table of contents #
- Install
- Configure Microsoft Store
- Quick start
- Usage
- API reference
- Architecture
- Example app
- Limitations & notes
- License
Install #
dependencies:
flutter_windows_iap: ^0.0.1
flutter pub add flutter_windows_iap
import 'package:flutter_windows_iap/flutter_windows_iap.dart';
Configure Microsoft Store #
This plugin talks to the Microsoft Store at runtime. Your app must be MSIX-packaged and associated with a Store listing. Unpackaged debug builds will not receive valid Store responses.
1. Partner Center — create add-ons #
- Open Partner Center → your app → Monetization → Add-ons.
- Create one or more add-ons (Consumable, Durable, or Unmanaged Consumable).
- Copy each add-on’s Store ID. You pass this string to every API that accepts a
storeId.
| Add-on type in Partner Center | productKind you may see |
Typical use |
|---|---|---|
| Consumable | Consumable |
Coins, credits, one-time use |
| Durable | Durable |
Permanent unlock, non-expiring subscription SKU |
| Unmanaged consumable | UnmanagedConsumable |
Consumables you track on your own server |
Subscription add-ons are often configured with ProductKind
Durablein the Store API. The plugin queriesConsumable,Durable, andUnmanagedConsumablewhen loading products.
2. Package identity #
Your MSIX package identity (name, publisher, version) must match the app registered in Partner Center. Use tools such as msix or Visual Studio packaging to produce a build you can sideload or submit to the Store.
3. Azure AD / One Store (optional — for getPurchaseId) #
Server-side purchase validation requires OAuth credentials from Azure AD:
| Field | Required | Why |
|---|---|---|
clientId |
Yes | Azure AD application (client) ID |
clientSecret |
Yes | Client secret for the app registration |
tenantId |
Yes | Azure AD tenant that owns the app |
resource |
No | OAuth resource for One Store B2B token (default: https://onestore.microsoft.com) |
collectionsResource |
No | OAuth resource for collections token (default: https://onestore.microsoft.com/b2b/keys/create/collections) |
Register your app in Azure Portal, grant it access to the Microsoft Store commerce APIs, and pass credentials via OneStoreConfig.
4. Testing #
- Store sandbox: Use a Microsoft account enrolled in Partner Center’s sandbox testers.
- Sideloaded MSIX: Install a signed package whose identity matches Partner Center before calling purchase APIs.
- Logs: Native code writes debug logs to
%LocalAppData%\flutter_windows_iap_example\iap_log.txtwhen running the example app.
Quick start #
import 'package:flutter/services.dart';
import 'package:flutter_windows_iap/flutter_windows_iap.dart';
Future<void> purchaseAddon() async {
final iap = FlutterWindowsIap();
// Store ID from Partner Center → Monetization → Add-ons
const storeId = 'StoreId';
// 1. Load product info (title, price, whether user already owns it)
final products = await iap.getProducts(storeId);
if (products.isEmpty) return;
// 2. Start purchase (opens Microsoft Store UI)
final status = await iap.makePurchase(storeId);
// 3. Verify entitlement
final owns = await iap.checkPurchase(storeId: storeId);
}
Usage #
Create one instance of FlutterWindowsIap and reuse it for the app lifetime.
final iap = FlutterWindowsIap();
1. Get products (getProducts) #
Loads Store metadata for a single add-on Store ID.
final List<Product> products = await iap.getProducts('StoreId');
| Parameter | Type | Required | What to pass | Why |
|---|---|---|---|---|
storeId |
String |
Yes | Add-on Store ID from Partner Center | Identifies the SKU in GetStoreProductsAsync |
Returns: Future<List<Product>> — usually one item when the ID is valid; empty list when nothing is found.
Throws: PlatformException when the Store returns an extended error (misconfigured product, network failure, app not associated with Store, etc.).
| Field | Type | Description |
|---|---|---|
title |
String? |
Localized product title from the Store |
description |
String? |
Localized description |
price |
String? |
Formatted price string (e.g. $4.99, $9.99 per month) |
inCollection |
bool? |
true if the signed-in user already owns this add-on |
productKind |
String? |
Store kind: Consumable, Durable, or UnmanagedConsumable |
storeId |
String? |
Echo of the Store ID |
Example — display in UI:
for (final p in products) {
print('${p.title} — ${p.price} (${p.productKind})');
if (p.inCollection == true) print('Already owned');
}
2. Make purchase (makePurchase) #
Opens the Microsoft Store purchase dialog for the given add-on.
final StorePurchaseStatus? status = await iap.makePurchase('StoreId');
| Parameter | Type | Required | What to pass | Why |
|---|---|---|---|---|
storeId |
String |
Yes | Same Store ID used in getProducts |
Passed to RequestPurchaseAsync |
Returns: Future<StorePurchaseStatus?> — see enum below. Returns null for an unrecognized status code.
Throws: PlatformException when ExtendedError is not S_OK (e.g. product not configured — HRESULT 0x803f6107).
| Status | Meaning | Typical next step |
|---|---|---|
StorePurchaseStatus.succeeded |
User completed purchase | Unlock content; call checkPurchase |
StorePurchaseStatus.alreadyPurchased |
User already owns it | Treat as entitled |
StorePurchaseStatus.notPurchased |
User cancelled or did not buy | Keep paywall |
StorePurchaseStatus.networkError |
Network issue | Retry later |
StorePurchaseStatus.serverError |
Store server error | Retry later |
switch (await iap.makePurchase(storeId)) {
case StorePurchaseStatus.succeeded:
case StorePurchaseStatus.alreadyPurchased:
// grant access
break;
case StorePurchaseStatus.notPurchased:
// user declined
break;
case StorePurchaseStatus.networkError:
case StorePurchaseStatus.serverError:
// show error, retry
break;
default:
break;
}
3. Check purchase (checkPurchase) #
Checks whether the current user has an active add-on license. Uses GetAppLicenseAsync and inspects add-on licenses.
final bool owns = await iap.checkPurchase(storeId: 'StoreId');
| Parameter | Type | Required | What to pass | Why |
|---|---|---|---|---|
storeId |
String |
No (default '') |
Specific add-on Store ID, or empty string | Controls which license is checked — see rules below |
Returns: Future<bool>
storeId value |
Returns true when… |
Returns false when… |
|---|---|---|
| Non-empty | That add-on’s license exists and IsActive == true |
ID not found, inactive, or app license inactive |
Empty ('') |
Any add-on license has IsActive == true |
No active add-ons, or app license inactive |
If the app license itself is not active (
AppLicense.IsActive == false), this method always returnsfalse.
Use this on app start, after purchase, and when restoring entitlements:
// Premium feature for one SKU
if (await iap.checkPurchase(storeId: premiumStoreId)) {
showPremiumContent();
}
// Any paid add-on (e.g. “has anything unlocked?”)
if (await iap.checkPurchase()) {
showPaidTier();
}
4. Add-on licenses (getAddonLicenses) #
Returns the full license map for all add-ons the user has ever acquired.
final Map<String, StoreLicense> licenses = await iap.getAddonLicenses();
| Parameter | None |
Returns: Future<Map<String, StoreLicense>> — keys are add-on Store IDs; values are license objects.
StoreLicense field |
Type | Description |
|---|---|---|
isActive |
bool? |
Whether the license is currently valid |
skuStoreId |
String? |
SKU Store ID |
inAppOfferToken |
String? |
Offer token for promotions / trials |
expirationDate |
num? |
Raw Windows DateTime tick count |
Helper:
final expiry = license.getExpirationDate(); // DateTime? — converted from Store ticks
Use when you need expiration dates, offer tokens, or a full entitlement snapshot:
for (final entry in licenses.entries) {
print('${entry.key}: active=${entry.value.isActive}, '
'expires=${entry.value.getExpirationDate()}');
}
5. Purchase & collection IDs (getPurchaseId) #
Resolves Microsoft Store customer identifiers for server-side purchase and collections validation (One Store commerce API).
final Map<String, String> ids = await iap.getPurchaseId(
'UserId',
config: const OneStoreConfig(
clientId: 'ClientId',
clientSecret: 'ClientSecret',
tenantId: 'TenantId',
),
);
| Parameter | Type | Required | What to pass | Why |
|---|---|---|---|---|
userId |
String |
Yes | A stable, anonymous ID you assign per user (not PII) | Passed to GetCustomerPurchaseIdAsync / GetCustomerCollectionsIdAsync |
config |
OneStoreConfig? |
Yes (credentials required) | Azure AD OAuth settings | Acquires tokens for Store API calls |
Returns: Future<Map<String, String>>
| Key | Description |
|---|---|
purchaseId |
Customer purchase ID for purchase validation APIs |
collectionId |
Customer collections ID for entitlement queries |
azureToken |
One Store B2B OAuth access token (use promptly; short-lived) |
If native code returns no map, Dart fills 'NA' for each key. On failure, expect PlatformException with codes such as invalid_oauth_config, oauth_token_failed, or oauth_collections_token_failed.
Security: Never ship clientSecret in client code for production. Prefer a backend that holds secrets and returns tokens or validated entitlements to the app.
// Send to your backend — do not log secrets or tokens in release builds
await http.post(
Uri.parse('https://your-api.com/validate-purchase'),
body: {
'userId': userId,
'purchaseId': ids['purchaseId']!,
'collectionId': ids['collectionId']!,
},
);
API reference #
FlutterWindowsIap #
| Method | Returns | Description |
|---|---|---|
getProducts(storeId) |
Future<List<Product>> |
Load add-on metadata from Store catalog |
makePurchase(storeId) |
Future<StorePurchaseStatus?> |
Start Store purchase UI |
checkPurchase({storeId}) |
Future<bool> |
Check active add-on license |
getAddonLicenses() |
Future<Map<String, StoreLicense>> |
All add-on licenses for current user |
getPurchaseId(userId, {config}) |
Future<Map<String, String>> |
Purchase ID, collection ID, OAuth token |
Limitations & notes #
| Topic | Detail |
|---|---|
| Platform | Windows only. Other platforms are not supported by this package. |
| Packaging | Store APIs require an MSIX-packaged app associated with Partner Center. Plain flutter run without Store association may fail or return empty data. |
| Store ID | Must match an add-on configured and published (or in flight) in Partner Center for your app identity. |
| User account | Purchases and licenses are tied to the Microsoft account signed into the Store on the device. |
| Consumables | Microsoft does not auto-consume consumables; your app or backend must track consumption if needed. |
| Subscriptions | Renewal and grace periods follow Store rules; use StoreLicense.isActive and getExpirationDate() for time-bound access. |
| OAuth secrets | getPurchaseId needs Azure credentials — keep secrets on a server in production. |
| Errors | getProducts and makePurchase surface Store ExtendedError as PlatformException. Always wrap in try/catch in UI code. |
| macOS stub | getProducts / checkPurchase contain macOS stubs that throw or return false — ignore unless you fork the plugin. |
License #
MIT © MOON TECHNOLABS PVT LTD — see LICENSE.