InAppBillingPlugin wraps StoreKit and Google Play billing for .NET
Cross-platform In App Billing Plugin for .NET
At a glance
- What is it?
- InAppBillingPlugin is a .NET MAUI and Windows plugin for querying products, making purchases and restoring them across Apple and Android, with the two stores unified behind one API. The catch is that its author has stated he does not plan to continue supporting it, so the store APIs it targets keep moving underneath it.
- Who is it for?
- InAppBillingPlugin suits a .NET MAUI or Windows team that needs a cross platform in-app purchase API today and is prepared to own the maintenance, because the author has said in plain terms that he does not plan to continue supporting the library and invites forks. It does not suit a team that wants a store SDK to keep pace with platform changes on its own, since Google Play Billing and StoreKit both evolve faster than a community wrapper can.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 173 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One API over two store SDKs that do not match
The problem is that buying something in an app is two different implementations. Apple has StoreKit, Google has the Play Billing Library, and the concepts, the error handling and the lifecycle of a purchase differ enough that a team supporting both platforms writes the purchase logic twice. InAppBillingPlugin is described as a simple in-app purchase plugin for .NET MAUI and Windows to query item information, purchase items, restore items and more, and the point of it is that a C# developer writes one call rather than one per store. The intended user is a .NET developer shipping to iOS and Android from a single MAUI project. The author is James Montemagno, and the plugin is published on NuGet as Plugin.InAppBilling with the documentation hosted on GitHub Pages.
The author has said he is stepping away from maintenance
This is the first thing to read, and it is stated at the top of the README. The author says he does not plan on continuing support for the library, and gives his reasoning in a note from April 2025: in-app purchase APIs have always been extremely complex, hard to manage, hard to update and hard to streamline into a single API, and with StoreKit being deprecated for StoreKit2 and the ever-evolving Android Billing Library always having massive changes, he does not have the time and energy to keep the library up to date. He thanks the contributors and users, and encourages forking and evolving the library, and says you can pull the source code into your project directly. That is a much more useful statement than a stale version number, because it tells you the code is a starting point you own rather than a dependency you can rely on.
Version numbers track the platform, not just the library
The version table is a map of the platform matrix, and it is the most practical thing in the README. Version 10 supports .NET 10 or newer and Android Billing v8, version 9 supports .NET 9 and Android Billing v7, version 8 supports .NET 8 for .NET MAUI and Windows apps, and version 7 supported .NET 6, .NET MAUI, UWP and Xamarin and Xamarin.Forms projects and was built against Android Billing Library 6.0. So the major version of the plugin tells you which .NET and which Android Billing it targets, and the platform support table gives the minimums: iOS for .NET 10 or newer, macCatalyst for .NET at any version, Android for .NET 21 or newer, Windows App SDK with WinUI 3 at 10 or newer, and .NET MAUI at any version. The recent tagged releases are v9.0.5.1 from 2024-11-29, v9.0.4.1 from 2024-11-27 and v9.0.3.1 from 2024-11-27.
Connecting, checking purchases and listening for updates
The Android flow is documented in the most detail, and the code shows the shape of the API. You connect to the billing service, then query the purchases for a given item type, and if any of them is in a pending state you subscribe to a callback that fires when purchases are updated:
// Connect to the service here
await CrossInAppBilling.Current.ConnectAsync();
// Check if there are pending orders, if so then subscribe
var purchases = await CrossInAppBilling.Current.GetPurchasesAsync(ItemType.InAppPurchase);
if (purchases?.Any(p => p.State == PurchaseState.PaymentPending) ?? false)
{
Plugin.InAppBilling.InAppBillingImplementation.OnAndroidPurchasesUpdated = (billingResult, purchases) =>
{
// decide what you are going to do here with purchases
// probably acknowledge
// probably disconnect
};
}
else
{
await CrossInAppBilling.Current.DisconnectAsync();The callback is assigned to a static handler, OnAndroidPurchasesUpdated, and the code comments in the example are honest about the work still to be done, deciding what to do with the purchases and probably acknowledging them, then probably disconnecting. The README also flags a subtlety in the connection lifecycle. If you do connect, the IsConnected property will be true, and when you make purchases or check purchases again you should check ahead of time and not re-connect or disconnect if there are pending purchases.
Pending purchases need handling in your code, not in the plugin
The pending transaction section is the longest part of the README and it is a specification for your application rather than a description of the plugin. The rule is that if the result of a purchase is PurchaseState.PaymentPending, you store the order details locally and inform the user that they will have access to the product when the payment completes. When the user starts the app, or visits a particular page, you check the stored state and call GetPurchasesAsync to query for a purchase that matches. If it is still pending you show the same no-access message, and if it is Purchased you call ConsumePurchaseAsync or AcknowledgePurchaseAsync depending on the product type. The author recommends reading the entire Google Play Billing System documentation, and this section is the clearest illustration of why the author decided the problem was too hard to keep on top of.
Consumables and non-consumables are acknowledged differently
On Android, if you purchase anything you must first acknowledge the purchase or it will be refunded, and the two product types are handled differently. The README points at the Google Play documentation for the process and for pending purchases, then explains the split. For consumables, the consumeAsync method fulfils the acknowledgement requirement and indicates that the app has granted entitlement to the user, and it also enables the app to make the one-time product available for purchase again. So if you have a consumable, ConsumePurchaseAsync also acknowledges it, while if you have a non-consumable you need to call AcknowledgePurchaseAsync. If you receive an error in Google Play you may need to add a meta-data entry to your AndroidManifest.xml inside the application node, with the name set to the Play Billing client version and the value set to the version of the Billing Library that is a dependency on the NuGet package:
<meta-data
android:name="com.google.android.play.billingclient.version"
android:value="X.X.X" />Linker and ProGuard settings that Android and iOS builds need
For version 4 and later, the README documents linker settings that are easy to miss and produce confusing failures when wrong. On Android, if you are setting Link All, you may need to add the assembly references for the plugin and for the Google Billing client. There are also Android ProGuard rules, and they are given as a pair of keep rules, one for the billing client API package and one for the vending billing package. On iOS the setting is a single linker skip for the plugin. The repository layout matches what you would expect for a .NET MAUI plugin: src/ for the source, nuget/ for the packaging, docs/ for the documentation site, art/ for images, and an .editorconfig and .gitattributes for the usual repository hygiene. The MIT licence is in the LICENSE file at the root.
What the version 7 and 8 rewrites changed
The two most recent major rewrites are recorded in the README, and they explain why a major version bump is sometimes a code change on your side. Version 8 was a re-write and re-implementation of the APIs, built against .NET 8 and the latest Android Billing v6.2.1 at the time. Version 7 was the harder break: on Android you must compile and target against Android 12 or higher, or Android 13 if you are on v7.1, it moved to Android Billing v6, and there were major changes to Android product details and subscriptions, with a link to Google's migration guide. One rename in that release is the kind of detail that breaks a build, since AcknowledgePurchaseAsync became FinalizePurchaseAsync. There are also Extras for all products, giving back more information per platform, and the README says migration guides exist for the earlier versions as well.
Editorial conclusion
InAppBillingPlugin suits a .NET MAUI or Windows team that needs a cross platform in-app purchase API today and is prepared to own the maintenance, because the author has said in plain terms that he does not plan to continue supporting the library and invites forks. It does not suit a team that wants a store SDK to keep pace with platform changes on its own, since Google Play Billing and StoreKit both evolve faster than a community wrapper can. Before adopting, read the version table and pick the major version matching your target framework, check that the Android Billing Library version in your AndroidManifest.xml matches the NuGet dependency, and note that the last push to the repository was on 2026-05-03.
Frequently asked questions
Which platforms does the InAppBillingPlugin support?
The platform table gives iOS for .NET at 10 or newer, macCatalyst for .NET at any version, Android for .NET at 21 or newer, Windows App SDK with WinUI 3 at 10 or newer, and .NET MAUI at any version. Subscriptions are supported on iOS, Android and Mac, while Windows, UWP and WinUI 3 do not support subscriptions.
How do I handle a pending purchase in the InAppBillingPlugin?
If PurchaseAsync returns PurchaseState.PaymentPending, store the order details locally and tell the user they will get access when the payment completes. On the next app start or page visit, call GetPurchasesAsync to look for a matching purchase, show the no-access message if it is still pending, and call ConsumePurchaseAsync or AcknowledgePurchaseAsync if it has become Purchased.
What is the difference between ConsumePurchaseAsync and AcknowledgePurchaseAsync?
On Android a purchase must be acknowledged or it is refunded. For consumables, ConsumePurchaseAsync both fulfils the acknowledgement requirement and signals that entitlement was granted, and it makes the product available for purchase again. For a non-consumable you need to call AcknowledgePurchaseAsync instead.
Is the InAppBillingPlugin still maintained?
The README states that the author does not plan on continuing support for the library, giving his reasons as the complexity of in-app purchase APIs, StoreKit being deprecated for StoreKit2 and constant changes in the Android Billing Library. He encourages forking the library or pulling the source into your project, and the last push to the repository was on 2026-05-03.
Where do I get the InAppBillingPlugin?
It is published on NuGet as Plugin.InAppBilling, and the documentation is hosted at jamesmontemagno.github.io/InAppBillingPlugin. The plugin also requires extra linker settings on Android and iOS, including ProGuard keep rules for the Google Billing client packages and a linker skip for the plugin on iOS.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jamesmontemagno-inappbillingplugin)Was this useful?