OpenIap.Maui
3.0.1
dotnet add package OpenIap.Maui --version 3.0.1
NuGet\Install-Package OpenIap.Maui -Version 3.0.1
<PackageReference Include="OpenIap.Maui" Version="3.0.1" />
<PackageVersion Include="OpenIap.Maui" Version="3.0.1" />
<PackageReference Include="OpenIap.Maui" />
paket add OpenIap.Maui --version 3.0.1
#r "nuget: OpenIap.Maui, 3.0.1"
#:package OpenIap.Maui@3.0.1
#addin nuget:?package=OpenIap.Maui&version=3.0.1
#tool nuget:?package=OpenIap.Maui&version=3.0.1
maui-iap
OpenIAP for .NET MAUI - unified in-app purchases on iOS, Android, and macCatalyst from a single C# API.
Status matrix
| Layer | iOS | Android | macCatalyst |
|---|---|---|---|
Generated types (Types.cs) |
yes | yes | yes |
OpenIapClient.Instance facade and listener streams |
yes | yes | yes |
| StoreKit 2 / Play Billing native bindings | yes | yes | yes |
| Example MAUI app | yes | yes | yes |
| NuGet package shape | single public package | single public package | single public package |
Install
OpenIap.Maui 2.x requires the supported .NET 10 SDK and MAUI workload:
dotnet workload install maui
dotnet add package OpenIap.Maui
For manual .csproj edits, copy the current PackageReference from the
OpenIap.Maui NuGet package page.
OpenIap.Maui is the only NuGet package apps reference. The Android and iOS
binding outputs are flattened into the main NuGet package, while Play Services,
Gson, AndroidX, and Kotlin Android libraries remain normal NuGet dependencies so
apps can deduplicate them with their own package graph. The store SDK itself
(Google Play Billing, the Horizon billing library, or the Amazon Appstore SDK)
is linked when the app builds; see Android store.
Stable NuGet releases rebuild the embedded Apple XCFramework with the current App Store-accepted toolchain (Xcode 26.6 / SDK 26.5) and verify every packaged Mach-O slice before publishing. Xcode 27 remains a source-compatibility CI lane until Apple accepts it for App Store submissions, so its guarded StoreKit 27 paths are not included in stable release artifacts yet.
Usage
using OpenIap;
using OpenIap.Maui;
var iap = OpenIapClient.Instance;
var query = (QueryResolver)iap;
var mutate = (MutationResolver)iap;
IDisposable purchaseSub = iap.PurchaseUpdated.Subscribe(async purchase =>
{
bool verified = await VerifyOnServerAsync(purchase);
if (!verified) return;
await mutate.FinishTransactionAsync(
purchase: new PurchaseInput(purchase),
isConsumable: true);
});
IDisposable errorSub = iap.PurchaseError.Subscribe(error =>
{
Console.WriteLine($"{error.Code}: {error.Message}");
});
await mutate.InitConnectionAsync();
var result = await query.FetchProductsAsync(new ProductRequest
{
Skus = new[] { "premium", "coins_100" },
Type = ProductQueryType.InApp,
});
await mutate.RequestPurchaseAsync(new RequestPurchaseProps
{
Type = ProductQueryType.InApp,
RequestPurchase = new RequestPurchasePropsByPlatforms
{
Apple = new RequestPurchaseIosProps { Sku = "coins_100", Quantity = 1 },
Google = new RequestPurchaseAndroidProps { Skus = new[] { "coins_100" } },
},
});
Always validate purchases on your server before granting entitlement, then call
FinishTransactionAsync. On Android, unfinished purchases are refunded
automatically after 3 days.
OpenIap.Maui 2.x exposes OpenIapClient; the deprecated Iap compatibility
facade was removed. Replace Iap calls before upgrading from 1.x.
IAPKit API and webhooks
MAUI exposes the same kit helper surface as expo-iap and
react-native-iap, with C# naming conventions:
using OpenIap;
using OpenIap.Maui;
var kit = OpenIapClient.KitApi(new KitApiOptions
{
ApiKey = "openiap-kit_pk_<your-publishable-key>",
BaseUrl = "https://kit.openiap.dev",
});
var status = await kit.StatusAsync("user-123");
var entitlements = await kit.EntitlementsAsync("user-123");
string? cursor = null;
do
{
var page = await kit.ProductsAsync(new KitProductsOptions
{
Platform = KitProductPlatform.IOS, // required with IncludeClientPayload
IncludeClientPayload = true,
Limit = 25, // default 25, maximum 50
Cursor = cursor,
});
foreach (var product in page.Products)
{
// Consume product.ClientPayload when present.
}
cursor = page.HasMore == true ? page.NextCursor : null;
} while (cursor is not null);
var payload = await kit.ClientPayloadAsync(
"premium.monthly",
KitProductPlatform.IOS);
await kit.BindUserAsync(purchaseToken: "token", userId: "user-123");
Publishable keys are intentionally app-readable. Never embed an
openiap-kit_sk_ secret key in a shipped app; reserve it for trusted backends,
CI, and MCP administration.
Client payloads are public app-readable metadata. Keep secrets and server-only
rules out of them; catalog responses omit payload bodies unless explicitly
requested. Payload-inclusive catalog reads require a platform and return
bounded cursor pages (HasMore / NextCursor). Limit and Cursor are
ignored by the legacy non-payload catalog path. If catalog churn returns
INVALID_CURSOR, restart without Cursor.
IAPKit accepts store-to-server lifecycle webhooks but does not expose an outbound event stream to MAUI or other mobile SDKs. Apps should use purchase verification, scoped entitlement reads, and bounded lifecycle refreshes.
Example app
cd /path/to/openiap
# Android source runs need every store's Google AAR plus the MAUI-owned module AAR.
(cd packages/google && ./gradlew :openiap:assemblePlayRelease :openiap:assembleHorizonRelease :openiap:assembleAmazonRelease)
(cd libraries/maui-iap/android && ../../../packages/google/gradlew :openiap:assembleRelease)
cd libraries/maui-iap/example/OpenIap.Maui.Example
adb uninstall dev.hyo.martie || true
dotnet build -t:Run -f net10.0-android
dotnet build -t:Run -f net10.0-ios
dotnet build -t:Run -f net10.0-maccatalyst
VS Code launch configurations are in libraries/maui-iap/.vscode/launch.json.
The Android launcher builds the AARs and passes its device to the build.
Android store
A Debug build links the store of the device it deploys to — the IDE's target
(AdbTarget), else the one ANDROID_SERIAL names, else the only one attached:
a Quest gets Meta Horizon, a Fire device the Amazon Appstore, anything else
Google Play. Only the Debug configuration looks at a device, so pin every
build that ships to another store:
dotnet publish -f net10.0-android -c Release -p:OpenIapStore=horizon
The aliases google/gplay/googleplay/google-play/gms, meta/quest,
and fire/fireos/fire-os work too, and a value that names no store fails the
build. OpenIapAndroidStore is deprecated: it still pins, with a warning, until
the next major release, and fails the build against a different OpenIapStore.
MAUI only: NuGet fixes dependencies before the build knows the store, so every build also carries Play Services and DataTransport (about 3.1 MB, with their manifest entries) and kotlinx-serialization-json (up to 0.9 MB); the store SDK itself is linked for the chosen store only. The MAUI setup guide lists the manifest entries.
What's generated vs. hand-written
- Generated:
src/OpenIap.Maui/Types.csis synced fromspecs/client/src/generated/Types.cs. Do not edit it by hand. - Hand-written:
OpenIap.cs,ObservableExtensions.cs, native resolver files underPlatforms/, and the binding projects.
Regenerate types with:
cd specs/client && bun run generate
Links
- OpenIAP docs: https://openiap.dev
- Monorepo: https://github.com/hyodotdev/openiap
- Setup guide: https://openiap.dev/docs/setup/maui
Built something with OpenIap.Maui?
List your app on the OpenIAP showcase.
Questions and feedback go to Discussions.
If OpenIap.Maui saves you time, a ⭐ on hyodotdev/openiap helps.
Sponsors
<p align="center"> <a href="https://meta.com"> <img src="https://openiap.dev/meta.svg" alt="Meta" height="80" align="middle"> </a> <a href="https://developer.amazon.com/"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://openiap.dev/sponsors/amazon-dark.webp"> <img src="https://openiap.dev/sponsors/amazon.webp" alt="Amazon Developer" height="44" align="middle"> </picture> </a> </p>
Thank you to Meta and Amazon Developer for supporting OpenIAP. View sponsorship options.
OpenCollective
We also recognize sponsors and backers through OpenCollective. The original react-native-iap collective now supports the broader OpenIAP ecosystem and is managed separately from the main sponsor program.
Sponsors: <a href="https://opencollective.com/openiap#sponsors"><img src="https://opencollective.com/openiap/sponsors.svg?width=890&cache=20260706" alt="OpenCollective sponsors" /></a>
Backers: <a href="https://opencollective.com/openiap#backers"><img src="https://opencollective.com/openiap/backers.svg?width=890&cache=20260706" alt="OpenCollective backers" /></a>
Become a sponsor | Become a backer
Past supporters
Supported the project before the OpenIAP sponsor program.
<p align="center"> <a href="https://namiml.com"> <img src="https://openiap.dev/sponsors/nami.webp" alt="Nami" height="32" align="middle"> </a> <a href="https://www.courier.com/?utm_source=react-native-iap&utm_campaign=osssponsors"> <img src="https://openiap.dev/sponsors/courier.webp" alt="Courier" height="32" align="middle"> </a> </p>
License
MIT - (c) hyodotdev
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-android36.0 is compatible. net10.0-browser was computed. net10.0-ios was computed. net10.0-ios26.0 is compatible. net10.0-maccatalyst was computed. net10.0-maccatalyst26.0 is compatible. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net10.0-android36.0
- GoogleGson (>= 2.14.0.1)
- Xamarin.AndroidX.Activity (>= 1.13.0.1)
- Xamarin.AndroidX.Activity.Ktx (>= 1.13.0.1)
- Xamarin.AndroidX.Collection.Ktx (>= 1.6.0.1)
- Xamarin.AndroidX.Compose.Runtime.Annotation (>= 1.11.4)
- Xamarin.AndroidX.Compose.Runtime.Annotation.Android (>= 1.11.4)
- Xamarin.AndroidX.Fragment (>= 1.9.0)
- Xamarin.AndroidX.Fragment.Ktx (>= 1.9.0)
- Xamarin.AndroidX.Lifecycle.LiveData (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.LiveData.Core (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.LiveData.Core.Ktx (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.Process (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.Runtime (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.Runtime.Ktx (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.Runtime.Ktx.Android (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.ViewModel (>= 2.11.0.1)
- Xamarin.AndroidX.Lifecycle.ViewModel.Ktx (>= 2.11.0.1)
- Xamarin.AndroidX.SavedState (>= 1.5.0.1)
- Xamarin.AndroidX.SavedState.SavedState.Ktx (>= 1.5.0.1)
- Xamarin.AndroidX.Window (>= 1.5.1.3)
- Xamarin.Google.Android.DataTransport.TransportApi (>= 4.1.1.1)
- Xamarin.Google.Android.DataTransport.TransportBackendCct (>= 4.1.1.1)
- Xamarin.Google.Android.DataTransport.TransportRuntime (>= 4.1.1.1)
- Xamarin.GooglePlayServices.Base (>= 118.10.0.2)
- Xamarin.GooglePlayServices.Basement (>= 118.10.0.2)
- Xamarin.GooglePlayServices.Location (>= 121.3.0.10)
- Xamarin.GooglePlayServices.Tasks (>= 118.4.1.2)
- Xamarin.Kotlin.StdLib (>= 2.4.0.1)
- Xamarin.KotlinX.Coroutines.Android (>= 1.11.0.1)
- Xamarin.KotlinX.Coroutines.Core (>= 1.11.0.1)
- Xamarin.KotlinX.Coroutines.Core.Jvm (>= 1.11.0.1)
- Xamarin.KotlinX.Serialization.Json (>= 1.11.0.1)
- Xamarin.KotlinX.Serialization.Json.Jvm (>= 1.11.0.1)
-
net10.0-ios26.0
- No dependencies.
-
net10.0-maccatalyst26.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.1 | 48 | 10/12/2026 |
| 3.0.0 | 117 | 10/9/2026 |
| 3.0.0-rc.1 | 44 | 10/8/2026 |
| 2.6.3 | 87 | 10/7/2026 |
| 2.6.2 | 181 | 9/30/2026 |
| 2.6.1 | 155 | 9/26/2026 |
| 2.6.0 | 132 | 9/26/2026 |
| 2.5.0 | 620 | 8/31/2026 |
| 2.4.1 | 249 | 8/29/2026 |
| 2.4.0 | 142 | 8/26/2026 |
| 2.3.2 | 178 | 8/21/2026 |
| 2.3.1 | 267 | 8/14/2026 |
| 2.3.0 | 126 | 8/11/2026 |
| 2.2.2 | 126 | 8/11/2026 |
| 2.2.1 | 125 | 8/10/2026 |
| 2.2.0 | 112 | 8/9/2026 |
| 2.1.0 | 146 | 8/8/2026 |
| 2.0.2 | 163 | 8/3/2026 |
| 2.0.1 | 136 | 8/1/2026 |
| 2.0.0 | 136 | 7/30/2026 |