diff --git a/examples/freezone/freezone copy 2.rhai b/examples/freezone/freezone copy 2.rhai new file mode 100644 index 0000000..f0ccf0e --- /dev/null +++ b/examples/freezone/freezone copy 2.rhai @@ -0,0 +1,360 @@ + + + + + + + + + + + +flow = registration_flow(); + +public_key_registration_step = step.new() + .title("Public Key Registration") + .description("Register your public key") + .add_action("Register"); + + +public_key_registration_step.complete() + +public_key = +print("Public key <>registered."); +print("Now user needs to submit email"); + + + + + + +public_key_registration_step.continue() + + + + + + +user_profile_creation_step = user_profile_creation(); +flow.add_step(user_profile_creation_step); + +step = email_verification(); +flow.add_step(step); + +step = terms_and_conditions(); +flow.add_step(step); + +step = crypto_wallet_creation(); +flow.add_step(payment_processing()); +flow.add_step(kyc_verification()); + +flow.execute(); + +print("Step 1: Public Key Registration"); +print("─────────────────────────────────────────────────────────────"); + +// User (Timur) provides their public key +print("User public key received: " + timur_pubkey); +print("✓ Public key validated and stored\n"); + +// ============================================================================ +// STEP 2: User Profile Creation & Email Verification +// ============================================================================ + +print("Step 2: User Profile Creation & Email Verification"); +print("─────────────────────────────────────────────────────────────"); + +// Collect basic user information +let user_name = "Timur Gordon"; +let user_email = "timur@freezone.example"; +print("Collecting user information:"); +print(" Name: " + user_name); +print(" Email: " + user_email); + +// Create user profile +let user_profile = new_user() + .username(user_name) + .pubkey(timur_pubkey) + .add_email(user_email); + +print("✓ User profile created"); +freezone_ctx.save(user_profile); +print("✓ Profile saved"); + +// Email Verification +print("\nInitiating email verification..."); +let verification = new_verification("user_1", user_email); + +print("✓ Email verification created"); +print(" Verification code: " + verification.get_code()); +print(" Nonce: " + verification.get_nonce()); + +// Prepare verification email +let verification_link = "https://freezone.example/verify?nonce=" + verification.get_nonce(); + +// Create verification mail with template parameters +let verification_mail = new_mail() + .to(user_email) + .template(verification_mail_template.get_id()) + .parameter("url", verification_link) + .parameter("code", verification.get_code()); + +print("✓ Verification email prepared"); +print(" To: " + user_email); +print(" Template: " + verification_mail_template.get_id()); +print(" Link: " + verification_link); +print(" Code: " + verification.get_code()); + +// Send verification email using template +// Note: In production, configure real SMTP credentials +// For now, we'll simulate sending (actual SMTP would fail with example.com) +print("✓ Verification email would be sent to: " + user_email); +print(" (Skipping actual SMTP send - configure real server in production)"); + +// Simulate user clicking verification link and verifying +print("\n✓ User clicks verification link and verifies email"); +verification.verify_nonce(verification.get_nonce()); +print("✓ Email verified: " + verification.get_status()); + +freezone_ctx.save(verification); +print("✓ Verification saved\n"); + +// ============================================================================ +// STEP 3: Terms & Conditions Signing +// ============================================================================ + +print("Step 3: Terms & Conditions Signing"); +print("─────────────────────────────────────────────────────────────"); + +// Create Terms & Conditions contract +let terms_contract = new_contract(1) + .title("Freezone Membership Terms & Conditions") + .content("By signing this agreement, you agree to abide by all freezone rules and regulations...") + .creator_id(999); // Freezone admin + +print("✓ Terms & Conditions contract created"); +print(" Title: " + terms_contract.title()); + +// User signs the contract (add their signature ID) +let user_signature_id = 1001; +terms_contract = terms_contract.add_signature(user_signature_id); +print("✓ User signature added (ID: " + user_signature_id + ")"); + +// Activate contract once signed +if terms_contract.is_fully_signed(1) { + terms_contract = terms_contract.activate(); + print("✓ Contract activated: " + terms_contract.status()); +} + +freezone_ctx.save(terms_contract); +print("✓ Signed contract saved\n"); + +// ============================================================================ +// STEP 4: Crypto Wallet Creation +// ============================================================================ + +print("Step 4: Crypto Wallet Creation"); +print("─────────────────────────────────────────────────────────────"); + +// Create TFT crypto account for user +let tft_account = new_account() + .owner_id(1) + .currency("TFT") // ThreeFold Token + .balance(0.0); + +print("✓ TFT account created"); +print(" Owner ID: 1"); +print(" Currency: TFT"); +print(" Initial balance: 0"); + +freezone_ctx.save(tft_account); + +// Create Ethereum wallet for user +print("\nCreating Ethereum wallet..."); +let eth_wallet = new_ethereum_wallet() + .owner_id(1) + .network("mainnet"); + +print("✓ Ethereum wallet created"); +print(" Address: " + eth_wallet.get_address()); +print(" Network: mainnet"); +print(" Balance: 0 ETH"); + +// Save as account +let eth_account = new_account() + .owner_id(1) + .address(eth_wallet.get_address()) + .currency("ETH") + .balance(0.0); + +freezone_ctx.save(eth_account); +print("✓ Ethereum account saved\n"); + +// ============================================================================ +// STEP 5: Payment Processing +// ============================================================================ + +print("Step 5: Payment Processing"); +print("─────────────────────────────────────────────────────────────"); + +print("Using configured Pesapal payment client..."); + +// Create payment request for registration fee +print("\nCreating payment session..."); +let payment_request = new_payment_request() + .amount(100.0) + .currency("USD") + .description("Freezone Registration Fee") + .callback_url("https://freezone.example/payment/callback") + .merchant_reference("REG_USER_1_" + timestamp()); + +print("✓ Payment request created"); +print(" Amount: $100 USD"); +print(" Description: Freezone Registration Fee"); + +// Initiate payment with Pesapal - creates real payment link +print("\nInitiating payment session with Pesapal..."); +let payment_response = freezone_payment_client.create_payment_link(payment_request); + +let payment_url = payment_response.get_payment_url(); +let order_tracking_id = payment_response.get_order_tracking_id(); + +print("✓ Payment session created"); +print(" Payment URL: " + payment_url); +print(" Order Tracking ID: " + order_tracking_id); +print(" (User would be redirected to Pesapal payment page)"); + +// In production, you would: +// 1. Redirect user to payment_url +// 2. User completes payment on Pesapal +// 3. Pesapal calls your callback_url +// 4. You verify payment status with get_payment_status(order_tracking_id) + +// For demo purposes, check payment status +print("\nChecking payment status..."); +let payment_status = freezone_payment_client.get_payment_status(order_tracking_id); + +print("✓ Payment status retrieved"); +print(" Status: " + payment_status.get_payment_status_description()); +print(" Amount: " + payment_status.get_amount() + " " + payment_status.get_currency()); +print(" Transaction ID: " + order_tracking_id); + +// Create payment transaction record +let payment_tx = new_transaction() + .source(0) // External payment + .destination(1) // Freezone account + .amount(100.0) + .assetid(1); + +print("✓ Payment transaction recorded"); + +freezone_ctx.save(payment_tx); +print("✓ Transaction saved\n"); + +// ============================================================================ +// STEP 6: KYC Verification +// ============================================================================ + +print("Step 6: KYC Verification"); +print("─────────────────────────────────────────────────────────────"); + +// Pre-fill KYC form with already collected info (first name, last name) +// User will fill in the rest at Idenfy (DOB, address, nationality, etc.) +print("\nPreparing KYC session with pre-filled data..."); +let kyc_info = new_kyc_info() + .client_id("user_1") + .first_name("Timur") // From user profile + .last_name("Gordon") // From user profile + .email(user_email); // From user profile + +print("✓ Pre-fill data prepared"); +print(" Name: Timur Gordon (pre-filled)"); +print(" Email: " + user_email + " (pre-filled)"); +print(" User will provide: DOB, address, nationality, ID document"); + +// Create KYC session +let kyc_session = new_kyc_session("user_1", "idenfy") + .callback_url("https://freezone.example/kyc/callback") + .success_url("https://freezone.example/kyc/success") + .error_url("https://freezone.example/kyc/error") + .locale("en"); + +print("✓ KYC session created"); +print(" Client ID: " + kyc_session.get_client_id()); +print(" Provider: " + kyc_session.get_provider()); + +// Create verification session with Idenfy - generates real verification URL +print("\nCreating Idenfy verification session..."); +let verification_url = freezone_kyc_client.create_verification_session(kyc_info, kyc_session); + +print("✓ Idenfy verification session created"); +print(" Verification URL: " + verification_url); +print(" (User redirected to Idenfy to complete verification)"); + +// Production flow: +// 1. Redirect user to verification_url +// 2. User fills in remaining info (DOB, address, nationality) +// 3. User uploads ID document (Passport/ID/Driver's License) +// 4. User takes selfie for liveness check +// 5. Idenfy performs automated verification +// 6. Idenfy calls callback_url with VERIFIED data from ID document +// 7. Callback handler stores verified KYC info + +print("\n✓ User completes verification at Idenfy:"); +print(" - Fills in DOB, address, nationality"); +print(" - Uploads identity document"); +print(" - Takes selfie for liveness check"); +print(" - Idenfy extracts and verifies data from ID"); + +print("\n✓ Idenfy callback will provide VERIFIED data:"); +print(" - All personal info extracted from ID document"); +print(" - Document number, issue date, expiry date"); +print(" - Verification status (APPROVED/DENIED)"); +print(" - Liveness check result"); + +// For demo purposes, simulate what callback would receive +print("\nSimulating callback data (in production, this comes from Idenfy)..."); +let kyc_info_verified = new_kyc_info() + .client_id("user_1") + .first_name("Timur") // Verified from ID + .last_name("Gordon") // Verified from ID + .email(user_email) + .date_of_birth("1990-05-15") // Extracted from ID + .nationality("US") // Extracted from ID + .document_type("passport") // From verification + .document_number("P123456789") // Extracted from ID + .provider("idenfy") + .verified(true); // Only if status == "APPROVED" + +freezone_ctx.save(kyc_info_verified); +freezone_ctx.save(kyc_session); +print("✓ KYC verification data saved"); +print("✓ KYC verification completed\n"); + +// ============================================================================ +// SUMMARY +// ============================================================================ + +print("═══════════════════════════════════════════════════════════════"); +print("REGISTRATION COMPLETE"); +print("═══════════════════════════════════════════════════════════════"); +print("\nUser Summary:"); +print(" Name: " + user_name); +print(" Email: " + user_email); +print(" Public Key: " + timur_pubkey); +print(" TFT Account: Created"); +print(" ETH Account: Created"); +print(" KYC Status: Verified"); +print(" Payment Status: Completed ($100 USD)"); +print(" Contract: Signed and Active"); +print("\nRegistration Flow:"); +print(" ✓ Freezone initialization (Email, Payment, KYC providers configured)"); +print(" ✓ Freezone Ethereum wallet created"); +print(" ✓ Public key registration"); +print(" ✓ User profile creation & email verification"); +print(" ✓ Terms & Conditions signed"); +print(" ✓ Crypto wallets created (TFT + ETH)"); +print(" ✓ Payment processed ($100 USD)"); +print(" ✓ KYC verification completed with verified info"); +print("\n" + user_name + " is now a verified Freezone member!"); +print("═══════════════════════════════════════════════════════════════\n"); diff --git a/examples/freezone/freezone copy.rhai b/examples/freezone/freezone copy.rhai new file mode 100644 index 0000000..ef8e354 --- /dev/null +++ b/examples/freezone/freezone copy.rhai @@ -0,0 +1,370 @@ +registration_flow = flow.new(); + +public_key_registration_step = step.new() + .title("Public Key Registration") + .description("Register your public key") + .add_action("Register"); + +flow_id = registration_flow.start() + + + + + + + + + + + + + + + + + + + + + +public_key_registration_step.complete() + +public_key = +print("Public key <>registered."); +print("Now user needs to submit email"); + + + + + + +public_key_registration_step.continue() + + + + + + +user_profile_creation_step = user_profile_creation(); +flow.add_step(user_profile_creation_step); + +step = email_verification(); +flow.add_step(step); + +step = terms_and_conditions(); +flow.add_step(step); + +step = crypto_wallet_creation(); +flow.add_step(payment_processing()); +flow.add_step(kyc_verification()); + +flow.execute(); + +print("Step 1: Public Key Registration"); +print("─────────────────────────────────────────────────────────────"); + +// User (Timur) provides their public key +print("User public key received: " + timur_pubkey); +print("✓ Public key validated and stored\n"); + +// ============================================================================ +// STEP 2: User Profile Creation & Email Verification +// ============================================================================ + +print("Step 2: User Profile Creation & Email Verification"); +print("─────────────────────────────────────────────────────────────"); + +// Collect basic user information +let user_name = "Timur Gordon"; +let user_email = "timur@freezone.example"; +print("Collecting user information:"); +print(" Name: " + user_name); +print(" Email: " + user_email); + +// Create user profile +let user_profile = new_user() + .username(user_name) + .pubkey(timur_pubkey) + .add_email(user_email); + +print("✓ User profile created"); +freezone_ctx.save(user_profile); +print("✓ Profile saved"); + +// Email Verification +print("\nInitiating email verification..."); +let verification = new_verification("user_1", user_email); + +print("✓ Email verification created"); +print(" Verification code: " + verification.get_code()); +print(" Nonce: " + verification.get_nonce()); + +// Prepare verification email +let verification_link = "https://freezone.example/verify?nonce=" + verification.get_nonce(); + +// Create verification mail with template parameters +let verification_mail = new_mail() + .to(user_email) + .template(verification_mail_template.get_id()) + .parameter("url", verification_link) + .parameter("code", verification.get_code()); + +print("✓ Verification email prepared"); +print(" To: " + user_email); +print(" Template: " + verification_mail_template.get_id()); +print(" Link: " + verification_link); +print(" Code: " + verification.get_code()); + +// Send verification email using template +// Note: In production, configure real SMTP credentials +// For now, we'll simulate sending (actual SMTP would fail with example.com) +print("✓ Verification email would be sent to: " + user_email); +print(" (Skipping actual SMTP send - configure real server in production)"); + +// Simulate user clicking verification link and verifying +print("\n✓ User clicks verification link and verifies email"); +verification.verify_nonce(verification.get_nonce()); +print("✓ Email verified: " + verification.get_status()); + +freezone_ctx.save(verification); +print("✓ Verification saved\n"); + +// ============================================================================ +// STEP 3: Terms & Conditions Signing +// ============================================================================ + +print("Step 3: Terms & Conditions Signing"); +print("─────────────────────────────────────────────────────────────"); + +// Create Terms & Conditions contract +let terms_contract = new_contract(1) + .title("Freezone Membership Terms & Conditions") + .content("By signing this agreement, you agree to abide by all freezone rules and regulations...") + .creator_id(999); // Freezone admin + +print("✓ Terms & Conditions contract created"); +print(" Title: " + terms_contract.title()); + +// User signs the contract (add their signature ID) +let user_signature_id = 1001; +terms_contract = terms_contract.add_signature(user_signature_id); +print("✓ User signature added (ID: " + user_signature_id + ")"); + +// Activate contract once signed +if terms_contract.is_fully_signed(1) { + terms_contract = terms_contract.activate(); + print("✓ Contract activated: " + terms_contract.status()); +} + +freezone_ctx.save(terms_contract); +print("✓ Signed contract saved\n"); + +// ============================================================================ +// STEP 4: Crypto Wallet Creation +// ============================================================================ + +print("Step 4: Crypto Wallet Creation"); +print("─────────────────────────────────────────────────────────────"); + +// Create TFT crypto account for user +let tft_account = new_account() + .owner_id(1) + .currency("TFT") // ThreeFold Token + .balance(0.0); + +print("✓ TFT account created"); +print(" Owner ID: 1"); +print(" Currency: TFT"); +print(" Initial balance: 0"); + +freezone_ctx.save(tft_account); + +// Create Ethereum wallet for user +print("\nCreating Ethereum wallet..."); +let eth_wallet = new_ethereum_wallet() + .owner_id(1) + .network("mainnet"); + +print("✓ Ethereum wallet created"); +print(" Address: " + eth_wallet.get_address()); +print(" Network: mainnet"); +print(" Balance: 0 ETH"); + +// Save as account +let eth_account = new_account() + .owner_id(1) + .address(eth_wallet.get_address()) + .currency("ETH") + .balance(0.0); + +freezone_ctx.save(eth_account); +print("✓ Ethereum account saved\n"); + +// ============================================================================ +// STEP 5: Payment Processing +// ============================================================================ + +print("Step 5: Payment Processing"); +print("─────────────────────────────────────────────────────────────"); + +print("Using configured Pesapal payment client..."); + +// Create payment request for registration fee +print("\nCreating payment session..."); +let payment_request = new_payment_request() + .amount(100.0) + .currency("USD") + .description("Freezone Registration Fee") + .callback_url("https://freezone.example/payment/callback") + .merchant_reference("REG_USER_1_" + timestamp()); + +print("✓ Payment request created"); +print(" Amount: $100 USD"); +print(" Description: Freezone Registration Fee"); + +// Initiate payment with Pesapal - creates real payment link +print("\nInitiating payment session with Pesapal..."); +let payment_response = freezone_payment_client.create_payment_link(payment_request); + +let payment_url = payment_response.get_payment_url(); +let order_tracking_id = payment_response.get_order_tracking_id(); + +print("✓ Payment session created"); +print(" Payment URL: " + payment_url); +print(" Order Tracking ID: " + order_tracking_id); +print(" (User would be redirected to Pesapal payment page)"); + +// In production, you would: +// 1. Redirect user to payment_url +// 2. User completes payment on Pesapal +// 3. Pesapal calls your callback_url +// 4. You verify payment status with get_payment_status(order_tracking_id) + +// For demo purposes, check payment status +print("\nChecking payment status..."); +let payment_status = freezone_payment_client.get_payment_status(order_tracking_id); + +print("✓ Payment status retrieved"); +print(" Status: " + payment_status.get_payment_status_description()); +print(" Amount: " + payment_status.get_amount() + " " + payment_status.get_currency()); +print(" Transaction ID: " + order_tracking_id); + +// Create payment transaction record +let payment_tx = new_transaction() + .source(0) // External payment + .destination(1) // Freezone account + .amount(100.0) + .assetid(1); + +print("✓ Payment transaction recorded"); + +freezone_ctx.save(payment_tx); +print("✓ Transaction saved\n"); + +// ============================================================================ +// STEP 6: KYC Verification +// ============================================================================ + +print("Step 6: KYC Verification"); +print("─────────────────────────────────────────────────────────────"); + +// Pre-fill KYC form with already collected info (first name, last name) +// User will fill in the rest at Idenfy (DOB, address, nationality, etc.) +print("\nPreparing KYC session with pre-filled data..."); +let kyc_info = new_kyc_info() + .client_id("user_1") + .first_name("Timur") // From user profile + .last_name("Gordon") // From user profile + .email(user_email); // From user profile + +print("✓ Pre-fill data prepared"); +print(" Name: Timur Gordon (pre-filled)"); +print(" Email: " + user_email + " (pre-filled)"); +print(" User will provide: DOB, address, nationality, ID document"); + +// Create KYC session +let kyc_session = new_kyc_session("user_1", "idenfy") + .callback_url("https://freezone.example/kyc/callback") + .success_url("https://freezone.example/kyc/success") + .error_url("https://freezone.example/kyc/error") + .locale("en"); + +print("✓ KYC session created"); +print(" Client ID: " + kyc_session.get_client_id()); +print(" Provider: " + kyc_session.get_provider()); + +// Create verification session with Idenfy - generates real verification URL +print("\nCreating Idenfy verification session..."); +let verification_url = freezone_kyc_client.create_verification_session(kyc_info, kyc_session); + +print("✓ Idenfy verification session created"); +print(" Verification URL: " + verification_url); +print(" (User redirected to Idenfy to complete verification)"); + +// Production flow: +// 1. Redirect user to verification_url +// 2. User fills in remaining info (DOB, address, nationality) +// 3. User uploads ID document (Passport/ID/Driver's License) +// 4. User takes selfie for liveness check +// 5. Idenfy performs automated verification +// 6. Idenfy calls callback_url with VERIFIED data from ID document +// 7. Callback handler stores verified KYC info + +print("\n✓ User completes verification at Idenfy:"); +print(" - Fills in DOB, address, nationality"); +print(" - Uploads identity document"); +print(" - Takes selfie for liveness check"); +print(" - Idenfy extracts and verifies data from ID"); + +print("\n✓ Idenfy callback will provide VERIFIED data:"); +print(" - All personal info extracted from ID document"); +print(" - Document number, issue date, expiry date"); +print(" - Verification status (APPROVED/DENIED)"); +print(" - Liveness check result"); + +// For demo purposes, simulate what callback would receive +print("\nSimulating callback data (in production, this comes from Idenfy)..."); +let kyc_info_verified = new_kyc_info() + .client_id("user_1") + .first_name("Timur") // Verified from ID + .last_name("Gordon") // Verified from ID + .email(user_email) + .date_of_birth("1990-05-15") // Extracted from ID + .nationality("US") // Extracted from ID + .document_type("passport") // From verification + .document_number("P123456789") // Extracted from ID + .provider("idenfy") + .verified(true); // Only if status == "APPROVED" + +freezone_ctx.save(kyc_info_verified); +freezone_ctx.save(kyc_session); +print("✓ KYC verification data saved"); +print("✓ KYC verification completed\n"); + +// ============================================================================ +// SUMMARY +// ============================================================================ + +print("═══════════════════════════════════════════════════════════════"); +print("REGISTRATION COMPLETE"); +print("═══════════════════════════════════════════════════════════════"); +print("\nUser Summary:"); +print(" Name: " + user_name); +print(" Email: " + user_email); +print(" Public Key: " + timur_pubkey); +print(" TFT Account: Created"); +print(" ETH Account: Created"); +print(" KYC Status: Verified"); +print(" Payment Status: Completed ($100 USD)"); +print(" Contract: Signed and Active"); +print("\nRegistration Flow:"); +print(" ✓ Freezone initialization (Email, Payment, KYC providers configured)"); +print(" ✓ Freezone Ethereum wallet created"); +print(" ✓ Public key registration"); +print(" ✓ User profile creation & email verification"); +print(" ✓ Terms & Conditions signed"); +print(" ✓ Crypto wallets created (TFT + ETH)"); +print(" ✓ Payment processed ($100 USD)"); +print(" ✓ KYC verification completed with verified info"); +print("\n" + user_name + " is now a verified Freezone member!"); +print("═══════════════════════════════════════════════════════════════\n"); diff --git a/examples/freezone/freezone.rhai b/examples/freezone/freezone.rhai index 799b9f4..da5a802 100644 --- a/examples/freezone/freezone.rhai +++ b/examples/freezone/freezone.rhai @@ -15,11 +15,7 @@ print("=== FREEZONE REGISTRATION FLOW ===\n"); // ============================================================================ // KEYPAIRS AND IDENTITIES // ============================================================================ - -// Freezone Organization (Keypair 1) -let freezone_pubkey = "04d0aea7f0a48bcab4389753ddc2e61623dd89d800652b11d0a383eb3ea74561d730bdd06e0ca8f4cd4013907d95782a0a584313e1d91ae5ad09b663de36bfac44"; - -// User: Timur (Keypair 2) +let freezone_pubkey = "04e58314c13ea3f9caed882001a5090797b12563d5f9bbd7f16efe020e060c780b446862311501e2e9653416527d2634ff8a8050ff3a085baccd7ddcb94185ff56"; let timur_pubkey = "04090636d0a15854c4c0b73f65b6de5f6a27a7b22d6fbf5f6d97c45476a0384fe50781444c33f5af577e017599e4b432373fbcdcd844d8783c5e52240a14b63dc3"; print("Identities:"); @@ -40,35 +36,77 @@ print("✓ Freezone context created"); print(" Context ID: " + freezone_ctx.context_id()); print(" Signatory: Freezone (" + freezone_pubkey + ")"); -// Configure Email Client -print("\nConfiguring Email Client..."); +// Configure email client for sending verification emails +// Common SMTP providers: +// - Gmail: smtp.gmail.com:587 (requires app password) +// - Outlook: smtp-mail.outlook.com:587 +// - SendGrid: smtp.sendgrid.net:587 +// - Mailgun: smtp.mailgun.org:587 +// - AWS SES: email-smtp.us-east-1.amazonaws.com:587 + let freezone_email_client = new_email_client() - .smtp_host("smtp.freezone.example") - .smtp_port(587) - .from_email("noreply@freezone.example") - .from_name("Freezone Platform"); + .smtp_host("smtp-relay.brevo.com") // Change to your SMTP server + .smtp_port(587) // 587 for TLS, 465 for SSL + .username("timur@incubaid.com") // Your SMTP username/email + .password("xsmtpsib-a470d3fffa3f3b0f66800fe065f339067a07066d6a7df405fa465759418285ee-ojLWpd4oIShqIr6X") // Your SMTP password or app password + .from_email("registrar@ourworldfreezone.com") // From address + .from_name("Zanzibar Digital Free Zone") + .use_tls(true); print("✓ Email client configured"); -print(" SMTP Host: smtp.freezone.example"); +freezone_ctx.save(freezone_email_client); + +// Create verification email template +let verification_mail_template = new_mail_template() + .id("verification_email") + .name("Email Verification Template") + .subject("Verify your email address - Freezone") + .body("Hello,\n\nPlease verify your email address by clicking the link below:\n\n${url}\n\nOr use this verification code: ${code}\n\nThis link will expire in 24 hours.\n\nIf you didn't request this, please ignore this email.\n\nBest regards,\nFreezone Team") + .html_body("

Verify your email address

Hello,

Please verify your email address by clicking the button below:

Verify Email

Or enter this verification code:

${code}

This link will expire in 24 hours.

If you didn't request this, please ignore this email.

Best regards,
Freezone Team

"); + +print("✓ Verification email template created"); +freezone_ctx.save(verification_mail_template); print(" From: noreply@freezone.example"); // Configure Payment Provider (Pesapal) +// Get your credentials from: https://developer.pesapal.com/ +// Sandbox: https://demo.pesapal.com/ +// Production: https://www.pesapal.com/ print("\nConfiguring Payment Provider..."); + +// For SANDBOX testing (demo environment): let freezone_payment_client = new_payment_client_pesapal_sandbox( 1, - "qkio1BGGYAXTu2JOfm7XSXNruoZsrqEW", - "osGQ364R49cXKeOYSpaOnT++rHs=" + "qkio1BGGYAXTu2JOfm7XSXNruoZsrqEW", // Your Pesapal Consumer Key + "osGQ364R49cXKeOYSpaOnT++rHs=" // Your Pesapal Consumer Secret ); +// For PRODUCTION (real payments), use: +// let freezone_payment_client = new_payment_client_pesapal( +// 1, +// "your-production-consumer-key", +// "your-production-consumer-secret" +// ); + print("✓ Payment provider configured (Pesapal Sandbox)"); print(" Provider: Pesapal"); print(" Environment: Sandbox"); +print(" Note: Real payment links will be generated"); -// Configure KYC Provider +// Configure KYC Provider (Idenfy) +// Get your credentials from: https://www.idenfy.com/ +// Dashboard: https://dashboard.idenfy.com/ print("\nConfiguring KYC Provider..."); -print("✓ KYC provider configured"); -print(" Provider: Freezone KYC"); -print(" Callback URL: https://freezone.example/kyc/callback"); + +// Create Idenfy KYC client +let freezone_kyc_client = new_kyc_client_idenfy( + "your-idenfy-api-key", // Your Idenfy API Key + "your-idenfy-api-secret" // Your Idenfy API Secret +); + +print("✓ KYC provider configured (Idenfy)"); +print(" Provider: Idenfy"); +print(" Note: Real KYC verification sessions will be created"); // Create Freezone's own Ethereum wallet print("\nCreating Freezone Ethereum Wallet..."); @@ -155,8 +193,25 @@ print(" Nonce: " + verification.get_nonce()); // Prepare verification email let verification_link = "https://freezone.example/verify?nonce=" + verification.get_nonce(); -print(" Verification link: " + verification_link); -print(" (Email sent to: " + user_email + ")"); + +// Create verification mail with template parameters +let verification_mail = new_mail() + .to(user_email) + .template(verification_mail_template.get_id()) + .parameter("url", verification_link) + .parameter("code", verification.get_code()); + +print("✓ Verification email prepared"); +print(" To: " + user_email); +print(" Template: " + verification_mail_template.get_id()); +print(" Link: " + verification_link); +print(" Code: " + verification.get_code()); + +// Send verification email using template +// Note: In production, configure real SMTP credentials +// For now, we'll simulate sending (actual SMTP would fail with example.com) +print("✓ Verification email would be sent to: " + user_email); +print(" (Skipping actual SMTP send - configure real server in production)"); // Simulate user clicking verification link and verifying print("\n✓ User clicks verification link and verifies email"); @@ -259,15 +314,32 @@ print("✓ Payment request created"); print(" Amount: $100 USD"); print(" Description: Freezone Registration Fee"); -// Initiate payment with Pesapal (this would return a payment URL) +// Initiate payment with Pesapal - creates real payment link print("\nInitiating payment session with Pesapal..."); -print(" Payment URL: https://pay.pesapal.com/iframe/PesapalIframe3/Index/?OrderTrackingId=abc123"); +let payment_response = freezone_payment_client.create_payment_link(payment_request); + +let payment_url = payment_response.get_payment_url(); +let order_tracking_id = payment_response.get_order_tracking_id(); + +print("✓ Payment session created"); +print(" Payment URL: " + payment_url); +print(" Order Tracking ID: " + order_tracking_id); print(" (User would be redirected to Pesapal payment page)"); -// Simulate user completing payment -print("\n✓ User clicks payment link and completes payment"); -print(" Payment Status: COMPLETED"); -print(" Transaction ID: TXN_" + timestamp()); +// In production, you would: +// 1. Redirect user to payment_url +// 2. User completes payment on Pesapal +// 3. Pesapal calls your callback_url +// 4. You verify payment status with get_payment_status(order_tracking_id) + +// For demo purposes, check payment status +print("\nChecking payment status..."); +let payment_status = freezone_payment_client.get_payment_status(order_tracking_id); + +print("✓ Payment status retrieved"); +print(" Status: " + payment_status.get_payment_status_description()); +print(" Amount: " + payment_status.get_amount() + " " + payment_status.get_currency()); +print(" Transaction ID: " + order_tracking_id); // Create payment transaction record let payment_tx = new_transaction() @@ -288,48 +360,73 @@ print("✓ Transaction saved\n"); print("Step 6: KYC Verification"); print("─────────────────────────────────────────────────────────────"); +// Pre-fill KYC form with already collected info (first name, last name) +// User will fill in the rest at Idenfy (DOB, address, nationality, etc.) +print("\nPreparing KYC session with pre-filled data..."); +let kyc_info = new_kyc_info() + .client_id("user_1") + .first_name("Timur") // From user profile + .last_name("Gordon") // From user profile + .email(user_email); // From user profile + +print("✓ Pre-fill data prepared"); +print(" Name: Timur Gordon (pre-filled)"); +print(" Email: " + user_email + " (pre-filled)"); +print(" User will provide: DOB, address, nationality, ID document"); + // Create KYC session -let kyc_session = new_kyc_session("user_1", "freezone_kyc") +let kyc_session = new_kyc_session("user_1", "idenfy") .callback_url("https://freezone.example/kyc/callback") .success_url("https://freezone.example/kyc/success") - .error_url("https://freezone.example/kyc/error"); + .error_url("https://freezone.example/kyc/error") + .locale("en"); print("✓ KYC session created"); print(" Client ID: " + kyc_session.get_client_id()); print(" Provider: " + kyc_session.get_provider()); -// Generate KYC verification URL -print("\nKYC Verification URL generated:"); -print(" https://kyc.provider.com/verify?session=kyc_session_" + timestamp()); -print(" (User would be redirected to KYC provider)"); +// Create verification session with Idenfy - generates real verification URL +print("\nCreating Idenfy verification session..."); +let verification_url = freezone_kyc_client.create_verification_session(kyc_info, kyc_session); -// Simulate user clicking KYC link and completing verification -print("\n✓ User clicks KYC link and completes verification"); -print(" - Uploads identity document (Passport)"); +print("✓ Idenfy verification session created"); +print(" Verification URL: " + verification_url); +print(" (User redirected to Idenfy to complete verification)"); + +// Production flow: +// 1. Redirect user to verification_url +// 2. User fills in remaining info (DOB, address, nationality) +// 3. User uploads ID document (Passport/ID/Driver's License) +// 4. User takes selfie for liveness check +// 5. Idenfy performs automated verification +// 6. Idenfy calls callback_url with VERIFIED data from ID document +// 7. Callback handler stores verified KYC info + +print("\n✓ User completes verification at Idenfy:"); +print(" - Fills in DOB, address, nationality"); +print(" - Uploads identity document"); print(" - Takes selfie for liveness check"); -print(" - Provides address proof"); +print(" - Idenfy extracts and verifies data from ID"); -// Simulate KYC callback with verification results -print("\n✓ KYC Provider callback received:"); -print(" - Identity verification: PASSED"); -print(" - Liveness check: PASSED"); -print(" - Address verification: PASSED"); -print(" - Sanctions screening: CLEAR"); -print(" - PEP check: NOT FOUND"); -print(" - Overall Status: VERIFIED"); +print("\n✓ Idenfy callback will provide VERIFIED data:"); +print(" - All personal info extracted from ID document"); +print(" - Document number, issue date, expiry date"); +print(" - Verification status (APPROVED/DENIED)"); +print(" - Liveness check result"); -// Create KYC info with verified data from callback -print("\nStoring verified KYC information..."); +// For demo purposes, simulate what callback would receive +print("\nSimulating callback data (in production, this comes from Idenfy)..."); let kyc_info_verified = new_kyc_info() - .first_name("Timur") - .last_name("Gordon") + .client_id("user_1") + .first_name("Timur") // Verified from ID + .last_name("Gordon") // Verified from ID .email(user_email) - .phone("+1-555-0123") - .country("US") - .date_of_birth("1990-05-15") - .document_type("passport") - .document_number("P123456789") - .verified(true); + .date_of_birth("1990-05-15") // Extracted from ID + .nationality("US") // Extracted from ID + .document_type("passport") // From verification + .document_number("P123456789") // Extracted from ID + .provider("idenfy") + .verified(true); // Only if status == "APPROVED" freezone_ctx.save(kyc_info_verified); freezone_ctx.save(kyc_session); diff --git a/src/engine.rs b/src/engine.rs index 5645386..b4d7321 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -100,6 +100,10 @@ def_package! { .set_into_module(module, |ctx: &mut OsirisContext, flow_instance: crate::objects::FlowInstance| ctx.save_object(flow_instance)); FuncRegistration::new("save") .set_into_module(module, |ctx: &mut OsirisContext, verification: crate::objects::Verification| ctx.save_object(verification)); + FuncRegistration::new("save") + .set_into_module(module, |ctx: &mut OsirisContext, email_client: crate::objects::communication::email::EmailClient| ctx.save_object(email_client)); + FuncRegistration::new("save") + .set_into_module(module, |ctx: &mut OsirisContext, mail_template: crate::objects::communication::email::MailTemplate| ctx.save_object(mail_template)); FuncRegistration::new("save") .set_into_module(module, |ctx: &mut OsirisContext, account: crate::objects::Account| ctx.save_object(account)); FuncRegistration::new("save") diff --git a/src/objects/communication/email.rs b/src/objects/communication/email.rs index e39b2de..42b35b2 100644 --- a/src/objects/communication/email.rs +++ b/src/objects/communication/email.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use super::verification::Verification; +use crate::store::{BaseData, Object, Storable}; use lettre::{ Message, SmtpTransport, Transport, message::{header::ContentType, MultiPart, SinglePart}, @@ -11,8 +12,11 @@ use lettre::{ }; /// Email client with SMTP configuration -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, crate::DeriveObject)] pub struct EmailClient { + #[serde(flatten)] + pub base_data: BaseData, + /// SMTP server hostname pub smtp_host: String, @@ -35,9 +39,64 @@ pub struct EmailClient { pub use_tls: bool, } +/// Mail template with placeholders +#[derive(Debug, Clone, Serialize, Deserialize, crate::DeriveObject)] +pub struct MailTemplate { + #[serde(flatten)] + pub base_data: BaseData, + + /// Template ID + pub id: String, + + /// Template name + pub name: String, + + /// Email subject (can contain placeholders like ${name}) + pub subject: String, + + /// Email body (can contain placeholders like ${code}, ${url}) + pub body: String, + + /// HTML body (optional, can contain placeholders) + pub html_body: Option, +} + +impl Default for MailTemplate { + fn default() -> Self { + Self { + base_data: BaseData::new(), + id: String::new(), + name: String::new(), + subject: String::new(), + body: String::new(), + html_body: None, + } + } +} + +/// Email message created from a template +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct Mail { + /// Recipient email address + pub to: String, + + /// Template ID to use + pub template_id: Option, + + /// Parameters to replace in template + pub parameters: std::collections::HashMap, + + /// Direct subject (if not using template) + pub subject: Option, + + /// Direct body (if not using template) + pub body: Option, +} + impl Default for EmailClient { fn default() -> Self { Self { + base_data: BaseData::new(), smtp_host: "localhost".to_string(), smtp_port: 587, username: String::new(), @@ -49,6 +108,105 @@ impl Default for EmailClient { } } +impl MailTemplate { + /// Create a new mail template + pub fn new() -> Self { + Self::default() + } + + /// Builder: Set template ID + pub fn id(mut self, id: String) -> Self { + self.id = id; + self + } + + /// Builder: Set template name + pub fn name(mut self, name: String) -> Self { + self.name = name; + self + } + + /// Builder: Set subject + pub fn subject(mut self, subject: String) -> Self { + self.subject = subject; + self + } + + /// Builder: Set body + pub fn body(mut self, body: String) -> Self { + self.body = body; + self + } + + /// Builder: Set HTML body + pub fn html_body(mut self, html_body: String) -> Self { + self.html_body = Some(html_body); + self + } + + /// Replace placeholders in text + fn replace_placeholders(&self, text: &str, parameters: &std::collections::HashMap) -> String { + let mut result = text.to_string(); + for (key, value) in parameters { + let placeholder = format!("${{{}}}", key); + result = result.replace(&placeholder, value); + } + result + } + + /// Render subject with parameters + pub fn render_subject(&self, parameters: &std::collections::HashMap) -> String { + self.replace_placeholders(&self.subject, parameters) + } + + /// Render body with parameters + pub fn render_body(&self, parameters: &std::collections::HashMap) -> String { + self.replace_placeholders(&self.body, parameters) + } + + /// Render HTML body with parameters + pub fn render_html_body(&self, parameters: &std::collections::HashMap) -> Option { + self.html_body.as_ref().map(|html| self.replace_placeholders(html, parameters)) + } +} + +impl Mail { + /// Create a new mail + pub fn new() -> Self { + Self::default() + } + + /// Builder: Set recipient + pub fn to(mut self, to: String) -> Self { + self.to = to; + self + } + + /// Builder: Set template ID + pub fn template(mut self, template_id: String) -> Self { + self.template_id = Some(template_id); + self + } + + /// Builder: Add a parameter + pub fn parameter(mut self, key: String, value: String) -> Self { + self.parameters.insert(key, value); + self + } + + /// Builder: Set subject (for non-template emails) + pub fn subject(mut self, subject: String) -> Self { + self.subject = Some(subject); + self + } + + /// Builder: Set body (for non-template emails) + pub fn body(mut self, body: String) -> Self { + self.body = Some(body); + self + } +} + impl EmailClient { /// Create a new email client pub fn new() -> Self { @@ -198,6 +356,21 @@ impl EmailClient { Ok(()) } + /// Send a mail using a template + pub fn send_mail(&self, mail: &Mail, template: &MailTemplate) -> Result<(), String> { + // Render subject and body with parameters + let subject = template.render_subject(&mail.parameters); + let body_text = template.render_body(&mail.parameters); + let html_body = template.render_html_body(&mail.parameters); + + // Send email + if let Some(html) = html_body { + self.send_html_email(&mail.to, &subject, &html, Some(&body_text)) + } else { + self.send_email(&mail.to, &subject, &body_text) + } + } + /// Send a verification email with code pub fn send_verification_code_email( &self, diff --git a/src/objects/communication/rhai.rs b/src/objects/communication/rhai.rs index 54636cf..e8d2f4d 100644 --- a/src/objects/communication/rhai.rs +++ b/src/objects/communication/rhai.rs @@ -4,7 +4,7 @@ use ::rhai::plugin::*; use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder}; use super::verification::{Verification, VerificationStatus, VerificationTransport}; -use super::email::EmailClient; +use super::email::{EmailClient, MailTemplate, Mail}; // ============================================================================ // Verification Module @@ -107,6 +107,129 @@ mod rhai_verification_module { } } +// ============================================================================ +// Mail Template Module +// ============================================================================ + +type RhaiMailTemplate = MailTemplate; + +#[export_module] +mod rhai_mail_template_module { + use super::RhaiMailTemplate; + use super::super::email::MailTemplate; + use ::rhai::EvalAltResult; + + #[rhai_fn(name = "new_mail_template", return_raw)] + pub fn new_mail_template() -> Result> { + Ok(MailTemplate::new()) + } + + #[rhai_fn(name = "id", return_raw)] + pub fn set_id( + template: &mut RhaiMailTemplate, + id: String, + ) -> Result> { + let owned = std::mem::take(template); + *template = owned.id(id); + Ok(template.clone()) + } + + #[rhai_fn(name = "name", return_raw)] + pub fn set_name( + template: &mut RhaiMailTemplate, + name: String, + ) -> Result> { + let owned = std::mem::take(template); + *template = owned.name(name); + Ok(template.clone()) + } + + #[rhai_fn(name = "subject", return_raw)] + pub fn set_subject( + template: &mut RhaiMailTemplate, + subject: String, + ) -> Result> { + let owned = std::mem::take(template); + *template = owned.subject(subject); + Ok(template.clone()) + } + + #[rhai_fn(name = "body", return_raw)] + pub fn set_body( + template: &mut RhaiMailTemplate, + body: String, + ) -> Result> { + let owned = std::mem::take(template); + *template = owned.body(body); + Ok(template.clone()) + } + + #[rhai_fn(name = "html_body", return_raw)] + pub fn set_html_body( + template: &mut RhaiMailTemplate, + html_body: String, + ) -> Result> { + let owned = std::mem::take(template); + *template = owned.html_body(html_body); + Ok(template.clone()) + } + + // Getters + #[rhai_fn(name = "get_id")] + pub fn get_id(template: &mut RhaiMailTemplate) -> String { + template.id.clone() + } +} + +// ============================================================================ +// Mail Module +// ============================================================================ + +type RhaiMail = Mail; + +#[export_module] +mod rhai_mail_module { + use super::RhaiMail; + use super::super::email::Mail; + use ::rhai::EvalAltResult; + + #[rhai_fn(name = "new_mail", return_raw)] + pub fn new_mail() -> Result> { + Ok(Mail::new()) + } + + #[rhai_fn(name = "to", return_raw)] + pub fn set_to( + mail: &mut RhaiMail, + to: String, + ) -> Result> { + let owned = std::mem::take(mail); + *mail = owned.to(to); + Ok(mail.clone()) + } + + #[rhai_fn(name = "template", return_raw)] + pub fn set_template( + mail: &mut RhaiMail, + template_id: String, + ) -> Result> { + let owned = std::mem::take(mail); + *mail = owned.template(template_id); + Ok(mail.clone()) + } + + #[rhai_fn(name = "parameter", return_raw)] + pub fn add_parameter( + mail: &mut RhaiMail, + key: String, + value: String, + ) -> Result> { + let owned = std::mem::take(mail); + *mail = owned.parameter(key, value); + Ok(mail.clone()) + } +} + // ============================================================================ // Email Client Module // ============================================================================ @@ -116,7 +239,9 @@ type RhaiEmailClient = EmailClient; #[export_module] mod rhai_email_module { use super::RhaiEmailClient; - use super::super::email::EmailClient; + use super::RhaiMail; + use super::RhaiMailTemplate; + use super::super::email::{EmailClient, Mail, MailTemplate}; use super::super::verification::Verification; use ::rhai::EvalAltResult; @@ -125,6 +250,86 @@ mod rhai_email_module { Ok(EmailClient::new()) } + #[rhai_fn(name = "smtp_host", return_raw)] + pub fn set_smtp_host( + client: &mut RhaiEmailClient, + host: String, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.smtp_host(host); + Ok(client.clone()) + } + + #[rhai_fn(name = "smtp_port", return_raw)] + pub fn set_smtp_port( + client: &mut RhaiEmailClient, + port: i64, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.smtp_port(port as u16); + Ok(client.clone()) + } + + #[rhai_fn(name = "username", return_raw)] + pub fn set_username( + client: &mut RhaiEmailClient, + username: String, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.username(username); + Ok(client.clone()) + } + + #[rhai_fn(name = "password", return_raw)] + pub fn set_password( + client: &mut RhaiEmailClient, + password: String, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.password(password); + Ok(client.clone()) + } + + #[rhai_fn(name = "from_email", return_raw)] + pub fn set_from_email( + client: &mut RhaiEmailClient, + email: String, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.from_address(email); + Ok(client.clone()) + } + + #[rhai_fn(name = "from_name", return_raw)] + pub fn set_from_name( + client: &mut RhaiEmailClient, + name: String, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.from_name(name); + Ok(client.clone()) + } + + #[rhai_fn(name = "use_tls", return_raw)] + pub fn set_use_tls( + client: &mut RhaiEmailClient, + use_tls: bool, + ) -> Result> { + let owned = std::mem::take(client); + *client = owned.use_tls(use_tls); + Ok(client.clone()) + } + + #[rhai_fn(name = "send_mail", return_raw)] + pub fn send_mail( + client: &mut RhaiEmailClient, + mail: RhaiMail, + template: RhaiMailTemplate, + ) -> Result<(), Box> { + client.send_mail(&mail, &template) + .map_err(|e| e.into()) + } + #[rhai_fn(name = "send_verification_code", return_raw)] pub fn send_verification_code( client: &mut RhaiEmailClient, @@ -152,15 +357,25 @@ mod rhai_email_module { pub fn register_communication_modules(parent_module: &mut Module) { // Register custom types parent_module.set_custom_type::("Verification"); + parent_module.set_custom_type::("MailTemplate"); + parent_module.set_custom_type::("Mail"); parent_module.set_custom_type::("EmailClient"); // Merge verification functions let verification_module = exported_module!(rhai_verification_module); - parent_module.merge(&verification_module); + parent_module.combine_flatten(verification_module); + + // Merge mail template functions + let mail_template_module = exported_module!(rhai_mail_template_module); + parent_module.combine_flatten(mail_template_module); + + // Merge mail functions + let mail_module = exported_module!(rhai_mail_module); + parent_module.combine_flatten(mail_module); // Merge email client functions let email_module = exported_module!(rhai_email_module); - parent_module.merge(&email_module); + parent_module.combine_flatten(email_module); } // ============================================================================ @@ -173,6 +388,18 @@ impl CustomType for Verification { } } +impl CustomType for MailTemplate { + fn build(mut builder: TypeBuilder) { + builder.with_name("MailTemplate"); + } +} + +impl CustomType for Mail { + fn build(mut builder: TypeBuilder) { + builder.with_name("Mail"); + } +} + impl CustomType for EmailClient { fn build(mut builder: TypeBuilder) { builder.with_name("EmailClient"); diff --git a/src/objects/kyc/rhai.rs b/src/objects/kyc/rhai.rs index e522d1a..72fff60 100644 --- a/src/objects/kyc/rhai.rs +++ b/src/objects/kyc/rhai.rs @@ -2,10 +2,10 @@ use ::rhai::plugin::*; use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder}; -use std::mem; use super::info::{KycInfo, VerificationStatus}; use super::session::{KycSession, SessionStatus}; +use super::client::KycClient; // ============================================================================ // KYC Info Module @@ -290,6 +290,45 @@ mod rhai_kyc_session_module { } } +// ============================================================================ +// KYC Client Module +// ============================================================================ + +type RhaiKycClient = KycClient; + +#[export_module] +mod rhai_kyc_client_module { + use super::RhaiKycClient; + use super::RhaiKycInfo; + use super::RhaiKycSession; + use ::rhai::EvalAltResult; + + #[rhai_fn(name = "new_kyc_client_idenfy", return_raw)] + pub fn new_idenfy_client( + api_key: String, + api_secret: String, + ) -> Result> { + Ok(KycClient::idenfy(api_key, api_secret)) + } + + #[rhai_fn(name = "create_verification_session", return_raw)] + pub fn create_verification_session( + client: &mut RhaiKycClient, + kyc_info: RhaiKycInfo, + session: RhaiKycSession, + ) -> Result> { + // Need to use tokio runtime for async call + let rt = tokio::runtime::Runtime::new() + .map_err(|e| format!("Failed to create runtime: {}", e))?; + + let mut session_mut = session.clone(); + let url = rt.block_on(client.create_verification_session(&kyc_info, &mut session_mut)) + .map_err(|e| format!("Failed to create verification session: {}", e))?; + + Ok(url) + } +} + // ============================================================================ // Registration Functions // ============================================================================ @@ -299,6 +338,7 @@ pub fn register_kyc_modules(parent_module: &mut Module) { // Register custom types parent_module.set_custom_type::("KycInfo"); parent_module.set_custom_type::("KycSession"); + parent_module.set_custom_type::("KycClient"); // Merge KYC info functions let info_module = exported_module!(rhai_kyc_info_module); @@ -307,6 +347,10 @@ pub fn register_kyc_modules(parent_module: &mut Module) { // Merge KYC session functions let session_module = exported_module!(rhai_kyc_session_module); parent_module.merge(&session_module); + + // Merge KYC client functions + let client_module = exported_module!(rhai_kyc_client_module); + parent_module.merge(&client_module); } // ============================================================================ @@ -324,3 +368,9 @@ impl CustomType for KycSession { builder.with_name("KycSession"); } } + +impl CustomType for KycClient { + fn build(mut builder: TypeBuilder) { + builder.with_name("KycClient"); + } +}