# API Integration Guide for RhaiLib ## Quick Start This guide shows you how to integrate external APIs with Rhai scripts using RhaiLib's async architecture. ## Table of Contents 1. [Setup and Configuration](#setup-and-configuration) 2. [Basic API Calls](#basic-api-calls) 3. [Stripe Payment Integration](#stripe-payment-integration) 4. [Error Handling Patterns](#error-handling-patterns) 5. [Advanced Usage](#advanced-usage) 6. [Extending to Other APIs](#extending-to-other-apis) ## Setup and Configuration ### 1. Environment Variables Create a `.env` file in your project: ```bash # .env STRIPE_SECRET_KEY=sk_test_your_stripe_key_here STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key_here ``` ### 2. Rust Setup ```rust use rhailib_dsl::payment::register_payment_rhai_module; use rhai::{Engine, EvalAltResult, Scope}; use std::env; fn main() -> Result<(), Box> { // Load environment variables dotenv::from_filename(".env").ok(); // Create Rhai engine and register payment module let mut engine = Engine::new(); register_payment_rhai_module(&mut engine); // Set up scope with API credentials let mut scope = Scope::new(); let stripe_key = env::var("STRIPE_SECRET_KEY").unwrap(); scope.push("STRIPE_API_KEY", stripe_key); // Execute your Rhai script let script = std::fs::read_to_string("payment_script.rhai")?; engine.eval_with_scope::<()>(&mut scope, &script)?; Ok(()) } ``` ### 3. Rhai Script Configuration ```rhai // Configure the API client let config_result = configure_stripe(STRIPE_API_KEY); print(`Configuration: ${config_result}`); ``` ## Basic API Calls ### Simple Product Creation ```rhai // Create a basic product let product = new_product() .name("My Product") .description("A great product"); try { let product_id = product.create(); print(`✅ Created product: ${product_id}`); } catch(error) { print(`❌ Error: ${error}`); } ``` ### Price Configuration ```rhai // One-time payment price let one_time_price = new_price() .amount(1999) // $19.99 in cents .currency("usd") .product(product_id); let price_id = one_time_price.create(); // Subscription price let monthly_price = new_price() .amount(999) // $9.99 in cents .currency("usd") .product(product_id) .recurring("month"); let monthly_price_id = monthly_price.create(); ``` ## Stripe Payment Integration ### Complete Payment Workflow ```rhai // 1. Configure Stripe configure_stripe(STRIPE_API_KEY); // 2. Create Product let product = new_product() .name("Premium Software License") .description("Professional software solution") .metadata("category", "software") .metadata("tier", "premium"); let product_id = product.create(); // 3. Create Pricing Options let monthly_price = new_price() .amount(2999) // $29.99 .currency("usd") .product(product_id) .recurring("month") .metadata("billing", "monthly"); let annual_price = new_price() .amount(29999) // $299.99 (save $60) .currency("usd") .product(product_id) .recurring("year") .metadata("billing", "annual") .metadata("discount", "save_60"); let monthly_price_id = monthly_price.create(); let annual_price_id = annual_price.create(); // 4. Create Discount Coupons let welcome_coupon = new_coupon() .duration("once") .percent_off(25) .metadata("campaign", "welcome_offer"); let coupon_id = welcome_coupon.create(); // 5. Create Payment Intent for One-time Purchase let payment_intent = new_payment_intent() .amount(2999) .currency("usd") .customer("cus_customer_id") .description("Monthly subscription payment") .add_payment_method_type("card") .metadata("price_id", monthly_price_id); let intent_id = payment_intent.create(); // 6. Create Subscription let subscription = new_subscription() .customer("cus_customer_id") .add_price(monthly_price_id) .trial_days(14) .coupon(coupon_id) .metadata("source", "website"); let subscription_id = subscription.create(); ``` ### Builder Pattern Examples #### Product with Metadata ```rhai let product = new_product() .name("Enterprise Software") .description("Full-featured business solution") .metadata("category", "enterprise") .metadata("support_level", "premium") .metadata("deployment", "cloud"); ``` #### Complex Pricing ```rhai let tiered_price = new_price() .amount(4999) // $49.99 .currency("usd") .product(product_id) .recurring_with_count("month", 12) // 12 monthly payments .metadata("tier", "professional") .metadata("features", "advanced"); ``` #### Multi-item Subscription ```rhai let enterprise_subscription = new_subscription() .customer("cus_enterprise_customer") .add_price_with_quantity(user_license_price_id, 50) // 50 user licenses .add_price(support_addon_price_id) // Premium support .add_price(analytics_addon_price_id) // Analytics addon .trial_days(30) .metadata("plan", "enterprise") .metadata("contract_length", "annual"); ``` ## Error Handling Patterns ### Basic Error Handling ```rhai try { let result = some_api_call(); print(`Success: ${result}`); } catch(error) { print(`Error occurred: ${error}`); // Continue with fallback logic } ``` ### Graceful Degradation ```rhai // Try to create with coupon, fallback without coupon let subscription_id; try { subscription_id = new_subscription() .customer(customer_id) .add_price(price_id) .coupon(coupon_id) .create(); } catch(error) { print(`Coupon failed: ${error}, creating without coupon`); subscription_id = new_subscription() .customer(customer_id) .add_price(price_id) .create(); } ``` ### Validation Before API Calls ```rhai // Validate inputs before making API calls if customer_id == "" { print("❌ Customer ID is required"); return; } if price_id == "" { print("❌ Price ID is required"); return; } // Proceed with API call let subscription = new_subscription() .customer(customer_id) .add_price(price_id) .create(); ``` ## Advanced Usage ### Conditional Logic ```rhai // Different pricing based on customer type let price_id; if customer_type == "enterprise" { price_id = enterprise_price_id; } else if customer_type == "professional" { price_id = professional_price_id; } else { price_id = standard_price_id; } let subscription = new_subscription() .customer(customer_id) .add_price(price_id); // Add trial for new customers if is_new_customer { subscription = subscription.trial_days(14); } let subscription_id = subscription.create(); ``` ### Dynamic Metadata ```rhai // Build metadata dynamically let product = new_product() .name(product_name) .description(product_description); // Add metadata based on conditions if has_support { product = product.metadata("support", "included"); } if is_premium { product = product.metadata("tier", "premium"); } if region != "" { product = product.metadata("region", region); } let product_id = product.create(); ``` ### Bulk Operations ```rhai // Create multiple prices for a product let price_configs = [ #{amount: 999, interval: "month", name: "Monthly"}, #{amount: 9999, interval: "year", name: "Annual"}, #{amount: 19999, interval: "", name: "Lifetime"} ]; let price_ids = []; for config in price_configs { let price = new_price() .amount(config.amount) .currency("usd") .product(product_id) .metadata("plan_name", config.name); if config.interval != "" { price = price.recurring(config.interval); } let price_id = price.create(); price_ids.push(price_id); print(`Created ${config.name} price: ${price_id}`); } ``` ## Extending to Other APIs ### Adding New API Support To extend the architecture to other APIs, follow this pattern: #### 1. Define Configuration Structure ```rust #[derive(Debug, Clone)] pub struct CustomApiConfig { pub api_key: String, pub base_url: String, pub client: Client, } ``` #### 2. Implement Request Handler ```rust async fn handle_custom_api_request( config: &CustomApiConfig, request: &AsyncRequest ) -> Result { let url = format!("{}/{}", config.base_url, request.endpoint); let response = config.client .request(Method::from_str(&request.method).unwrap(), &url) .header("Authorization", format!("Bearer {}", config.api_key)) .json(&request.data) .send() .await .map_err(|e| format!("Request failed: {}", e))?; let response_text = response.text().await .map_err(|e| format!("Failed to read response: {}", e))?; Ok(response_text) } ``` #### 3. Register Rhai Functions ```rust #[rhai_fn(name = "custom_api_call", return_raw)] pub fn custom_api_call( endpoint: String, data: rhai::Map ) -> Result> { let registry = CUSTOM_API_REGISTRY.lock().unwrap(); let registry = registry.as_ref().ok_or("API not configured")?; let form_data: HashMap = data.into_iter() .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); registry.make_request(endpoint, "POST".to_string(), form_data) .map_err(|e| e.to_string().into()) } ``` ### Example: GitHub API Integration ```rhai // Hypothetical GitHub API integration configure_github_api(GITHUB_TOKEN); // Create a repository let repo_data = #{ name: "my-new-repo", description: "Created via Rhai script", private: false }; let repo_result = github_api_call("user/repos", repo_data); print(`Repository created: ${repo_result}`); // Create an issue let issue_data = #{ title: "Initial setup", body: "Setting up the repository structure", labels: ["enhancement", "setup"] }; let issue_result = github_api_call("repos/user/my-new-repo/issues", issue_data); print(`Issue created: ${issue_result}`); ``` ## Performance Tips ### 1. Batch Operations ```rhai // Instead of creating items one by one, batch when possible let items_to_create = [item1, item2, item3]; let created_items = []; for item in items_to_create { try { let result = item.create(); created_items.push(result); } catch(error) { print(`Failed to create item: ${error}`); } } ``` ### 2. Reuse Configuration ```rhai // Configure once, use multiple times configure_stripe(STRIPE_API_KEY); // Multiple operations use the same configuration let product1_id = new_product().name("Product 1").create(); let product2_id = new_product().name("Product 2").create(); let price1_id = new_price().product(product1_id).amount(1000).create(); let price2_id = new_price().product(product2_id).amount(2000).create(); ``` ### 3. Error Recovery ```rhai // Implement retry logic for transient failures let max_retries = 3; let retry_count = 0; let success = false; while retry_count < max_retries && !success { try { let result = api_operation(); success = true; print(`Success: ${result}`); } catch(error) { retry_count += 1; print(`Attempt ${retry_count} failed: ${error}`); if retry_count < max_retries { print("Retrying..."); } } } if !success { print("❌ All retry attempts failed"); } ``` ## Debugging and Monitoring ### Enable Detailed Logging ```rhai // The architecture automatically logs key operations: // 🔧 Configuring Stripe... // 🚀 Async worker thread started // 🔄 Processing POST request to products // 📥 Stripe response: {...} // ✅ Request successful with ID: prod_xxx ``` ### Monitor Request Performance ```rhai // Time API operations let start_time = timestamp(); let result = expensive_api_operation(); let end_time = timestamp(); print(`Operation took ${end_time - start_time}ms`); ``` ### Handle Rate Limits ```rhai // Implement backoff for rate-limited APIs try { let result = api_call(); } catch(error) { if error.contains("rate limit") { print("Rate limited, waiting before retry..."); // In a real implementation, you'd add delay logic } } ``` ## Best Practices Summary 1. **Always handle errors gracefully** - Use try/catch blocks for all API calls 2. **Validate inputs** - Check required fields before making API calls 3. **Use meaningful metadata** - Add context to help with debugging and analytics 4. **Configure once, use many** - Set up API clients once and reuse them 5. **Implement retry logic** - Handle transient network failures 6. **Monitor performance** - Track API response times and success rates 7. **Secure credentials** - Use environment variables for API keys 8. **Test with demo data** - Use test API keys during development This architecture provides a robust foundation for integrating any HTTP-based API with Rhai scripts while maintaining the simplicity and safety that makes Rhai attractive for domain-specific scripting.