### Install Dependencies for Example App Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Install project dependencies for the example application using pnpm. ```bash pnpm install ``` -------------------------------- ### Build Example App for Production Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Build the example application for production using pnpm and Tauri. ```bash pnpm tauri build ``` -------------------------------- ### Run Example App in Development Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Run the example application in development mode using pnpm and Tauri. ```bash pnpm tauri dev ``` -------------------------------- ### Async Command Implementation Example Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md This Rust snippet demonstrates an asynchronous command implementation for getting products. It utilizes the '?' operator for error propagation and awaits platform-specific asynchronous calls. ```rust pub async fn get_products( app: AppHandle, payload: GetProductsRequest, ) -> Result { app.iap() .get_products(payload.product_ids, payload.product_type) .await // Awaits platform async call } ``` -------------------------------- ### Request Serialization Example (Rust to JSON) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Demonstrates how Rust structs with camelCase field names are serialized into JSON payloads for requests. Includes an example of flattening nested options. ```rust // Rust struct struct PurchaseRequest { product_id: String, product_type: String, options: Option, } // JSON payload { "productId": "...", "productType": "subs", "offerToken": "..." // From flattened options } ``` -------------------------------- ### Initiate a Purchase Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Use this function to start a purchase flow for a product or subscription. Remember to acknowledge the purchase on Android. ```typescript import { purchase, acknowledgePurchase } from '@choochmeque/tauri-plugin-iap-api'; const result = await purchase('premium_monthly', 'subs'); await acknowledgePurchase(result.purchaseToken); // Required on Android ``` -------------------------------- ### init() Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Initializes the IAP plugin in your Tauri application. This must be called during application setup to enable all IAP functionality. ```APIDOC ## init() ### Description Initialize the IAP plugin in your Tauri application. This function must be called in the main Tauri builder setup. ### Method Plugin Initialization ### Signature ```rust pub fn init() -> TauriPlugin ``` ### Returns Returns a `TauriPlugin` instance that can be registered with the Tauri builder. ### Usage Example ```rust fn main() { tauri::Builder::default() .plugin(tauri_plugin_iap::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` ### Notes - Must be called during application initialization - Required before any other IAP operations can be performed - Generic parameter `R` represents the Tauri Runtime type ``` -------------------------------- ### TypeScript: Upgrade or Downgrade Subscription Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Provides an example of how to handle subscription upgrades or downgrades using the `purchase` function with `oldPurchaseToken` and `subscriptionReplacementMode`. ```typescript const currentPlan = 'monthly_plan'; const newPlan = 'yearly_plan'; const upgrade = await purchase(newPlan, 'subs', { offerToken: newOffer.offerToken, oldPurchaseToken: currentSubscription.purchaseToken, subscriptionReplacementMode: SubscriptionReplacementMode.WITH_TIME_PRORATION }); ``` -------------------------------- ### Response Serialization Example (Rust to JSON) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Shows how Rust structs are serialized into JSON responses. Highlights handling of optional fields and null values. ```rust // Rust struct struct Purchase { order_id: Option, package_name: String, product_id: String, // ... } // JSON response { "orderId": "...", // null if not present "packageName": "...", "productId": "...", // ... "originalJson": "", // Optional fields present if Some "signature": "" } ``` -------------------------------- ### Consume Purchase on Windows Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Example of consuming a purchased item on Windows after granting the user the product. This is a required step for consumable products. ```typescript const purchase = await purchase('coins_100', 'inapp'); grantCoinsToUser(100); await consumePurchase(purchase.purchaseToken); // Required ``` -------------------------------- ### Product Caching Implementation Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md This example shows how to implement product caching to improve performance. It includes logic for retrieving cached products and a basic cache invalidation strategy. ```typescript let cachedProducts: Product[] | null = null; async function getProducts(ids: string[], type: string) { if (cachedProducts) { return { products: cachedProducts.filter(p => ids.includes(p.productId)) }; } const response = await iap.getProducts(ids, type); cachedProducts = response.products; return response; } ``` -------------------------------- ### Cross-Platform Purchase Flow Example Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Demonstrates the complete lifecycle of an in-app purchase, from fetching products to acknowledging the purchase and cleaning up listeners. This flow is applicable across different platforms supported by the plugin. ```typescript import { getProducts, purchase, acknowledgePurchase, getProductStatus, onPurchaseUpdated, PurchaseState } from '@choochmeque/tauri-plugin-iap-api'; // 1. Fetch available products const { products } = await getProducts(['premium_monthly'], 'subs'); // 2. Listen for updates const listener = await onPurchaseUpdated((purchase) => { if (purchase.purchaseState === PurchaseState.PURCHASED) { grantAccess(); } }); // 3. Initiate purchase with platform-specific options const result = await purchase('premium_monthly', 'subs', { // Android-specific offerToken: products[0].subscriptionOfferDetails?.[0].offerToken, // iOS-specific appAccountToken: getUserUUID() }); // 4. Acknowledge (critical on Android) try { await acknowledgePurchase(result.purchaseToken); } catch (error) { console.warn('Ack failed (non-critical on iOS/macOS):', error); } // 5. Check status const status = await getProductStatus('premium_monthly', 'subs'); console.log(`Owned: ${status.isOwned}, Auto-renewing: ${status.isAutoRenewing}`); // 6. Clean up await listener.unregister(); ``` -------------------------------- ### Install JavaScript Package Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Install the JavaScript package for the Tauri Plugin IAP using npm, yarn, or pnpm. ```bash npm install @choochmeque/tauri-plugin-iap-api ``` ```bash yarn add @choochmeque/tauri-plugin-iap-api ``` ```bash pnpm add @choochmeque/tauri-plugin-iap-api ``` -------------------------------- ### Initiate a Purchase Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Use the `purchase` function to start a transaction for a product or subscription. Specify the product ID and type. Optional parameters allow for platform-specific configurations like fraud prevention or offer tokens. Ensure correct product types ('subs' or 'inapp') are used. ```typescript import { purchase, SubscriptionReplacementMode } from '@choochmeque/tauri-plugin-iap-api'; // Simple purchase (uses first available offer on Android) const result = await purchase('premium_monthly', 'subs'); console.log(`Purchase token: ${result.purchaseToken}`); // With fraud prevention (Android) const result = await purchase('premium_monthly', 'subs', { obfuscatedAccountId: 'hashed_account_123', obfuscatedProfileId: 'hashed_profile_456' }); // With app account token (iOS) const result = await purchase('premium_monthly', 'subs', { appAccountToken: '550e8400-e29b-41d4-a716-446655440000' }); // With specific offer token (Android) const result = await purchase('premium_monthly', 'subs', { offerToken: product.subscriptionOfferDetails[0].offerToken }); // Upgrade subscription (Android) const upgraded = await purchase('premium_yearly', 'subs', { offerToken: newOffer.offerToken, oldPurchaseToken: currentSubscription.purchaseToken, subscriptionReplacementMode: SubscriptionReplacementMode.WITH_TIME_PRORATION }); ``` -------------------------------- ### Initiate Purchase Transaction Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Use this command to start a purchase flow for a product. Ensure the product ID is valid and consider platform-specific payload fields like offerToken or appAccountToken. ```typescript { productId: string; // Required. Product ID. productType: string; // Optional, defaults to "subs". // "subs" or "inapp" offerToken?: string; // Optional (Android only). obfuscatedAccountId?: string; // Optional (Android only). obfuscatedProfileId?: string; // Optional (Android only). appAccountToken?: string; // Optional (iOS only). oldPurchaseToken?: string; // Optional (Android only). subscriptionReplacementMode?: number; // Optional (Android only). } ``` -------------------------------- ### Initialize Desktop Listener Infrastructure Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Initialize the custom listener system for desktop platforms (macOS, Windows, Linux) within the plugin's setup. This is necessary as Tauri's native listener API is mobile-only. ```rust // Initialize listeners at plugin setup #[cfg(desktop)] listeners::init(); ``` -------------------------------- ### Enum Serialization Example Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Shows how Rust enum variants are serialized to their corresponding numeric values. ```rust PurchaseStateValue::Purchased // Serializes to 0 ``` -------------------------------- ### Platform-Specific Dependencies Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/architecture-and-flow.md Example TOML configuration for including dependencies only for specific target operating systems. This ensures that platform-specific libraries are not included in builds for other platforms. ```toml [target.'cfg(target_os = "macos")'.dependencies] swift-bridge = { version = "0.1", features = ["async"] } [target.'cfg(target_os = "windows")'.dependencies] windows = { version = "0.61", features = [...] } ``` -------------------------------- ### Error Serialization Example Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Illustrates how Rust error types are serialized into simple strings. ```rust Error::Io(...) // Serializes to "io error message" ``` -------------------------------- ### Purchasing with App Account Token Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Example of how to include an app account token during the purchase process for linking purchases to user accounts. This is an optional parameter recommended for production apps. ```typescript await purchase('premium_monthly', 'subs', { appAccountToken: 'user_uuid_here' }); ``` -------------------------------- ### Tauri Command Handler for Get Products Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/architecture-and-flow.md Handles the `get_products` command from JavaScript. It deserializes the request, accesses the IAP interface via the AppHandle, delegates to the platform implementation, and returns a typed response. ```rust #[command] pub async fn get_products( app: AppHandle, payload: GetProductsRequest ) -> Result ``` -------------------------------- ### Initialize Tauri Plugin IAP Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Initializes the IAP plugin within your Tauri application. This setup is necessary before using any IAP functionalities. ```rust pub fn init() -> TauriPlugin ``` ```rust fn main() { tauri::Builder::default() .plugin(tauri_plugin_iap::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` -------------------------------- ### Manage Plugin Instance in Tauri State Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Manage the Iap instance within Tauri's app state during setup. Access the instance in commands or other parts of your application. ```rust app.manage(iap); ``` ```rust fn iap(&self) -> &Iap { self.state::>().inner() } ``` -------------------------------- ### Invoke get_products Directly Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Example of how to directly invoke the `get_products` command using Tauri's `invoke` API in TypeScript. It shows the structure of the payload with product IDs and type. ```typescript import { invoke } from '@tauri-apps/api/core'; const response = await invoke('plugin:iap|get_products', { payload: { productIds: ['premium_monthly', 'premium_yearly'], productType: 'subs' } }); ``` -------------------------------- ### Rust Field Serialization Example Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Demonstrates how Rust snake_case fields are serialized to JSON camelCase. Optional fields are omitted if they are None. ```rust pub is_auto_renewing: bool // Serializes to "isAutoRenewing" #[serde(skip_serializing_if = "Option::is_none")] pub original_json: Option, ``` -------------------------------- ### Purchase Flow with Error Handling in JavaScript Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/errors.md This example shows a robust purchase flow in JavaScript, including acknowledging the purchase and handling various potential errors. It logs specific messages for unsupported platforms or already owned products. ```typescript import { getProducts, purchase, acknowledgePurchase } from '@choochmeque/tauri-plugin-iap-api'; async function handlePurchase(productId: string) { try { const purchase = await purchase(productId, 'subs'); // Acknowledge the purchase (important for Android) try { await acknowledgePurchase(purchase.purchaseToken); console.log('Purchase successful and acknowledged'); } catch (ackError) { // If ack fails but purchase succeeded, log but continue console.error('Failed to acknowledge purchase:', ackError); // User still owns the product; retry ack later } } catch (error) { if (error instanceof Error) { const message = error.message; if (message.includes('not supported')) { console.error('IAP not available on this platform'); } else if (message.includes('already owned')) { console.error('User already owns this product'); } else { console.error(`Purchase failed: ${message}`); } } } } ``` -------------------------------- ### Get Product Status Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Retrieves the current status of a specific product, identified by its ID and type. This helps in checking availability and pricing. ```rust pub async fn get_product_status( &self, product_id: String, product_type: String ) -> Result ``` -------------------------------- ### Get Purchase History Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Retrieves the purchase history for the user. Note that this operation is synchronous on mobile platforms but asynchronous on desktop. ```rust pub fn get_purchase_history(&self) -> Result ``` -------------------------------- ### Rust: Get Products and Purchase Status Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Retrieve available products and check the ownership status of a specific product using the Rust API. Ensure correct product IDs and types are provided. ```rust 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"); } } ``` -------------------------------- ### JavaScript/TypeScript: Get Products and Purchase Status Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Retrieve available products and check the ownership status of a specific product. Ensure the product IDs and types are correctly specified. ```typescript 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'); } } ``` -------------------------------- ### Get Product Status (JavaScript) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Check the current ownership and subscription status of a specific product. Use this to determine if a user already owns a product or subscription. ```typescript export async function getProductStatus( productId: string, productType: "subs" | "inapp" = "subs" ): Promise ``` ```typescript import { getProductStatus, PurchaseState } from '@choochmeque/tauri-plugin-iap-api'; const status = await getProductStatus('premium_monthly', 'subs'); if (status.isOwned) { console.log('User owns this subscription'); if (status.purchaseState === PurchaseState.PURCHASED) { console.log('Purchase is complete'); } if (status.isAutoRenewing) { console.log('Subscription will auto-renew'); } if (status.expirationTime) { const expiresAt = new Date(status.expirationTime); console.log(`Expires: ${expiresAt.toDateString()}`); } } else { console.log('Product available for purchase'); } ``` -------------------------------- ### Get Products (Rust) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Fetches product details from the store using a list of product IDs and type. This method is accessed via the IapExt trait. ```rust pub async fn get_products( &self, product_ids: Vec, product_type: String ) -> Result ``` -------------------------------- ### Check IAP Support and Get Products in JavaScript Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Use Tauri's invoke API to check if IAP is supported on the current platform and retrieve product information. Handles potential errors during invocation. ```typescript import { invoke } from '@tauri-apps/api/core'; const isSupported = await invoke('plugin:iap|is_supported') .catch(() => false); if (isSupported) { // Use IAP APIs const products = await getProducts([...], 'subs'); } ``` -------------------------------- ### Get Purchase History (TypeScript) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Retrieves the complete purchase history for the user. Note that this may be empty or unavailable on iOS/macOS due to StoreKit 2 limitations. ```typescript import { getPurchaseHistory } from '@choochmeque/tauri-plugin-iap-api'; const { history } = await getPurchaseHistory(); history.forEach(record => { console.log(`Product: ${record.productId}`); console.log(` Purchase time: ${new Date(record.purchaseTime)}`); console.log(` Quantity: ${record.quantity}`); }); ``` -------------------------------- ### IAP Extension Trait (Rust) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md An extension trait for Tauri's App, AppHandle, and Window to access IAP functionalities. Use this to get an IAP instance within your Rust commands. ```rust pub trait IapExt { fn iap(&self) -> &Iap; } ``` ```rust use tauri_plugin_iap::{IapExt, PurchaseRequest}; #[tauri::command] async fn my_command(app: tauri::AppHandle) -> Result<()> { let iap = app.iap(); let products = iap.get_products( vec!["subscription_id_1".into()], "subs".into() ).await?; Ok(()) } ``` -------------------------------- ### TypeScript: Purchase with Specific Offer Token Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Demonstrates how to initiate a purchase using a specific offer token from the product details. If no offer token is provided, the first available offer is used by default. ```typescript // Mandatory to specify which offer const purchase = await purchase('premium', 'subs', { offerToken: product.subscriptionOfferDetails[0].offerToken }); // If not provided, first available offer is used const purchase = await purchase('premium', 'subs'); ``` -------------------------------- ### Sandbox Testing with StoreKit Configuration Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Instructions for setting up and running local tests using a StoreKit Configuration file in Xcode for iOS simulators. ```bash # Run Xcode tests with StoreKit Configuration xcrun simctl addMedia booted storekit_config.storekit ``` -------------------------------- ### Initialize Windows Platform Service Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Initialize the store context for the IAP plugin on Windows with lazy initialization. ```rust let store_context = Arc::new(RwLock::new(None)); // Lazy init Ok(Iap { app_handle: app.clone(), store_context }) ``` -------------------------------- ### purchase() Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Initiates a purchase. ```APIDOC ## purchase() ### Description Initiates a purchase. ### Method `purchase(&self, payload: PurchaseRequest) -> Result` ### Parameters #### Path Parameters * **payload** (PurchaseRequest) - Product ID, type, and optional parameters ### Response #### Success Response Result ``` -------------------------------- ### get_products Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Fetches product details from the platform store. This command allows you to retrieve information about specific products, such as their IDs, titles, descriptions, and pricing. ```APIDOC ## get_products ### Description Fetch product details from the platform store. ### Method POST ### Endpoint `plugin:iap|get_products` ### Parameters #### Request Body - **payload** (object) - Required - The request payload for fetching products. - **productIds** (string[]) - Required. Product IDs to fetch. - **productType** (string) - Optional, defaults to "subs". Can be "subs" or "inapp". ### Request Example ```json { "productIds": ["product_id_1", "product_id_2"], "productType": "subs" } ``` ### Response #### Success Response (200) - **products** (array) - An array of product objects. - **productId** (string) - The unique identifier for the product. - **title** (string) - The display name of the product. - **description** (string) - A description of the product. - **productType** (string) - The type of product ('subs' or 'inapp'). - **formattedPrice** (string) - Optional. The price of the product formatted for display. - **priceCurrencyCode** (string) - Optional. The currency code for the product's price. - **priceAmountMicros** (number) - Optional. The price of the product in micro-units (e.g., millionths of the currency unit). - **subscriptionOfferDetails** (array) - Optional (Android only). Details about subscription offers. - **offerToken** (string) - **basePlanId** (string) - **offerId** (string) - Optional. - **pricingPhases** (array) - **formattedPrice** (string) - **priceCurrencyCode** (string) - **priceAmountMicros** (number) - **billingPeriod** (string) - **billingCycleCount** (number) - **recurrenceMode** (number) ### Response Example ```json { "products": [ { "productId": "product_id_1", "title": "Premium Subscription", "description": "Unlock all features with our premium subscription.", "productType": "subs", "formattedPrice": "$9.99", "priceCurrencyCode": "USD", "priceAmountMicros": 9990000, "subscriptionOfferDetails": [ { "offerToken": "offer_token_1", "basePlanId": "base_plan_1", "pricingPhases": [ { "formattedPrice": "$9.99", "priceCurrencyCode": "USD", "priceAmountMicros": 9990000, "billingPeriod": "P1M", "billingCycleCount": 1, "recurrenceMode": 1 } ] } ] } ] } ``` ### Error Scenarios - Platform doesn't support IAP - Network error - Invalid product IDs - Store not initialized ``` -------------------------------- ### initialize Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Initializes the IAP plugin. This command is deprecated and should not be called directly as the plugin initializes automatically at startup. It returns an error instructing users to remove the call. ```APIDOC ## initialize ### Description Deprecated. Returns error instructing to remove the call. Plugin initializes automatically at startup. ### Method GET ### Endpoint `plugin:iap|initialize` ### Parameters None ### Request Example None ### Response #### Success Response (Deprecated) - `success` (boolean) - For backward compatibility only. ### Response Example ```json { "success": true } ``` ### Note Do not call. Plugin initializes automatically at startup. ``` -------------------------------- ### Generate Obfuscated ID using SHA256 Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Example of hashing a user ID with SHA256 and truncating it to 64 characters for use as an obfuscated ID. ```typescript // Example: hash user ID import crypto from 'crypto'; const userId = 'user_123'; const obfuscatedId = crypto .createHash('sha256') .update(userId) .digest('hex') .substring(0, 64); ``` -------------------------------- ### Get Product Status Request Payload Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Defines the structure for requesting the ownership and subscription status of a specific product. The productType can be 'subs' or 'inapp'. ```typescript { productId: string; // Required. Product ID to check. productType: string; // Optional, defaults to "subs". // "subs" or "inapp" } ``` -------------------------------- ### Initialize macOS Platform Service Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Initialize the Swift bridge for the IAP plugin on macOS. ```rust let plugin = ffi::IapPlugin::init_plugin(); // Initialize Swift bridge Ok(Iap { _app: app.clone(), plugin }) ``` -------------------------------- ### Error Response Format Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Illustrates the format for error responses from IAP commands, which are serialized as strings. Examples include platform support errors and initialization issues. ```json "Error message as string" ``` ```json "IAP is not supported on this platform" ``` ```json "Invalid purchase token" ``` ```json "[storeNotInitialized] - Store context not initialized" ``` -------------------------------- ### Initialize Mobile Platform Services Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Register Android and iOS plugins for the IAP service on mobile platforms. ```rust let handle = api.register_android_plugin(...)?; let handle = api.register_ios_plugin(...)?; Ok(Iap(handle)) ``` -------------------------------- ### Rust: Purchase Subscription or In-App Product Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Initiate a purchase for a subscription or in-app product using a `PurchaseRequest`. The `options` field can be used for platform-specific configurations. ```rust // 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?; ``` -------------------------------- ### Tauri Plugin Initialization Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/architecture-and-flow.md The entry point for the Tauri IAP plugin. It registers the plugin, sets up command handlers, initializes platform-specific implementations, and manages plugin state. ```rust pub fn init() -> TauriPlugin ``` -------------------------------- ### Windows Store Context Initialization Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/architecture-and-flow.md Initializes the Windows StoreContext, ensuring multi-window compatibility by associating it with the correct window handle. ```rust fn get_store_context(&self) -> Result { if context_guard.is_none() { let context = StoreContext::GetDefault()?; let hwnd = window.hwnd()?; context.cast::()?. Initialize(hwnd)?; // Unsafe, required by Windows API *context_guard = Some(context); } Ok(context_guard.as_ref()?.clone()) } ``` -------------------------------- ### TypeScript Response for initialize (Legacy) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md The legacy response structure for the `initialize` command, included for backward compatibility. It indicates success with a boolean value. ```typescript { success: true // For backward compatibility only } ``` -------------------------------- ### JavaScript/TypeScript: Listen for Purchase Updates Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Set up a listener to receive real-time updates on purchase status. Remember to unregister the listener when it's no longer needed to prevent memory leaks. ```typescript // Listen for purchase updates const listener = await onPurchaseUpdated((purchase) => { console.log('Purchase updated:', purchase); }); // Stop listening await listener.unregister(); ``` -------------------------------- ### Command Registration in Rust Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Shows how all IAP commands are registered within the Tauri application's `src/lib.rs` file using the Builder pattern. ```rust Builder::new("iap") .invoke_handler(tauri::generate_handler![ commands::initialize, commands::get_products, commands::purchase, commands::restore_purchases, commands::acknowledge_purchase, commands::consume_purchase, commands::get_product_status, #[cfg(desktop)] listeners::register_listener, #[cfg(desktop)] listeners::remove_listener, ]) ``` -------------------------------- ### Low-level invoke for get_products Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Demonstrates how to use the low-level `invoke` function from `@tauri-apps/api/core` to call the `plugin:iap|get_products` command. This method is for advanced use cases. ```typescript import { invoke } from '@tauri-apps/api/core'; // Low-level invoke (for advanced use) const result = await invoke('plugin:iap|get_products', { payload: { productIds: ['product_id'], productType: 'subs' } }); ``` -------------------------------- ### High-level API for getProducts Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Shows the recommended way to fetch product details using the exported `getProducts` function from the `@choochmeque/tauri-plugin-iap-api` package. This is the preferred method for most use cases. ```typescript import { getProducts } from '@choochmeque/tauri-plugin-iap-api'; // High-level (recommended) const result = await getProducts(['product_id'], 'subs'); ``` -------------------------------- ### Windows Purchase Token Synthesis Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/architecture-and-flow.md Defines a structure for synthesizing purchase tokens on Windows, as the Windows Store API does not provide them directly. ```rust struct WindowsPurchaseTokenV1 { v: u8, // Version product_id: String, purchase_time: i64, nonce: u32, } impl WindowsPurchaseTokenV1 { fn encode(&self) -> Result // Base64-encode JSON fn decode(s: &str) -> Result // Base64-decode and validate } ``` -------------------------------- ### Async Runtime Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Explains that all API methods are asynchronous, returning Promises in JavaScript and using `.await` in Rust. ```APIDOC ## Async Runtime All API methods are async: **JavaScript:** All functions return Promise ```typescript const products = await getProducts([...], 'subs'); ``` **Rust:** All methods use `.await` ```rust let products = iap.get_products(ids, product_type).await?; ``` The plugin uses Tauri's async command system, which runs on Tokio for mobile and desktop platforms. ``` -------------------------------- ### JavaScript/TypeScript: Purchase Subscription or In-App Product Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/README.md Initiate a purchase for a subscription or in-app product. Different options are available for specifying offer tokens, fraud prevention details, and app account tokens. ```typescript // 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' }); ``` -------------------------------- ### getProducts() Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Fetches product details from the app store for a given list of product IDs and type. ```APIDOC ## getProducts() ### Description Fetch product details from the app store. ### Method `getProducts` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **productIds** (string[]) - Required - Array of product IDs to fetch from the store - **productType** (string) - Optional - Type of products: "subs" for subscriptions, "inapp" for one-time purchases. Defaults to "subs". ### Request Example ```typescript import { getProducts } from '@choochmeque/tauri-plugin-iap-api'; // Fetch subscription products const { products } = await getProducts( ['premium_monthly', 'premium_yearly'], 'subs' ); products.forEach(product => { console.log(`${product.title}: ${product.formattedPrice}`); if (product.subscriptionOfferDetails) { product.subscriptionOfferDetails.forEach(offer => { console.log(` Offer: ${offer.offerId || 'Default'}`); offer.pricingPhases.forEach(phase => { console.log(` ${phase.formattedPrice} for ${phase.billingPeriod}`); }); }); } }); ``` ### Response #### Success Response - **products** (Product[]) - Array of Product objects with full pricing and offer details #### Response Example (Response structure depends on the `GetProductsResponse` type, which includes an array of `Product` objects. Each `Product` object contains details like title, description, formattedPrice, and potentially subscriptionOfferDetails.) ``` -------------------------------- ### Handle Subscription Upgrades on Android Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Initiate a subscription upgrade on Android, specifying the new offer and the old purchase token. Supports time proration. ```typescript const upgrade = await purchase('premium_yearly', 'subs', { offerToken: newOffer.offerToken, oldPurchaseToken: currentSubscription.purchaseToken, subscriptionReplacementMode: SubscriptionReplacementMode.WITH_TIME_PRORATION }); ``` -------------------------------- ### Windows Purchase Token Structure Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/errors.md Defines the structure for a synthesized purchase token on Windows, as Windows does not natively support transaction tokens. This structure includes version, product ID, purchase time, and a nonce. ```rust struct WindowsPurchaseTokenV1 { v: u8, // Version (1) product_id: String, purchase_time: i64, nonce: u32, } ``` -------------------------------- ### Basic Tauri Plugin Initialization Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Initialize the IAP plugin within your Tauri application's main function. This is the standard way to integrate the plugin. ```rust fn main() { tauri::Builder::default() .plugin(tauri_plugin_iap::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` -------------------------------- ### Listen for Purchase Updates Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/README.md Set up a listener to receive real-time updates on purchase events. Remember to unregister the listener when it's no longer needed to prevent memory leaks. ```typescript const listener = await onPurchaseUpdated((purchase) => { if (purchase.purchaseState === PurchaseState.PURCHASED) { // Grant access } }); // Later: stop listening await listener.unregister(); ``` -------------------------------- ### purchase Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Initiates a purchase transaction for a given product. Supports various optional parameters for different platforms and scenarios. ```APIDOC ## POST / plugin:iap|purchase ### Description Initiate a purchase transaction. ### Method POST ### Endpoint plugin:iap|purchase ### Parameters #### Request Body - **productId** (string) - Required - Product ID. - **productType** (string) - Optional - Defaults to "subs". Can be "subs" or "inapp". - **offerToken** (string) - Optional (Android only). - **obfuscatedAccountId** (string) - Optional (Android only). - **obfuscatedProfileId** (string) - Optional (Android only). - **appAccountToken** (string) - Optional (iOS only). - **oldPurchaseToken** (string) - Optional (Android only). - **subscriptionReplacementMode** (number) - Optional (Android only). ### Request Example ```json { "productId": "example_product_id", "productType": "inapp" } ``` ### Response #### Success Response (200) - **orderId** (string) - May be undefined for pending purchases. - **packageName** (string) - The package name of the application. - **productId** (string) - The ID of the purchased product. - **purchaseTime** (number) - Unix timestamp in milliseconds when the purchase was made. - **purchaseToken** (string) - The token associated with the purchase. - **purchaseState** (number) - The state of the purchase (0=PURCHASED, 1=CANCELED, 2=PENDING). - **isAutoRenewing** (boolean) - Indicates if the subscription is set to auto-renew. - **isAcknowledged** (boolean) - Indicates if the purchase has been acknowledged. - **originalJson** (string) - Android only, usually empty on iOS/macOS. Contains the original purchase data. - **signature** (string) - Android only, usually empty on iOS/macOS. Contains the purchase signature. - **originalId** (string) - iOS/macOS only. Original transaction identifier. - **jwsRepresentation** (string) - iOS/macOS only. JSON Web Signature representation of the purchase. ### Response Example ```json { "orderId": "1234567890", "packageName": "com.example.app", "productId": "example_product_id", "purchaseTime": 1678886400000, "purchaseToken": "a1b2c3d4e5f6", "purchaseState": 0, "isAutoRenewing": true, "isAcknowledged": false, "originalJson": "{}", "signature": "example_signature" } ``` ``` -------------------------------- ### GetProductsRequest Struct (Rust) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/types.md Request parameters for fetching products, specifying product IDs and type. Defaults to 'subs' for product type. ```rust pub struct GetProductsRequest { pub product_ids: Vec, pub product_type: String, // defaults to "subs" } ``` -------------------------------- ### Unsupported Linux Platform Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/platform-specific-behavior.md Illustrates the error returned when attempting to use IAP functionality on an unsupported Linux platform. ```text Error: "IAP is not supported on this platform" ``` -------------------------------- ### PurchaseRequest Struct (Rust) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/types.md Request parameters for initiating a purchase, including product ID, type, and optional purchase options. Defaults to 'subs' for product type. ```rust pub struct PurchaseRequest { pub product_id: String, pub product_type: String, // defaults to "subs" pub options: Option, } ``` -------------------------------- ### Product Interface (TypeScript) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/types.md Defines the structure for product information retrieved from app stores. Includes details like ID, title, description, price, and subscription offers. ```typescript export interface Product { productId: string; title: string; description: string; productType: string; formattedPrice?: string; priceCurrencyCode?: string; priceAmountMicros?: number; subscriptionOfferDetails?: SubscriptionOffer[]; } ``` -------------------------------- ### Purchase Product (Rust) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Initiates a purchase for a given product. This method is accessed via the IapExt trait and requires a PurchaseRequest payload. ```rust pub async fn purchase(&self, payload: PurchaseRequest) -> Result ``` -------------------------------- ### Listener Infrastructure (Desktop) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Manages custom listener systems for desktop platforms (macOS, Windows, Linux) as Tauri's plugin listener API is mobile-only. ```APIDOC ## Listener Infrastructure (Desktop) Desktop platforms (macOS, Windows, Linux) use a custom listener system since Tauri's plugin listener API is mobile-only. **Listener Management:** ```rust // Initialize listeners at plugin setup #[cfg(desktop)] listeners::init(); // Register listener for an event register_listener(event: String, handler: Channel) -> Result<()> // Trigger event to all listeners pub fn trigger(event: &str, payload: &str) -> Result<()> // Remove listener by ID remove_listener(event: String, channel_id: u32) -> Result<()> ``` **Source:** `src/listeners.rs` **Usage in JavaScript:** ```typescript import { onPurchaseUpdated } from '@choochmeque/tauri-plugin-iap-api'; const listener = await onPurchaseUpdated((purchase) => { console.log('Purchase updated:', purchase); }); // Stop listening await listener.unregister(); ``` ``` -------------------------------- ### Safely Handle Platform Unavailability Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/errors.md This function attempts to perform a purchase, but catches specific errors indicating that the IAP service is not supported on the current platform. If unsupported, it logs a warning and returns null, allowing for alternative monetization strategies. ```typescript async function safePurchase(productId: string) { try { return await purchase(productId, 'subs'); } catch (error) { if (error instanceof Error && error.message.includes('not supported')) { console.warn('IAP not available; showing alternative monetization'); return null; } throw error; } } ``` -------------------------------- ### Product Struct (Rust) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/types.md Rust equivalent of the Product interface, used for server-side or native handling of product data. Supports optional fields for pricing and subscription details. ```rust pub struct Product { pub product_id: String, pub title: String, pub description: String, pub product_type: String, pub formatted_price: Option, pub price_currency_code: Option, pub price_amount_micros: Option, pub subscription_offer_details: Option>, } ``` -------------------------------- ### Enable IAP Permissions in capabilities.json Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md Configure default IAP permissions by adding 'iap:default' to your capabilities file. This grants access to all IAP operations. ```json { "permissions": [ "iap:default" ] } ``` -------------------------------- ### Fetch Subscription Products Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/api-reference-javascript.md Fetches details for specified subscription products from the app store. Ensure the product IDs are correctly formatted and available in your store configuration. ```typescript import { getProducts } from '@choochmeque/tauri-plugin-iap-api'; // Fetch subscription products const { products } = await getProducts( ['premium_monthly', 'premium_yearly'], 'subs' ); products.forEach(product => { console.log(`${product.title}: ${product.formattedPrice}`); if (product.subscriptionOfferDetails) { product.subscriptionOfferDetails.forEach(offer => { console.log(` Offer: ${offer.offerId || 'Default'}`); offer.pricingPhases.forEach(phase => { console.log(` ${phase.formattedPrice} for ${phase.billingPeriod}`); }); }); } }); ``` -------------------------------- ### macOS Interface Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/architecture-and-flow.md The In-App Purchase plugin interface for macOS, using a Swift-Rust FFI bridge and integrating with StoreKit 2 APIs. ```APIDOC ## `Iap` - macOS ### Description Provides methods to interact with in-app purchase functionalities on macOS using StoreKit 2. ### Methods - `get_products(&self, ...) -> Result`: Retrieves product information. - `purchase(&self, payload: PurchaseRequest) -> Result`: Initiates a purchase. ``` -------------------------------- ### Set MACOSX_DEPLOYMENT_TARGET for Development Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/plugin-initialization.md When running `tauri dev` on macOS, set the MACOSX_DEPLOYMENT_TARGET environment variable to avoid 'Requires .app bundle' errors. ```bash MACOSX_DEPLOYMENT_TARGET="13.0" pnpm tauri dev ``` -------------------------------- ### Rust Command Handler: get_products Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md The `get_products` command handler in Rust, responsible for fetching product details from the platform store. It takes a `GetProductsRequest` payload and returns a `GetProductsResponse`. ```rust #[command] pub async fn get_products( app: AppHandle, payload: GetProductsRequest ) -> Result ``` -------------------------------- ### SubscriptionOffer Interface (TypeScript) Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/types.md Details a specific subscription offer, including its token, base plan ID, and an array of pricing phases. Used for purchasing offers. ```typescript export interface SubscriptionOffer { offerToken: string; basePlanId: string; offerId?: string; pricingPhases: PricingPhase[]; } ``` -------------------------------- ### get_product_status Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md Check ownership and subscription status of a specific product. This command is available on iOS, macOS, Android, and Windows. ```APIDOC ## POST / async get_product_status ### Description Check ownership and subscription status of a specific product. ### Method POST / async ### Endpoint plugin:iap|get_product_status ### Parameters #### Request Body - **productId** (string) - Required - Product ID to check. - **productType** (string) - Optional - Defaults to "subs". Can be "subs" or "inapp". ### Request Example ```json { "productId": "some_product_id", "productType": "subs" } ``` ### Response #### Success Response (200) - **productId** (string) - The ID of the product. - **isOwned** (boolean) - Whether the user owns the product. - **purchaseState** (number) - Optional. 0=PURCHASED, 1=CANCELED, 2=PENDING. - **purchaseTime** (number) - Optional. Unix milliseconds when the purchase was made. - **expirationTime** (number) - Optional. Unix milliseconds when the subscription expires. - **isAutoRenewing** (boolean) - Optional. Whether the subscription is set to auto-renew. - **isAcknowledged** (boolean) - Optional. Whether the purchase has been acknowledged. - **purchaseToken** (string) - Optional. The token associated with the purchase. All optional fields may be undefined if `isOwned` is false. #### Response Example ```json { "productId": "premium_monthly", "isOwned": true, "purchaseState": 0, "purchaseTime": 1678886400000, "expirationTime": 1710422400000, "isAutoRenewing": true, "isAcknowledged": true, "purchaseToken": "a1b2c3d4e5f6" } ``` ### Error Handling - Network error - Invalid product ID - Platform doesn't support status check ``` -------------------------------- ### Rust Command Handler: initialize Source: https://github.com/choochmeque/tauri-plugin-iap/blob/main/_autodocs/command-reference.md The `initialize` command handler in Rust. This command is deprecated and should not be called, as the plugin initializes automatically. ```rust #[command] pub async fn initialize(_app: AppHandle) -> Result ```