In-App Billing Plugin for .NET MAUI and Windows

repository·master·Indexed 20 days ago

https://github.com/jamesmontemagno/inappbillingplugin

A .NET MAUI and Windows plugin for implementing in-app purchases across iOS, Android, and Mac. It provides APIs to query product information via GetProductInfoAsync, manage purchase flows for consumables, non-consumables, and subscriptions, and restore previous purchases using GetPurchasesAsync. The library supports dependency injection via the IInAppBilling interface and includes platform-specific handling for Google Play Billing and iOS StoreKit.

Tokens
12K
Snippets
26
Records
37
Agent score
73%

What's inside Plugin.InAppBilling

  1. Explore In-App Billing Plugin documentation

    master

    The In-App Billing Plugin documentation provides comprehensive guides for implementing in-app purchases in .NET MAUI and Windows applications. Key topics include:

    • Setup & Basics: Getting Started and Architecture.
    • Product Management: Retrieving product details.
    • Purchase Flows: Implementing purchases for Consumables, Non-Consumables, and Subscriptions.
    • Lifecycle & Maintenance: Checking and restoring previous purchases, and handling pending transactions.
    • Security & Reliability: Securing purchases and handling exceptions.
    • Development: Testing and troubleshooting procedures.
  2. Important version compatibility and support notice

    master

    Support Status

    As of April 2025, the maintainer is no longer providing active support for this library due to the complexity and frequent updates required by StoreKit2 (iOS) and the Android Billing Library. Users are encouraged to fork the library or pull the source code directly into their projects.

    Version Compatibility Matrix

    Version.NET TargetAndroid Billing Version
    v10.NET 10+Android Billing v8
    v9.0+.NET 9+Android Billing v7
    v8.0+.NET 8+(Updated APIs)
    v7.0+(Requires Android 12+)Android Billing v6
    v5.0+(Significant API changes)Android Billing v4
    v4.0(Requires Android 10+)(Uses Xamarin.Essentials)
  3. How to handle consumable in-app purchases

    master

    Consumables are items that can be purchased multiple times (e.g., virtual currency). Because they are reusable, you are responsible for managing their lifecycle: verifying the purchase, providing the item to the user, and consuming the purchase to allow for subsequent buys.

    Platform Differences

    • Apple (iOS): Referred to as Consumable. You must manually consume the purchase to finish the transaction (starting in versions 5.x and 6.x, auto-consumption is not the default behavior).
    • Android: Referred to as Managed Product. You must consume the purchase before the user can purchase the same item again; this also acts as an acknowledgment of the transaction.
    • Microsoft (Windows): Referred to as Developer-managed consumable. You must consume the purchase before the user can purchase it again.

    Lifecycle Requirements

    1. Always call ConnectAsync() before initiating a purchase.
    2. Always call DisconnectAsync() when finished with billing operations.
    3. For all platforms, use PurchaseAsync to initiate the transaction.
    4. For all platforms (with specific configuration for iOS), use ConsumePurchaseAsync to finalize the transaction and enable repeat purchases.
    // High-level flow
    await billing.ConnectAsync();
    var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchaseConsumable);
    if (purchase?.State == PurchaseState.Purchased)
    {
        // Process item delivery
        await billing.ConsumePurchaseAsync(purchase.ProductId, purchase.TransactionIdentifier);
    }
    await billing.DisconnectAsync();
  4. Implement Server Side Validation

    master

    The plugin provides the VerifyPurchase method, which contains the necessary data to facilitate server-side verification. While the plugin provides the data, the implementation of the actual verification logic (communicating with Apple or Google servers) must be handled by your own backend implementation.

    For architectural patterns on how to implement this, it is recommended to use serverless functions (like Azure Functions) to act as the intermediary between your app and the platform providers.

  5. How to use Dependency Injection with the In-App Billing Plugin

    master

    While the plugin provides a static singleton CrossInAppBilling.Current for quick access, it is built entirely on an interface. For applications using an Inversion of Control (IoC) container or ViewModel pattern, you should register the platform-specific implementation in your platform projects (iOS, Android, Windows, etc.).

    To do this, instantiate the platform-specific implementation (e.g., CrossInAppBillingImplementation) within your platform-specific startup code and register it against the plugin's interface. This allows you to inject the interface into your ViewModels or services rather than relying on the static Current property.

    // Example of injecting the plugin interface into a ViewModel
    public class MyViewModel
    {
        private readonly IInAppBillingPlugin plugin;
    
        public MyViewModel(IInAppBillingPlugin plugin)
        {
            this.plugin = plugin;
        }
    }
  6. Handle Android billing requirements for Version 4.0+

    master

    If you are using Version 4.0 or higher, you must adhere to the following Android-specific requirements:

    1. Target SDK: You must compile and target against Android 10 or higher.
    2. Pending Transactions: You must explicitly handle pending transactions.
    3. Finalizing Purchases: You must call FinalizePurchaseAsync when a purchase is completed to ensure the transaction is properly closed.
    4. Setup: The library now uses Xamarin.Essentials, which requires specific setup as described in the official documentation.
  7. Migrate to Version 5.0+ API changes

    master

    Version 5.0 introduced significant breaking changes to the API surface:

    1. Verification Removal: IInAppBillingVerifyPurchase has been removed from all methods. The library now returns all necessary data so that you can handle purchase verification yourself.
    2. iOS Receipt Data: ReceiptURL data is now accessible via the ReceiptData property.
    3. Method Renaming: AcknowledgePurchaseAsync has been renamed to FinalizePurchaseAsync.
    4. Android Billing: The library moved to Android Billing version 4.

    It is highly recommended to review the full documentation for all changes at: https://github.com/jamesmontemagno/InAppBillingPlugin

  8. Disposing of the In-App Billing Plugin

    master

    The plugin implements IDisposable to ensure that platform-specific events (such as the SKPaymentQueue on iOS) are properly unregistered.

    Important: While you can use a using statement on the Current instance, the recommended pattern is to call CrossInAppBilling.Dispose() on the static class itself. Calling CrossInAppBilling.Dispose() handles the disposal of the Current instance. The next time you access CrossInAppBilling.Current, a new instance will be created.

    Avoid disposing of the instance manually if you still need to listen to events; only dispose when you are finished with the billing lifecycle.

    public async Task<bool> MakePurchase()
    {
        if(!CrossInAppBilling.IsSupported)
            return false;
    
        var billing = CrossInAppBilling.Current;
        
        try
        {
            var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
            if(!connected)
                return false;
            
            // make additional billing calls
        }
        finally
        {
            await billing.DisconnectAsync();
        }
    
        // This is the recommended way to dispose of the plugin and its current instance
        CrossInAppBilling.Dispose();
    }
  9. Handle In-App Billing exceptions with InAppBillingPurchaseException

    master

    In-app billing operations are complex and prone to failure. This library abstracts various billing errors into a single InAppBillingPurchaseException. This exception can be thrown by most API calls, except for DisconnectAsync, which is designed not to throw.

    When catching an InAppBillingPurchaseException, you can access:

    1. A message returned from the billing server.
    2. A PurchaseError enum value to programmatically determine the cause of the failure and show appropriate feedback to the user.
    var billing = CrossInAppBilling.Current;
    try
    {
        var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
        if (!connected) return;
    
        var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase);
        if(purchase == null) { /* did not purchase */ }
        else { /* purchased! */ }
    }
    catch (InAppBillingPurchaseException purchaseEx)
    {
        // Use purchaseEx.PurchaseError to handle specific error cases
        switch (purchaseEx.PurchaseError)
        {
            case PurchaseError.AppStoreUnavailable:
                // Handle app store unavailable
                break;
            // ... other cases
        }
    }
    catch (Exception ex)
    {
        // Handle non-billing related exceptions
    }
    finally
    {
        await billing.DisconnectAsync();
    }
  10. Configure iOS 'Promoted Items' in .NET MAUI

    master

    If you are enabling "promoted items" (allowing users to purchase directly from the App Store), you must handle the OnShouldAddStorePayment event.

    In .NET MAUI, you can implement this within your MauiProgram.cs using ConfigureLifecycleEvents to hook into the iOS FinishedLaunching event.

    using Microsoft.Extensions.Logging;
    using Microsoft.Maui.LifecycleEvents;
    #if IOS
    using StoreKit;
    #endif
    
    namespace MauiApp4;
    
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureLifecycleEvents(AppLifecycle =>
                 {
    #if IOS
                     AppLifecycle.AddiOS(ios =>
                         ios.FinishedLaunching((del, b) =>
                        {
                            Plugin.InAppBilling.InAppBillingImplementation.OnShouldAddStorePayment = OnShouldAddStorePayment;
                            var current = Plugin.InAppBilling.CrossInAppBilling.Current;
                            return true;
                        }));
    #endif
                 });
    
    #if IOS
            bool OnShouldAddStorePayment(SKPaymentQueue queue, SKPayment payment, SKProduct product)
            {
                //Process and check purchases
                return true;
            }
    #endif
    
            return builder.Build();
        }
    }
  11. Install Plugin.InAppBilling via NuGet

    master

    The In-App Billing Plugin for .NET MAUI and Windows can be installed via NuGet. This plugin allows you to query item information, purchase items, restore items, and manage subscriptions on iOS, Android, and Mac. Note that Windows/UWP/WinUI 3 does not support subscriptions at this time.

    NuGet: [Plugin.InAppBilling](https://www.nuget.org/packages/Plugin.InAppBilling)