A Tauri plugin for In-App Purchases (IAP) with support for subscriptions on iOS (StoreKit 2), Android (Google Play Billing), Windows (Microsoft Store) and macOS (StoreKit 2).
- Query products and subscriptions with detailed pricing
- Purchase subscriptions with platform-specific features
- Restore previous purchases
- Get purchase history
- Check product ownership and subscription status
- Real-time purchase state updates via events
- Automatic transaction verification (iOS)
- Support for introductory offers and free trials
- Fraud prevention with obfuscated account/profile IDs (Android)
- App account token support for tracking (iOS)
- Automatic offer token selection (Android)
- Subscription upgrades/downgrades with proration modes (Android)
- iOS: StoreKit 2 (requires iOS 15.0+)
- Android: Google Play Billing Library v8.0.0
- Windows: Microsoft Store API (Windows 10/11)
- macOS: StoreKit 2 (requires macOS 13.0+)
Install the JavaScript package:
npm install @choochmeque/tauri-plugin-iap-api
# or
yarn add @choochmeque/tauri-plugin-iap-api
# or
pnpm add @choochmeque/tauri-plugin-iap-apiAdd the plugin to your Tauri project's Cargo.toml:
[dependencies]
tauri-plugin-iap = "0.9"Configure the plugin permissions in your capabilities/default.json:
{
"permissions": [
"iap:default"
]
}Register the plugin in your Tauri app:
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_iap::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}An example application is available in the examples/iap-demo directory. The example demonstrates all core IAP functionality with a UI:
- Fetch and display products with pricing
- Purchase products and subscriptions
- Restore previous purchases
- Check product ownership status
- Real-time purchase update notifications
cd examples/iap-demo
# Install dependencies
pnpm install
# Run in development mode
pnpm tauri dev
# Build for production
pnpm tauri buildImportant: The app must be properly code-signed to test IAP functionality:
- iOS/macOS: Code signing is required by StoreKit. Configure signing in Xcode or via Tauri's configuration.
- Android: Sign your APK/AAB with a release keystore for production testing.
- Windows: App must be associated with Microsoft Store and signed for Store submission.
The example app provides a fully functional demo with inline documentation for each IAP feature, making it easy to understand how to integrate the plugin into your own application.
import {
getProducts,
purchase,
restorePurchases,
acknowledgePurchase,
consumePurchase,
getProductStatus,
onPurchaseUpdated,
PurchaseState
} from '@choochmeque/tauri-plugin-iap-api';
// Get available products
const products = await getProducts(['subscription_id_1', 'subscription_id_2'], 'subs');
// Check if user owns a specific product
const status = await getProductStatus('subscription_id_1', 'subs');
if (status.isOwned && status.purchaseState === PurchaseState.PURCHASED) {
console.log('User has active subscription');
if (status.isAutoRenewing) {
console.log('Subscription will auto-renew');
}
}
// Purchase a subscription or in-app product
// Simple purchase (will use first available offer on Android if not specified)
const purchaseResult = await purchase('subscription_id_1', 'subs');
// With specific offer token (Android)
const purchaseResult = await purchase('subscription_id_1', 'subs', {
offerToken: product.subscriptionOfferDetails[0].offerToken
});
// With fraud prevention (Android)
const purchaseResult = await purchase('subscription_id_1', 'subs', {
obfuscatedAccountId: 'hashed_account_id',
obfuscatedProfileId: 'hashed_profile_id'
});
// With app account token (iOS - must be valid UUID)
const purchaseResult = await purchase('subscription_id_1', 'subs', {
appAccountToken: '550e8400-e29b-41d4-a716-446655440000'
});
// Upgrade/downgrade subscription (Android)
import { SubscriptionReplacementMode } from '@choochmeque/tauri-plugin-iap-api';
const upgraded = await purchase('premium_subscription', 'subs', {
offerToken: 'premium_offer_token',
oldPurchaseToken: currentSubscription.purchaseToken,
subscriptionReplacementMode: SubscriptionReplacementMode.WITH_TIME_PRORATION
});
// Restore purchases (specify product type)
const restored = await restorePurchases('subs');
// Acknowledge a non-consumable purchase (subscriptions, durables).
// No-op on iOS/macOS — StoreKit auto-finishes transactions.
await acknowledgePurchase(purchaseResult.purchaseToken);
// Consume a consumable purchase (credits, coins) so it can be re-bought.
// No-op on iOS/macOS — StoreKit auto-allows re-purchase.
await consumePurchase(purchaseResult.purchaseToken);
// Listen for purchase updates
const listener = await onPurchaseUpdated((purchase) => {
console.log('Purchase updated:', purchase);
});
// Stop listening
await listener.unregister();use tauri_plugin_iap::{IapExt, PurchaseRequest, PurchaseStateValue, Result};
// Get available products
let products = app.iap()
.get_products(
vec!["subscription_id_1".into(), "subscription_id_2".into()],
"subs".into(),
)
.await?;
// Check if user owns a specific product
let status = app.iap()
.get_product_status("subscription_id_1".into(), "subs".into())
.await?;
if status.is_owned && status.purchase_state == Some(PurchaseStateValue::Purchased) {
println!("User has active subscription");
if status.is_auto_renewing == Some(true) {
println!("Subscription will auto-renew");
}
}
// Purchase a subscription or in-app product
// Simple purchase (will use first available offer on Android if not specified)
let purchase_result = app.iap()
.purchase(PurchaseRequest {
product_id: "subscription_id_1".into(),
product_type: "subs".into(),
options: None,
})
.await?;
// Restore purchases (specify product type)
let restored = app.iap().restore_purchases("subs".into()).await?;
// Acknowledge a non-consumable purchase (subscriptions, durables).
// No-op on iOS/macOS — StoreKit auto-finishes transactions.
app.iap().acknowledge_purchase(purchase_result.purchase_token).await?;- Configure your app in App Store Connect
- Create subscription products with appropriate pricing
- Add In-App Purchase capability to your app in Xcode:
- Open your project in Xcode
- Select your target
- Go to "Signing & Capabilities"
- Click "+" and add "In-App Purchase"
- Test with sandbox accounts
- Add your app to Google Play Console
- Create subscription products in Google Play Console
- Configure your app's billing permissions (already included in the plugin)
- Test with test accounts or sandbox environment
- Register your app in Microsoft Partner Center
- Create add-on products (consumables, durables, or subscriptions)
- Associate your app with the Microsoft Store
- Test with Windows sandbox environment
- Configure your app in App Store Connect
- Create subscription or in-app purchase products with pricing
- Configure code signing in
tauri.conf.json:{ "bundle": { "macOS": { "signingIdentity": "Developer ID Application: Your Name (TEAM_ID)", "entitlements": "path/to/entitlements.plist" } } } - Add required entitlements for StoreKit (create
entitlements.plist):<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.network.client</key> <true/> </dict> </plist>
- Test with sandbox accounts or StoreKit Configuration files
- Important: App must be code-signed to use StoreKit APIs
Deprecated: This function is no longer needed and will be removed in a future major release. The billing client is now initialized automatically when the plugin loads.
Returns { success: true } for backward compatibility.
Fetches product details from the store.
Returns:
products: Array of product objects with:productId: Product identifiertitle: Display namedescription: Product descriptionproductType: Type of productformattedPrice: Localized price stringsubscriptionOfferDetails: (subscriptions only) Array of offers
Initiates a purchase flow with enhanced options for fraud prevention and account management.
Parameters:
productId: The product to purchaseproductType: Type of product ('subs' for subscriptions, 'inapp' for one-time purchases), defaults to 'subs'options: Optional purchase parameters:offerToken: (Android) Specific offer to purchase. If not provided, uses first available offerobfuscatedAccountId: (Android) Hashed account ID for fraud preventionobfuscatedProfileId: (Android) Hashed profile ID for fraud preventionappAccountToken: (iOS) UUID string for account tracking and fraud preventionoldPurchaseToken: (Android) Purchase token of existing subscription to replace for upgrades/downgradessubscriptionReplacementMode: (Android) Proration mode usingSubscriptionReplacementModeenum (defaults toWITH_TIME_PRORATION)
Returns: Purchase object with transaction details
Queries and returns all active purchases.
Parameters:
productType: Type of products to restore ('subs' or 'inapp'), defaults to 'subs'
Returns the complete purchase history.
Acknowledges a non-consumable purchase (subscriptions, durables). On Android this is required within 3 days or Google auto-refunds the purchase. No-op on iOS, macOS, and Windows. Use consumePurchase instead for consumables.
Consumes a consumable purchase (credits, coins, gems) so it can be purchased again. On Android calls BillingClient.consumeAsync(); on Windows calls StoreContext.ReportConsumableFulfillmentAsync with quantity 1. No-op on iOS and macOS — StoreKit auto-allows re-purchase. Never call both acknowledgePurchase and consumePurchase for the same purchase token.
Checks the ownership and subscription status of a specific product.
Parameters:
productId: The product identifier to checkproductType: Type of product ('subs' or 'inapp'), defaults to 'subs'
Returns: ProductStatus object with:
productId: Product identifierisOwned: Whether the user currently owns the productpurchaseState: Current state (PURCHASED=0, CANCELED=1, PENDING=2)purchaseTime: When the product was purchased (timestamp)expirationTime: (subscriptions only) When the subscription expiresisAutoRenewing: (subscriptions only) Whether auto-renewal is enabledisAcknowledged: Whether the purchase has been acknowledgedpurchaseToken: Token for the purchase transaction
Listens for purchase state changes.
Returns: A PluginListener object with an unregister() method to stop listening.
- Automatic transaction verification
- No manual acknowledgment needed
- Supports introductory offers and promotional offers
- Transaction updates are automatically observed
- Requires iOS 15.0+
- Manual acknowledgment required within 3 days
- Supports multiple subscription offers per product
- Offer tokens required for subscription purchases
- More detailed pricing phase information
- Automatic acknowledgment handled by the Store
- Supports consumables, durables, and subscriptions
- Uses SKUs for subscription offer variations
- Same StoreKit 2 API as iOS
- Automatic transaction verification
- No manual acknowledgment needed
- Requires macOS 13.0+
- App must be code-signed (StoreKit requires valid signature)
- Use sandbox test accounts
- Test on physical devices (subscriptions don't work well on simulators)
- Clear purchase history in Settings > App Store > Sandbox Account
- Upload your app to internal testing track
- Add test accounts in Google Play Console
- Test with test payment methods
- Use Microsoft Store sandbox environment
- Configure test accounts in Partner Center
- Test with Windows Dev Center test payment methods
- Ensure app is associated with Store listing
- Use sandbox test accounts (same as iOS)
- Use StoreKit Configuration files for local testing
- App must be code-signed to use StoreKit
- Clear purchase history in System Settings > App Store > Sandbox Account
dyld: Library not loaded: @rpath/libswift_Concurrency.dylib
This error occurs when MACOSX_DEPLOYMENT_TARGET is below 13.0. Tauri defaults to 11.0 in debug mode.
Option 1: Add .cargo/config.toml to your project:
[env]
MACOSX_DEPLOYMENT_TARGET = "13.0"Option 2: Set the environment variable when running:
MACOSX_DEPLOYMENT_TARGET="13.0" pnpm tauri dev