A .NET MAUI binding library that provides seamless integration with iOS StoreKit2 In-App Purchase functionality.
This library enables .NET MAUI applications to leverage Apple's modern StoreKit2 framework for handling in-app purchases on iOS. It provides a C# wrapper around the native StoreKit2 APIs, making it easy to integrate IAP functionality into your cross-platform MAUI applications.
- ✅ Product Information Retrieval: Fetch product details from the App Store
- ✅ Purchase Processing: Handle consumable, non-consumable, and subscription purchases
- ✅ Purchase Restoration: Restore previous purchases for users
- ✅ Transaction Verification: Built-in transaction verification using StoreKit2
- ✅ Purchase Status Checking: Check the current entitlement status of products
- ✅ Async/Await Support: Modern async programming patterns
- ✅ Delegate Pattern: Event-driven callbacks for purchase events
- ✅ iOS 15+ Support: Takes advantage of the latest StoreKit2 features
- iOS 15.0+ (StoreKit2 requirement)
- .NET 8.0 or later
- MAUI Project targeting iOS
- Create your app in App Store Connect
- Configure In-App Purchase products with the same product IDs used in your code
- Create sandbox test users for testing
- iOS 15.0+ deployment target
- Valid Bundle ID matching App Store Connect
- StoreKit capability enabled in your iOS project
dotnet add package StoreKit2Or add to your .csproj file:
<PackageReference Include="StoreKit2" Version="1.0.1" />- Clone this repository
- Add the project reference to your MAUI project:
<ProjectReference Include="path/to/MAUI.StoreKit2/MAUI.StoreKit2.csproj" />
- Build and run
using StoreKit2;
// Get the shared instance
var paymentManager = PaymentManager.Shared;
// Set up delegate for callbacks
paymentManager.Delegate = new PaymentManagerDelegateImpl();public class PaymentManagerDelegateImpl : PaymentManagerDelegate
{
public override void PaymentManagerDidFinishPurchase(string productId, PaymentTransaction transaction)
{
Console.WriteLine($"Purchase completed: {productId}");
// Handle successful purchase
}
public override void PaymentManagerDidFailPurchase(string productId, string error)
{
Console.WriteLine($"Purchase failed: {productId}, Error: {error}");
// Handle purchase failure
}
public override void PaymentManagerDidUpdateProducts(PaymentProduct[] products)
{
Console.WriteLine($"Products loaded: {products.Length}");
// Update UI with product information
}
public override void PaymentManagerDidRestorePurchases(PaymentTransaction[] transactions)
{
Console.WriteLine($"Restored {transactions.Length} purchases");
// Handle restored purchases
}
}string[] productIds = { "com.yourapp.product1", "com.yourapp.product2" };
paymentManager.RequestProductsWithProductIds(productIds, (success, error) =>
{
if (success)
{
Console.WriteLine("Products loaded successfully");
var products = paymentManager.GetAllProducts;
// Display products in your UI
}
else
{
Console.WriteLine($"Failed to load products: {error}");
}
});paymentManager.PurchaseProductWithProductId("com.yourapp.product1", null, (success, error) =>
{
if (success)
{
Console.WriteLine("Purchase initiated successfully");
}
else
{
Console.WriteLine($"Purchase failed: {error}");
}
});paymentManager.RestorePurchasesWithCompletion((success, error) =>
{
if (success)
{
Console.WriteLine("Purchases restored successfully");
}
else
{
Console.WriteLine($"Restore failed: {error}");
}
});paymentManager.CheckPurchaseStatusWithProductId("com.yourapp.product1", (hasPurchase, transaction) =>
{
if (hasPurchase)
{
Console.WriteLine($"User owns this product. Transaction ID: {transaction.TransactionId}");
}
else
{
Console.WriteLine("User does not own this product");
}
});The main class for handling in-app purchases.
Shared: Static singleton instanceDelegate: Delegate for receiving purchase events
RequestProductsWithProductIds(): Load product information from the App StorePurchaseProductWithProductId(): Initiate a purchase for a specific productRestorePurchasesWithCompletion(): Restore previous purchasesGetProductWithProductId(): Get product information by IDAllProducts: Get all loaded productsCheckPurchaseStatusWithProductId(): Check if user owns a specific product
Represents a product available for purchase.
ProductId: Unique product identifierDisplayName: Localized product nameProductDescription: Localized product descriptionPrice: Product price as NSDecimalNumberDisplayPrice: Formatted price stringProductType: Product type (consumable, nonConsumable, etc.)
Represents a completed transaction.
TransactionId: Unique transaction identifierProductId: Associated product identifierPurchaseDate: Date of purchaseIsUpgraded: Whether this is an upgrade transactionRevocationDate: Date of revocation (if applicable)RevocationReason: Reason for revocation (if applicable)
The library supports all StoreKit2 product types:
- Consumable: Products that can be purchased multiple times
- Non-Consumable: Products that are purchased once
- Auto-Renewable: Subscriptions that renew automatically
- Non-Renewable: Subscriptions that don't renew automatically
The library provides comprehensive error handling through:
- Completion callbacks with success/error parameters
- Delegate methods for handling purchase failures
- Detailed error messages for debugging
Before you can test in-app purchases during development, you need to set up a StoreKit Configuration file in Xcode. This allows you to test purchases locally without requiring App Store Connect configuration.
-
Open your iOS project in Xcode (the native iOS project inside your MAUI solution's
Platforms/iOSfolder, or open via the.xcodeproj/.xcworkspaceif building natively) -
Create a new StoreKit Configuration file:
- In Xcode, go to File → New → File...
- Search for "StoreKit" and select StoreKit Configuration File
- Name it (e.g.,
Products.storekit) and save it
-
Add your products:
- Click the + button in the StoreKit Configuration editor
- Choose the product type (Consumable, Non-Consumable, Auto-Renewable Subscription, or Non-Renewing Subscription)
- Fill in the product details:
- Reference Name: A descriptive name for your reference
- Product ID: Must match the product IDs you use in your code (e.g.,
com.yourapp.product1) - Price: Set a test price
- Localization: Add display name and description
-
Enable the StoreKit Configuration in your scheme:
- Go to Product → Scheme → Edit Scheme...
- Select Run on the left panel
- Go to the Options tab
- Under StoreKit Configuration, select your
.storekitfile
Important for MAUI developers: The
.storekitconfiguration file should remain in your Xcode project. It is used by Xcode's testing infrastructure and does not need to be moved to your MAUI project's iOS platform folder.
Local Testing with StoreKit Configuration (Recommended for Development):
- Works in the iOS Simulator (iOS 14+)
- No App Store Connect setup required
- Instant purchase confirmations
- Great for rapid development and debugging
Sandbox Testing (Recommended for Pre-Release):
- Use Apple's sandbox environment
- Create test user accounts in App Store Connect
- Configure your products in App Store Connect
- Test on physical devices for most accurate results
Note: While StoreKit2 supports testing in the simulator with a StoreKit Configuration file, testing on physical devices with sandbox accounts is recommended before release to ensure the full purchase flow works correctly.
For detailed instructions, see Apple's official documentation: Setting up StoreKit Testing in Xcode
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
If you encounter any issues or have questions:
- Check the Issues page
- Create a new issue with detailed information
- Provide sample code and error messages when applicable
Yuting Li
Shanghai Jiuqianji Technology Co., Ltd.
- Apple's StoreKit2 documentation and samples
- The .NET MAUI community for guidance and support
If you’d like to support this project, you can buy us a coffee at Buy me a coffee