### Example: Purchase and Consume Item Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/PurchaseConsumable.md This example demonstrates the complete process of purchasing a consumable item, checking its state, and then consuming it. It includes error handling and ensures disconnection from the billing service. ```csharp public async Task PurchaseItem(string productId) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) { //we are offline or can't connect, don't try to purchase return false; } //check purchases var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchaseConsumable); //possibility that a null came through. if(purchase == null) { //did not purchase } else if(purchase.State == PurchaseState.Purchased) { // purchased, we can now consume the item or do it later // here you may want to call your backend or process something in your app. //only required on Android & Windows var wasConsumed = await CrossInAppBilling.Current.ConsumePurchaseAsync(purchase.ProductId, purchase.TransactionIdentifier); if(wasConsumed) { //Consumed!! } } } catch (InAppBillingPurchaseException purchaseEx) { //Billing Exception handle this based on the type Debug.WriteLine("Error: " + purchaseEx); } catch (Exception ex) { //Something else has gone wrong, log it Debug.WriteLine("Issue connecting: " + ex); } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Purchase Subscription Example Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/PurchaseSubscription.md Example of how to purchase a subscription using the In-App Billing plugin. It includes connecting to the service, performing the purchase, and finalizing it on Android. Error handling for connection and purchase exceptions is also demonstrated. ```csharp public async Task PurchaseItem(string productId, string payload) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) { //we are offline or can't connect, don't try to purchase return false; } //check purchases var purchase = await billing.PurchaseAsync(productId, ItemType.Subscription); //possibility that a null came through. if(purchase == null) { //did not purchase } else if(purchase.State == PurchaseState.Purchased) { //only needed on android unless you turn off auto finalize var ack = await CrossInAppBilling.Current.FinalizePurchaseAsync([purchase.TransactionIdentifier]); // Handle if acknowledge was successful or not } } catch (InAppBillingPurchaseException purchaseEx) { //Billing Exception handle this based on the type Debug.WriteLine("Error: " + purchaseEx); } catch (Exception ex) { //Something else has gone wrong, log it Debug.WriteLine("Issue connecting: " + ex); } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Setup iOS Promoted In-App Purchase Handler (.NET MAUI) Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Configures the `OnShouldAddStorePayment` handler in `MauiProgram.cs` for .NET MAUI applications to manage purchases initiated from the App Store. This setup uses `ConfigureLifecycleEvents` for iOS integration. ```csharp // Alternative: .NET MAUI setup in MauiProgram.cs public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() .ConfigureLifecycleEvents(lifecycle => { #if IOS lifecycle.AddiOS(ios => ios.FinishedLaunching((app, options) => { Plugin.InAppBilling.InAppBillingImplementation.OnShouldAddStorePayment = (queue, payment, product) => { Console.WriteLine($"Promoted purchase: {product.ProductIdentifier}"); return true; }; var _ = Plugin.InAppBilling.CrossInAppBilling.Current; return true; })); #endif }); return builder.Build(); } ``` -------------------------------- ### .NET MAUI AppDelegate In-App Purchase Setup (iOS) Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GettingStarted.md Integrate in-app purchase handling within a .NET MAUI application's AppDelegate for iOS. This setup ensures the plugin is initialized and the purchase callback is registered during application launch. ```csharp 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() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }) .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 #if DEBUG builder.Logging.AddDebug(); #endif return builder.Build(); } } ``` -------------------------------- ### Example Usage of GetProductInfoAsync Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GetProductDetails.md Demonstrates connecting to the billing service, retrieving product information for specified IDs, and iterating through the results. Ensure to handle connection errors and disconnect afterwards. ```csharp var billing = CrossInAppBilling.Current; try { var productIds = new string []{"mysku","mysku2"}; //You must connect var connected = await billing.ConnectAsync(); if (!connected) { //Couldn't connect return; } //check purchases var items = await billing.GetProductInfoAsync(ItemType.InAppPurchase, productIds); foreach(var item in items) { //item info here. } } catch(InAppBillingPurchaseException pEx) { //Handle IAP Billing Exception } catch (Exception ex) { //Something has gone wrong } finally { await billing.DisconnectAsync(); } ``` -------------------------------- ### Get Product Information Async Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GetProductDetails.md Retrieves product details for specified IDs. Requires connecting to the billing service first and specifying the item type (InAppPurchase or Subscription). ```csharp Task> GetProductInfoAsync(ItemType itemType, params string[] productIds); ``` -------------------------------- ### Check if Item Was Purchased Example Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/CheckAndRestorePurchases.md Example of how to check if a specific product has been purchased by the user. This method connects to the billing service, retrieves purchases, and checks if the provided productId exists in the list of purchases. It handles potential connection and purchase exceptions. ```csharp public async Task WasItemPurchased(string productId) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) { //Couldn't connect return false; } //check purchases var idsToNotFinish = new List(new [] {"myconsumable"}); var purchases = await billing.GetPurchasesAsync(ItemType.InAppPurchase, idsToNotFinish); //check for null just in case if(purchases?.Any(p => p.ProductId == productId) ?? false) { //Purchase restored // if on Android may be good to check if these purchases need to be acknowledge return true; } else { //no purchases found return false; } } catch (InAppBillingPurchaseException purchaseEx) { //Billing Exception handle this based on the type Debug.WriteLine("Error: " + purchaseEx); } catch (Exception ex) { //Something has gone wrong } finally { await billing.DisconnectAsync(); } return false; } ``` -------------------------------- ### iOS AppDelegate In-App Purchase Setup Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GettingStarted.md Configure the AppDelegate to handle store payment requests for promoted in-app purchases. This code initializes the plugin and sets up the callback for purchase events. ```csharp Plugin.InAppBilling.InAppBillingImplementation.OnShouldAddStorePayment = OnShouldAddStorePayment; var current = Plugin.InAppBilling.CrossInAppBilling.Current; //initializes ``` ```csharp bool OnShouldAddStorePayment(SKPaymentQueue queue, SKPayment payment, SKProduct product) { //Process and check purchases return true; } ``` -------------------------------- ### Handle InAppBillingPurchaseException Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/HandlingExceptions.md Example of how to connect to the billing service, attempt a purchase, and handle potential InAppBillingPurchaseExceptions. Ensure to disconnect from the service in the finally block. ```csharp var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(ItemType.InAppPurchase); if (!connected) { //we are offline or can't connect, don't try to purchase return; } //check purchases var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase); //possibility that a null came through. if(purchase == null) { //did not purchase } else { //purchased! } } catch (InAppBillingPurchaseException purchaseEx) { var message = string.Empty; switch (purchaseEx.PurchaseError) { case PurchaseError.AppStoreUnavailable: message = "Currently the app store seems to be unavailable. Try again later."; break; case PurchaseError.BillingUnavailable: message = "Billing seems to be unavailable, please try again later."; break; case PurchaseError.PaymentInvalid: message = "Payment seems to be invalid, please try again."; break; case PurchaseError.PaymentNotAllowed: message = "Payment does not seem to be enabled/allowed, please try again."; break; } //Decide if it is an error we care about if (string.IsNullOrWhiteSpace(message)) return; //Display message to user } catch (Exception ex) { //Something else has gone wrong, log it Debug.WriteLine("Issue connecting: " + ex); } finally { await billing.DisconnectAsync(); } ``` -------------------------------- ### Get Purchases Async Method Signature Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/CheckAndRestorePurchases.md Signature for retrieving all current purchases for a specified item type. Use this to restore previous purchases. ```csharp /// /// Get all current purchases for a specified product type. /// /// Type of product /// Cancel the request /// The current purchases Task> GetPurchasesAsync(ItemType itemType, CancellationToken cancellationToken = default); ``` -------------------------------- ### Setup iOS Promoted In-App Purchase Handler (Xamarin.iOS) Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Configures the `OnShouldAddStorePayment` handler in `AppDelegate.cs` for Xamarin.iOS to manage purchases initiated from the App Store. This allows you to intercept and control the purchase flow. ```csharp // In AppDelegate.cs (Xamarin.iOS) or MauiProgram.cs (.NET MAUI) public partial class AppDelegate : MauiUIApplicationDelegate { public override bool FinishedLaunching(UIApplication app, NSDictionary options) { // Setup handler for App Store initiated purchases Plugin.InAppBilling.InAppBillingImplementation.OnShouldAddStorePayment = OnShouldAddStorePayment; // Initialize the billing service var current = Plugin.InAppBilling.CrossInAppBilling.Current; return base.FinishedLaunching(app, options); } private bool OnShouldAddStorePayment(SKPaymentQueue queue, SKPayment payment, SKProduct product) { // Return true to allow the purchase to proceed // Return false to defer the purchase (handle it yourself later) Console.WriteLine($"Store-initiated purchase: {product.ProductIdentifier}"); // You might want to show a confirmation dialog first return true; } } ``` -------------------------------- ### Get Product Information Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Retrieves product details for items configured in app stores. Products must be live and active. Handles both in-app purchases and subscriptions, and includes platform-specific extras for Apple, Android, and Windows. ```csharp public async Task> GetProductsAsync() { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) return null; // Query in-app purchase products var productIds = new string[] { "premium_upgrade", "remove_ads", "100_coins" }; var products = await billing.GetProductInfoAsync(ItemType.InAppPurchase, productIds); foreach (var product in products) { Console.WriteLine($"Name: {product.Name}"); Console.WriteLine($"Description: {product.Description}"); Console.WriteLine($"Product ID: {product.ProductId}"); Console.WriteLine($"Price: {product.LocalizedPrice}"); Console.WriteLine($"Currency: {product.CurrencyCode}"); Console.WriteLine($"Micros Price: {product.MicrosPrice}"); // Platform-specific extras if (product.AppleExtras != null) { Console.WriteLine($"Family Shareable: {product.AppleExtras.IsFamilyShareable}"); Console.WriteLine($"Subscription Group: {product.AppleExtras.SubscriptionGroupId}"); } if (product.AndroidExtras?.SubscriptionOfferDetails != null) { foreach (var offer in product.AndroidExtras.SubscriptionOfferDetails) { Console.WriteLine($"Offer Token: {offer}"); } } if (product.WindowsExtras != null) { Console.WriteLine($"On Sale: {product.WindowsExtras.IsOnSale}"); Console.WriteLine($"Sale End: {product.WindowsExtras.SaleEndDate}"); } } // Query subscription products separately var subscriptionIds = new string[] { "monthly_premium", "yearly_premium" }; var subscriptions = await billing.GetProductInfoAsync(ItemType.Subscription, subscriptionIds); return products.Concat(subscriptions).ToList(); } catch (InAppBillingPurchaseException ex) { Console.WriteLine($"Billing error: {ex.PurchaseError}"); return null; } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Get Product Details API Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GetProductDetails.md Retrieves detailed information about specified in-app purchase or subscription products. Note that products must be live in the store to be fetched. Separate calls are required for different item types (InAppPurchase, Subscription). ```APIDOC ## Get Product Details ### Description Retrieves product information for specified product IDs. This method requires an `ItemType` and a list of `productIds`. Products must be live in the app store to be fetched. ### Method GET (Implied, as it's a retrieval operation) ### Endpoint `/api/products` (Hypothetical endpoint, actual implementation may vary) ### Parameters #### Query Parameters - **itemType** (ItemType) - Required - Specifies the type of product offering (e.g., `ItemType.InAppPurchase`, `ItemType.Subscription`). - **productIds** (string[]) - Required - An array of product IDs or SKUs for which to retrieve information. ### Request Example (No explicit request body or complex request structure is defined for this retrieval operation. Parameters are typically passed as query parameters or method arguments in the SDK.) ### Response #### Success Response (200) - **InAppBillingProduct** (object[]) - A list of `InAppBillingProduct` objects containing details for each requested product. **InAppBillingProduct Object Structure:** - **Name** (string) - The name of the product. - **Description** (string) - The description of the product. - **ProductId** (string) - The product ID or SKU. - **LocalizedPrice** (string) - The localized price of the product (excluding tax). - **CurrencyCode** (string) - The ISO 4217 currency code for the price (e.g., "USD", "GBP"). - **MicrosPrice** (Int64) - The price in micro-units (1,000,000 micro-units = 1 currency unit). - **AppleExtras** (object) - Extra information specific to Apple platforms (optional). - **AndroidExtras** (object) - Extra information specific to Android platforms (optional). - **WindowsExtras** (object) - Extra information specific to Windows platforms (optional). #### Response Example ```json [ { "Name": "Example Product", "Description": "This is a sample product description.", "ProductId": "mysku", "LocalizedPrice": "$1.99", "CurrencyCode": "USD", "MicrosPrice": 1990000, "AppleExtras": null, "AndroidExtras": {}, "WindowsExtras": null } ] ``` ### Error Handling - **InAppBillingPurchaseException**: Catches specific billing-related exceptions. - **Exception**: Catches general exceptions during the process. ``` -------------------------------- ### Purchase Non-Consumable Item Example Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/PurchaseNonConsumable.md Connects to the billing service, purchases a non-consumable item, and finalizes the transaction on Android. Ensure to handle potential exceptions and disconnect from the service in the finally block. ```csharp public async Task PurchaseItem(string productId) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) { //we are offline or can't connect, don't try to purchase return false; } //check purchases var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase); //possibility that a null came through. if(purchase == null) { //did not purchase } else if(purchase.State == PurchaseState.Purchased) { // only need to finalize if on Android unless you turn off auto finalize on iOS var ack = await CrossInAppBilling.Current.FinalizePurchaseAsync([purchase.TransactionIdentifier]); // Handle if acknowledge was successful or not } } catch (InAppBillingPurchaseException purchaseEx) { //Billing Exception handle this based on the type Debug.WriteLine("Error: " + purchaseEx); } catch (Exception ex) { //Something else has gone wrong, log it Debug.WriteLine("Issue connecting: " + ex); } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Get iOS Receipt Data for Validation Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Retrieves the Base64-encoded receipt data for iOS, which can then be sent to your server for validation. Ensure you disconnect the billing service after use. ```csharp public async Task GetReceiptForValidation() { var billing = CrossInAppBilling.Current; try { await billing.ConnectAsync(); // Get the Base64-encoded receipt data (iOS only) string receiptData = billing.ReceiptData; if (!string.IsNullOrEmpty(receiptData)) { Console.WriteLine($"Receipt length: {receiptData.Length}"); // Send receiptData to your server for validation return receiptData; } return null; } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### ViewModel with Plugin Dependency Injection Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/Architecture.md Demonstrates how to inject the plugin interface into a ViewModel for dependency injection. This approach facilitates unit testing by allowing mock implementations. ```csharp public MyViewModel() { readonly IPLUGIN plugin; public MyViewModel(IPLUGIN plugin) { this.plugin = plugin; } } ``` -------------------------------- ### Purchase Flow with Using Statement Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/Architecture.md Illustrates a purchase flow using a 'using' statement to manage the plugin's lifecycle. Ensure to disconnect asynchronously after operations and dispose of the plugin when no longer needed. ```csharp public async Task MakePurchase() { if(!CrossInAppBilling.IsSupported) return false; using(var billing = CrossInAppBilling.Current) { try { var connected = await billing.ConnectAsync(ItemType.InAppPurchase); if(!connected) return false; //make additional billing calls } finally { await billing.DisconnectAsync(); } } CrossInAppBilling.Dispose(); } ``` -------------------------------- ### Load Raw Asset File in .NET MAUI Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/src/InAppBillingTests/InAppBillingMauiTest/Resources/Raw/AboutAssets.txt Use FileSystem.OpenAppPackageFileAsync to access raw assets included with the MauiAsset build action. Ensure the file name matches the one specified in the project file. ```csharp async Task LoadMauiAsset() { using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); using var reader = new StreamReader(stream); var contents = reader.ReadToEnd(); } ``` -------------------------------- ### Include Raw Asset in .NET MAUI Project Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/src/InAppBillingTests/InAppBillingMauiTest/Resources/Raw/AboutAssets.txt Add this to your .csproj file to include a raw asset file that will be deployed with your application. ```xml ``` -------------------------------- ### Purchase Flow without Using Statement Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/Architecture.md Shows a recommended purchase flow that avoids a 'using' statement for the plugin. Explicitly call 'Dispose' on the static class to manage resource cleanup and ensure proper event unregistration. ```csharp public async Task 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 does all the disposing you need CrossInAppBilling.Dispose(); } ``` -------------------------------- ### Android Linker Configuration Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/README.md When using 'Link All' on Android, ensure these assemblies are included. ```csharp Plugin.InAppBilling;Xamarin.Android.Google.BillingClient ``` -------------------------------- ### Connect to Billing Service Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Establishes a connection to the platform's billing service. Always wrap billing operations in try-finally blocks to ensure proper disconnection. Returns false if the device is offline or the service is unavailable. ```csharp public async Task PerformBillingOperation() { var billing = CrossInAppBilling.Current; try { // Connect to the billing service (required before any other calls) var connected = await billing.ConnectAsync(); if (!connected) { // Device is offline or billing service unavailable return false; } // Check if connected if (billing.IsConnected) { // Perform billing operations here } // Check if payments are allowed on this device if (!billing.CanMakePayments) { // User cannot make payments (parental controls, etc.) return false; } return true; } finally { // Always disconnect when finished await billing.DisconnectAsync(); } } ``` -------------------------------- ### iOS Linker Skip Configuration Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/README.md For iOS, if 'Link All' is enabled, you may need to skip the Plugin.InAppBilling assembly. ```bash --linkskip=Plugin.InAppBilling ``` -------------------------------- ### Xamarin.Android Initialization Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GettingStarted.md Initialize Xamarin.Essentials in your Xamarin.Android application's OnCreate method. This is required for version 4 of the plugin and is typically set up by default in new projects. ```csharp protected override void OnCreate(Bundle savedInstanceState) { //... base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle //... ``` -------------------------------- ### PurchaseAsync Method Signature Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/PurchaseNonConsumable.md This method is used to purchase a specific product or subscription. It requires the product ID, item type, and optionally obfuscated account and profile IDs for enhanced security and tracking. ```csharp Task PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null, CancellationToken cancellationToken = default); ``` -------------------------------- ### Android Signing Configuration in Visual Studio Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/TestingAndTroubleshooting.md Configure Android signing properties in Visual Studio for APK signing, essential for testing and deployment. ```xml True KeystoreLocation PASS ALIAS PASS ``` -------------------------------- ### Configure Testing Mode for In-App Billing Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Enable testing mode for development and debugging, particularly useful for Windows/UWP. This uses CurrentAppSimulator and can optionally ignore invalid products. ```csharp public async Task TestPurchaseFlow() { var billing = CrossInAppBilling.Current; // Enable testing mode (primarily for UWP/Windows) // Uses CurrentAppSimulator instead of CurrentApp billing.InTestingMode = true; // Optionally ignore invalid products during development billing.IgnoreInvalidProducts = true; try { var connected = await billing.ConnectAsync(); if (!connected) return; // Test product IDs for Android // Use these for testing without actual charges: // - "android.test.purchased" - Always succeeds // - "android.test.canceled" - Always cancels // - "android.test.item_unavailable" - Item unavailable var testProducts = await billing.GetProductInfoAsync( ItemType.InAppPurchase, new[] { "android.test.purchased" }) ; foreach (var product in testProducts) { Console.WriteLine($"Test product: {product.Name} - {product.LocalizedPrice}"); } } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### InAppBillingProduct Class Definition Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GetProductDetails.md Defines the structure for product information, including localized price, currency, and platform-specific extras. ```csharp public class InAppBillingProduct { /// /// Name of the product /// public string Name { get; set; } /// /// Description of the product /// public string Description { get; set; } /// /// Product ID or sku /// public string ProductId { get; set; } /// /// Localized Price (not including tax) /// public string LocalizedPrice { get; set; } /// /// ISO 4217 currency code for price. For example, if price is specified in British pounds sterling is "GBP". /// public string CurrencyCode { get; set; } /// /// Price in micro-units, where 1,000,000 micro-units equal one unit of the /// currency. For example, if price is "€7.99", price_amount_micros is "7990000". /// This value represents the localized, rounded price for a particular currency. /// public Int64 MicrosPrice { get; set; } /// /// Extra information for apple platforms /// public InAppBillingProductAppleExtras AppleExtras { get; set; } = null; /// /// Extra information for Android platforms /// public InAppBillingProductAndroidExtras AndroidExtras { get; set; } = null; /// /// Extra information for Windows platforms /// public InAppBillingProductWindowsExtras WindowsExtras { get; set; } = null; } ``` -------------------------------- ### Purchase a Subscription Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Use this method to purchase a subscription. Ensure you finalize the purchase on Android. Connect to the billing service before calling this method and disconnect in a finally block. ```csharp public async Task PurchaseSubscription(string productId, string offerToken = null) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) return false; // Purchase the subscription // On Android, you can pass a subOfferToken for specific subscription offers var purchase = await billing.PurchaseAsync( productId, ItemType.Subscription, obfuscatedAccountId: GetUserAccountId(), // Optional: link to user account obfuscatedProfileId: null, subOfferToken: offerToken ); if (purchase == null) return false; if (purchase.State == PurchaseState.Purchased) { Console.WriteLine($"Subscription purchased: {purchase.ProductId}"); Console.WriteLine($"Auto-renewing: {purchase.AutoRenewing}"); Console.WriteLine($"Purchase Token: {purchase.PurchaseToken}"); // Finalize the subscription purchase (required on Android) var results = await billing.FinalizePurchaseAsync(new[] { purchase.TransactionIdentifier }); // Enable subscription features for the user await EnableSubscriptionFeatures(purchase.ProductId); return true; } return false; } catch (InAppBillingPurchaseException ex) { HandlePurchaseException(ex); return false; } finally { await billing.DisconnectAsync(); } } private string GetUserAccountId() { // Return an obfuscated user account identifier return "hashed_user_id_12345"; } private async Task EnableSubscriptionFeatures(string productId) { Console.WriteLine($"Enabled subscription features for {productId}"); } ``` -------------------------------- ### Enable UWP In-App Billing Testing Mode Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/TestingAndTroubleshooting.md Set the InTestingMode property to true to enable testing of UWP in-app purchases using CurrentAppSimulator. ```csharp CrossInAppBilling.Current.InTestingMode = true; ``` -------------------------------- ### Check InAppBilling Platform Support Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GettingStarted.md Verifies if the InAppBilling API is supported on the current platform before attempting to connect or make calls. Useful for unit testing and handling unsupported environments. ```csharp public async Task MakePurchase() { if(!CrossInAppBilling.IsSupported) return false; try { var billing = CrossInAppBilling.Current; var connected = await billing.ConnectAsync(); if(!connected) return false; //make additional billing calls } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Handle In-App Purchase Exceptions Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Use this method to gracefully handle various errors that can occur during purchase operations, providing user-friendly messages based on the specific `PurchaseError`. ```csharp private void HandlePurchaseException(InAppBillingPurchaseException ex) { string userMessage; switch (ex.PurchaseError) { case PurchaseError.UserCancelled: userMessage = "Purchase was cancelled."; break; case PurchaseError.AppStoreUnavailable: userMessage = "The app store is currently unavailable. Please try again later."; break; case PurchaseError.BillingUnavailable: userMessage = "Billing is currently unavailable. Please try again later."; break; case PurchaseError.PaymentNotAllowed: userMessage = "Payments are not allowed on this device."; break; case PurchaseError.PaymentInvalid: userMessage = "The payment was invalid. Please try again."; break; case PurchaseError.ItemUnavailable: userMessage = "This item is not available for purchase."; break; case PurchaseError.InvalidProduct: userMessage = "The requested product is invalid."; break; case PurchaseError.ProductRequestFailed: userMessage = "Failed to load product information."; break; case PurchaseError.RestoreFailed: userMessage = "Failed to restore purchases."; break; case PurchaseError.ServiceUnavailable: userMessage = "Network connection unavailable. Please check your connection."; break; case PurchaseError.DeveloperError: userMessage = "A configuration error occurred."; Console.WriteLine($"Developer error: {ex.Message}"); break; default: userMessage = "An error occurred during purchase."; break; } Console.WriteLine($"Purchase error: {userMessage}"); // Display userMessage to the user } ``` -------------------------------- ### Restore Purchases and Subscriptions Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Use this method to retrieve all previously purchased non-consumable items and active subscriptions for the user. It connects to the billing service, fetches purchases, and finalizes any unacknowledged items on Android. Ensure the billing service is connected before calling. ```csharp public async Task> RestorePurchasesAsync() { var billing = CrossInAppBilling.Current; var restoredPurchases = new List(); try { var connected = await billing.ConnectAsync(); if (!connected) return restoredPurchases; // Get all non-consumable purchases var purchases = await billing.GetPurchasesAsync(ItemType.InAppPurchase); if (purchases != null) { foreach (var purchase in purchases) { Console.WriteLine($ ``` -------------------------------- ### Change Subscription Tier (Android) Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Use this method to upgrade or downgrade a user's subscription on Android. Specify the new product ID, the current purchase token, and the desired proration mode. ```csharp public async Task ChangeSubscription( string newProductId, string currentPurchaseToken, SubscriptionProrationMode prorationMode) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) return false; // Upgrade or downgrade the subscription // Proration modes: // - ImmediateWithTimeProration (1): Credit/charge prorated, immediate change // - ImmediateAndChargeProratedPrice (2): Charge prorated, immediate change // - ImmediateWithoutProration (3): No credit/charge until renewal // - Deferred (4): Change takes effect at next renewal // - ImmediateAndChargeFullPrice (5): Charge full price immediately var purchase = await billing.UpgradePurchasedSubscriptionAsync( newProductId, currentPurchaseToken, prorationMode ); if (purchase?.State == PurchaseState.Purchased) { await billing.FinalizePurchaseAsync(new[] { purchase.TransactionIdentifier }); Console.WriteLine($"Subscription changed to {newProductId}"); return true; } return false; } catch (InAppBillingPurchaseException ex) { HandlePurchaseException(ex); return false; } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Purchase Consumable Item Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Purchases a consumable item, grants it to the user, and then consumes the purchase. This allows the user to purchase the item again. The purchase must be consumed after the user has "used" the item. ```csharp public async Task PurchaseConsumable(string productId) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) return false; // Purchase the consumable item var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchaseConsumable); if (purchase == null) return false; if (purchase.State == PurchaseState.Purchased) { // Grant the user the consumable (e.g., add coins to their balance) await GrantConsumableToUser(purchase.ProductId, purchase.Quantity); // Consume the purchase so it can be bought again // On Android, this also acknowledges the transaction // On iOS, this finalizes the transaction var wasConsumed = await billing.ConsumePurchaseAsync( purchase.ProductId, purchase.TransactionIdentifier ); if (wasConsumed) { Console.WriteLine($"Consumed {purchase.ProductId} successfully"); return true; } else { Console.WriteLine("Failed to consume purchase"); return false; } } return false; } catch (InAppBillingPurchaseException ex) { HandlePurchaseException(ex); return false; } finally { await billing.DisconnectAsync(); } } private async Task GrantConsumableToUser(string productId, int quantity) { // Your logic to grant the consumable to the user Console.WriteLine($"Granted {quantity}x {productId} to user"); } ``` -------------------------------- ### Check for Pending Transactions (Android) Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt This method checks for and handles pending purchases on Android, such as those paid for with cash. It subscribes to purchase update events to process completed payments. Keep the connection open to receive these updates. ```csharp public async Task CheckPendingPurchasesAsync() { var billing = CrossInAppBilling.Current; try { await billing.ConnectAsync(); var purchases = await billing.GetPurchasesAsync(ItemType.InAppPurchase); var pendingPurchases = purchases?.Where(p => p.State == PurchaseState.PaymentPending); if (pendingPurchases?.Any() == true) { // Subscribe to purchase updates (Android only) Plugin.InAppBilling.InAppBillingImplementation.OnAndroidPurchasesUpdated = (billingResult, updatedPurchases) => { foreach (var purchase in updatedPurchases) { if (purchase.State == PurchaseState.Purchased) { // Payment completed, finalize and grant entitlement ProcessCompletedPurchase(purchase); } } }; // Keep connection open to receive updates Console.WriteLine("Monitoring pending purchases..."); } else { await billing.DisconnectAsync(); } } catch (Exception ex) { Console.WriteLine($"Error checking pending: {ex.Message}"); await billing.DisconnectAsync(); } } private async void ProcessCompletedPurchase(InAppBillingPurchase purchase) { var billing = CrossInAppBilling.Current; await billing.FinalizePurchaseAsync(new[] { purchase.TransactionIdentifier }); Console.WriteLine($"Pending purchase completed: {purchase.ProductId}"); } ``` -------------------------------- ### Handle Pending Transactions in Android Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/README.md This C# code snippet demonstrates how to handle pending transactions on Android. It connects to the billing service, checks for pending purchases, and sets up a listener to process purchase updates. If no pending orders are found, it disconnects the service. ```csharp // 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(); } ``` -------------------------------- ### Add Google Play Billing Version to AndroidManifest.xml Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/README.md If you encounter errors in Google Play, you may need to add this meta-data tag to your AndroidManifest.xml file. Ensure the 'android:value' matches the version of the Billing Library used by the NuGet package. ```xml ``` -------------------------------- ### Android Manifest Metadata for Billing Library Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GettingStarted.md Add metadata to your AndroidManifest.xml to specify the version of the Google Play Billing Library being used. Ensure the version number matches your project's dependency. ```xml ``` -------------------------------- ### Connect and Disconnect InAppBilling API Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/GettingStarted.md Ensures a valid connection to the app store before making billing calls and disconnects when finished. Recommended to call DisconnectAsync in a finally block. ```csharp public async Task MakePurchase() { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if(!connected) return false; //make additional billing calls } finally { await billing.DisconnectAsync(); } } ``` -------------------------------- ### Android ProGuard Rules Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/README.md These ProGuard rules are necessary for the Android billing client to function correctly. ```proguard -keep class com.android.billingclient.api.** { *; } -keep class com.android.vending.billing.** { *; } ``` -------------------------------- ### Purchase Consumable Item Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/PurchaseConsumable.md Use this method to initiate the purchase of a consumable in-app item. Ensure you are connected to the billing service before calling this method. The `obfuscatedAccountId` and `obfuscatedProfileId` parameters are optional and used for platform-specific user identification. ```csharp /// /// Purchase a specific product or subscription /// /// Sku or ID of product /// Type of product being requested /// Specifies an optional obfuscated string that is uniquely associated with the user's account in your app. /// Specifies an optional obfuscated string that is uniquely associated with the user's profile in your app. /// Cancel the request. /// Purchase details /// If an error occurs during processing Task PurchaseAsync(string productId, ItemType itemType, string obfuscatedAccountId = null, string obfuscatedProfileId = null, CancellationToken cancellationToken = default); ``` -------------------------------- ### Consume Purchase Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/PurchaseConsumable.md Consume a previously purchased item using its product ID and transaction identifier. This is required on Android and Windows to allow the user to purchase the item again. On iOS, manual consumption is required if `InAppBillingImplementation.FinishAllTransactions` is set to `false`. ```csharp /// /// Consume a purchase with a purchase token. /// /// Id or Sku of product /// Original Purchase Token /// Cancel the request /// If consumed successful /// If an error occurs during processing Task ConsumePurchaseAsync(string productId, string transactionIdentifier, CancellationToken cancellationToken = default); ``` -------------------------------- ### Present iOS Offer Code Redemption Sheet Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Displays the subscription offer code redemption sheet on iOS. This is a platform-specific feature for redeeming promotional codes. ```csharp public void ShowOfferCodeRedemption() { var billing = CrossInAppBilling.Current; // iOS only: Display the subscription offer code redemption sheet billing.PresentCodeRedemption(); } ``` -------------------------------- ### PurchaseError Enum Definition Source: https://github.com/jamesmontemagno/inappbillingplugin/blob/master/docs/HandlingExceptions.md Defines the possible error codes that can be returned by the In-App Billing system. Use these codes to identify specific issues during purchase operations. ```csharp /// /// Type of purchase error /// public enum PurchaseError { /// /// Billing system unavailable /// BillingUnavailable, /// /// Developer issue /// DeveloperError, /// /// Product sku not available /// ItemUnavailable, /// /// Other error /// GeneralError, /// /// User cancelled the purchase /// UserCancelled, /// /// App store unavailable on device /// AppStoreUnavailable, /// /// User is not allowed to authorize payments /// PaymentNotAllowed, /// /// One of hte payment parameters was not recognized by app store /// PaymentInvalid, /// /// The requested product is invalid /// InvalidProduct, /// /// The product request failed /// ProductRequestFailed, /// /// Restoring the transaction failed /// RestoreFailed, /// /// Network connection is down /// ServiceUnavailable } ``` -------------------------------- ### Purchase Non-Consumable Item Source: https://context7.com/jamesmontemagno/inappbillingplugin/llms.txt Purchases a non-consumable item and finalizes the transaction. Ensure the billing service is connected before calling this method. On Android, purchases must be acknowledged within 3 days. ```csharp public async Task PurchaseNonConsumable(string productId) { var billing = CrossInAppBilling.Current; try { var connected = await billing.ConnectAsync(); if (!connected) return false; // Purchase the non-consumable item var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase); if (purchase == null) { // Purchase was not completed return false; } if (purchase.State == PurchaseState.Purchased) { Console.WriteLine($ ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.