483 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | |
| <html lang="en">
 | |
| <head>
 | |
|     <meta charset="utf-8" />
 | |
|     <meta name="viewport" content="width=device-width, initial-scale=1" />
 | |
|     <title>Digital Freezone Platform</title>
 | |
|     
 | |
|     <!-- Bootstrap CSS -->
 | |
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
 | |
|     
 | |
|     <!-- Bootstrap Icons -->
 | |
|     <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
 | |
|     
 | |
|     <!-- Custom CSS -->
 | |
|     <style>
 | |
|         .step-indicator {
 | |
|             text-align: center;
 | |
|             position: relative;
 | |
|             opacity: 0.5;
 | |
|         }
 | |
| 
 | |
|         .step-indicator.active {
 | |
|             opacity: 1;
 | |
|             font-weight: bold;
 | |
|         }
 | |
| 
 | |
|         .step-indicator::after {
 | |
|             content: '';
 | |
|             position: absolute;
 | |
|             top: 50%;
 | |
|             right: -50%;
 | |
|             width: 100%;
 | |
|             height: 2px;
 | |
|             background-color: #dee2e6;
 | |
|             z-index: -1;
 | |
|         }
 | |
| 
 | |
|         .step-indicator:last-child::after {
 | |
|             display: none;
 | |
|         }
 | |
| 
 | |
|         /* Stripe Elements styling */
 | |
|         #payment-element {
 | |
|             min-height: 40px;
 | |
|             padding: 10px;
 | |
|             border: 1px solid #dee2e6;
 | |
|             border-radius: 0.375rem;
 | |
|             background-color: #ffffff;
 | |
|         }
 | |
| 
 | |
|         .payment-ready {
 | |
|             border-color: #198754 !important;
 | |
|             border-width: 2px !important;
 | |
|             box-shadow: 0 0 0 0.2rem rgba(25, 135, 84, 0.25) !important;
 | |
|         }
 | |
| 
 | |
|         /* Loading state for payment form */
 | |
|         .payment-loading {
 | |
|             opacity: 0.7;
 | |
|             pointer-events: none;
 | |
|         }
 | |
| 
 | |
|         /* Error display styling */
 | |
|         #payment-errors {
 | |
|             margin-top: 1rem;
 | |
|             margin-bottom: 1rem;
 | |
|             display: none;
 | |
|         }
 | |
| 
 | |
|         /* Persistent error styling */
 | |
|         #payment-errors[data-persistent-error="true"] {
 | |
|             position: sticky;
 | |
|             top: 20px;
 | |
|             z-index: 1050;
 | |
|             box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 | |
|         }
 | |
| 
 | |
|         /* Enhanced alert styling */
 | |
|         .alert-dismissible .btn-close {
 | |
|             position: absolute;
 | |
|             top: 0.5rem;
 | |
|             right: 0.5rem;
 | |
|         }
 | |
| 
 | |
|         /* Ad blocker guidance specific styling */
 | |
|         .alert-warning .bi-shield-exclamation {
 | |
|             color: #856404;
 | |
|         }
 | |
| 
 | |
|         /* Payment card error state */
 | |
|         .border-danger {
 | |
|             animation: pulse-border-danger 2s infinite;
 | |
|         }
 | |
| 
 | |
|         .border-warning {
 | |
|             animation: pulse-border-warning 2s infinite;
 | |
|         }
 | |
| 
 | |
|         @keyframes pulse-border-danger {
 | |
|             0% {
 | |
|                 border-color: #dc3545;
 | |
|             }
 | |
| 
 | |
|             50% {
 | |
|                 border-color: #f8d7da;
 | |
|             }
 | |
| 
 | |
|             100% {
 | |
|                 border-color: #dc3545;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @keyframes pulse-border-warning {
 | |
|             0% {
 | |
|                 border-color: #ffc107;
 | |
|             }
 | |
| 
 | |
|             50% {
 | |
|                 border-color: #fff3cd;
 | |
|             }
 | |
| 
 | |
|             100% {
 | |
|                 border-color: #ffc107;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @keyframes shake {
 | |
| 
 | |
|             0%,
 | |
|             100% {
 | |
|                 transform: translateX(0);
 | |
|             }
 | |
| 
 | |
|             10%,
 | |
|             30%,
 | |
|             50%,
 | |
|             70%,
 | |
|             90% {
 | |
|                 transform: translateX(-5px);
 | |
|             }
 | |
| 
 | |
|             20%,
 | |
|             40%,
 | |
|             60%,
 | |
|             80% {
 | |
|                 transform: translateX(5px);
 | |
|             }
 | |
|         }
 | |
|     </style>
 | |
| </head>
 | |
| <body>
 | |
|     <div id="app"></div>
 | |
|     
 | |
|     <!-- Bootstrap JS -->
 | |
|     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
 | |
|     
 | |
|     <!-- Stripe JavaScript SDK -->
 | |
|     <script src="https://js.stripe.com/v3/"></script>
 | |
|     
 | |
|     <!-- Custom Stripe Integration -->
 | |
|     <script>
 | |
|         // Stripe Integration for Company Registration
 | |
|         let stripe;
 | |
|         let elements;
 | |
|         let paymentElement;
 | |
| 
 | |
|         // Stripe publishable key - replace with your actual key from Stripe Dashboard
 | |
|         const STRIPE_PUBLISHABLE_KEY = 'pk_test_51MCkZTC7LG8OeRdIcqmmoDkRwDObXSwYdChprMHJYoD2VRO8OCDBV5KtegLI0tLFXJo9yyvEXi7jzk1NAB5owj8i00DkYSaV9y'; // Replace with your real key
 | |
| 
 | |
|         // Initialize Stripe when the script loads
 | |
|         document.addEventListener('DOMContentLoaded', function() {
 | |
|             console.log('🔧 Stripe integration script loaded');
 | |
|             
 | |
|             // Initialize Stripe
 | |
|             if (window.Stripe) {
 | |
|                 stripe = Stripe(STRIPE_PUBLISHABLE_KEY);
 | |
|                 console.log('✅ Stripe initialized');
 | |
|             } else {
 | |
|                 console.error('❌ Stripe.js not loaded');
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         // Create payment intent on server
 | |
|         window.createPaymentIntent = async function(formDataJson) {
 | |
|             console.log('💳 Creating payment intent for company registration...');
 | |
|             console.log('🔧 Server endpoint: /company/create-payment-intent');
 | |
|             
 | |
|             try {
 | |
|                 // Parse the JSON string from Rust
 | |
|                 let formData;
 | |
|                 if (typeof formDataJson === 'string') {
 | |
|                     formData = JSON.parse(formDataJson);
 | |
|                 } else {
 | |
|                     formData = formDataJson;
 | |
|                 }
 | |
|                 
 | |
|                 console.log('📋 Form data being sent:', {
 | |
|                     company_name: formData.company_name,
 | |
|                     company_type: formData.company_type,
 | |
|                     payment_plan: formData.payment_plan,
 | |
|                     final_agreement: formData.final_agreement
 | |
|                 });
 | |
|                 
 | |
|                 const response = await fetch('http://127.0.0.1:3001/company/create-payment-intent', {
 | |
|                     method: 'POST',
 | |
|                     headers: {
 | |
|                         'Content-Type': 'application/json',
 | |
|                     },
 | |
|                     body: JSON.stringify(formData)
 | |
|                 });
 | |
| 
 | |
|                 console.log('📡 Server response status:', response.status);
 | |
| 
 | |
|                 if (!response.ok) {
 | |
|                     const errorText = await response.text();
 | |
|                     console.error('❌ Payment intent creation failed:', errorText);
 | |
|                     
 | |
|                     let errorData;
 | |
|                     try {
 | |
|                         errorData = JSON.parse(errorText);
 | |
|                     } catch (e) {
 | |
|                         errorData = { error: errorText };
 | |
|                     }
 | |
|                     
 | |
|                     // Show user-friendly error message
 | |
|                     const errorMsg = errorData.error || 'Failed to create payment intent';
 | |
|                     console.error('💥 Error details:', errorData);
 | |
|                     throw new Error(errorMsg);
 | |
|                 }
 | |
| 
 | |
|                 const responseData = await response.json();
 | |
|                 console.log('✅ Payment intent created successfully');
 | |
|                 console.log('🔑 Client secret received:', responseData.client_secret ? 'Yes' : 'No');
 | |
|                 console.log('🆔 Payment intent ID:', responseData.payment_intent_id);
 | |
|                 
 | |
|                 const { client_secret } = responseData;
 | |
|                 if (!client_secret) {
 | |
|                     throw new Error('No client secret received from server');
 | |
|                 }
 | |
| 
 | |
|                 return client_secret;
 | |
|                 
 | |
|             } catch (error) {
 | |
|                 console.error('❌ Payment intent creation error:', error.message);
 | |
|                 console.error('🔧 Troubleshooting:');
 | |
|                 console.error('   1. Check if server is running on port 3001');
 | |
|                 console.error('   2. Verify Stripe API keys in .env file');
 | |
|                 console.error('   3. Check server logs for detailed error info');
 | |
|                 throw error;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         // Initialize Stripe Elements with client secret
 | |
|         window.initializeStripeElements = async function(clientSecret) {
 | |
|             console.log('🔧 Initializing Stripe Elements for company registration payment...');
 | |
|             console.log('🔑 Client secret format check:', clientSecret ? 'Valid' : 'Missing');
 | |
|             
 | |
|             try {
 | |
|                 if (!stripe) {
 | |
|                     throw new Error('Stripe not initialized - check your publishable key');
 | |
|                 }
 | |
| 
 | |
|                 // Create Elements instance with client secret
 | |
|                 elements = stripe.elements({
 | |
|                     clientSecret: clientSecret,
 | |
|                     appearance: {
 | |
|                         theme: 'stripe',
 | |
|                         variables: {
 | |
|                             colorPrimary: '#198754',
 | |
|                             colorBackground: '#ffffff',
 | |
|                             colorText: '#30313d',
 | |
|                             colorDanger: '#df1b41',
 | |
|                             fontFamily: 'system-ui, sans-serif',
 | |
|                             spacingUnit: '4px',
 | |
|                             borderRadius: '6px',
 | |
|                         }
 | |
|                     }
 | |
|                 });
 | |
| 
 | |
|                 // Clear the payment element container first
 | |
|                 const paymentElementDiv = document.getElementById('payment-element');
 | |
|                 if (!paymentElementDiv) {
 | |
|                     throw new Error('Payment element container not found');
 | |
|                 }
 | |
|                 
 | |
|                 paymentElementDiv.innerHTML = '';
 | |
| 
 | |
|                 // Create and mount the Payment Element
 | |
|                 paymentElement = elements.create('payment');
 | |
|                 paymentElement.mount('#payment-element');
 | |
| 
 | |
|                 // Handle real-time validation errors from the Payment Element
 | |
|                 paymentElement.on('change', (event) => {
 | |
|                     const displayError = document.getElementById('payment-errors');
 | |
|                     if (event.error) {
 | |
|                         displayError.textContent = event.error.message;
 | |
|                         displayError.style.display = 'block';
 | |
|                         displayError.classList.remove('alert-success');
 | |
|                         displayError.classList.add('alert-danger');
 | |
|                     } else {
 | |
|                         displayError.style.display = 'none';
 | |
|                     }
 | |
|                 });
 | |
| 
 | |
|                 // Handle when the Payment Element is ready
 | |
|                 paymentElement.on('ready', () => {
 | |
|                     console.log('✅ Stripe Elements ready for payment');
 | |
| 
 | |
|                     // Add a subtle success indicator
 | |
|                     const paymentCard = paymentElementDiv.closest('.card');
 | |
|                     if (paymentCard) {
 | |
|                         paymentCard.style.borderColor = '#198754';
 | |
|                         paymentCard.style.borderWidth = '2px';
 | |
|                     }
 | |
| 
 | |
|                     // Update button text to show payment is ready
 | |
|                     const submitButton = document.getElementById('submit-payment');
 | |
|                     const submitText = document.getElementById('submit-text');
 | |
|                     if (submitButton && submitText) {
 | |
|                         submitButton.disabled = false;
 | |
|                         submitText.textContent = 'Complete Payment';
 | |
|                         submitButton.classList.remove('btn-secondary');
 | |
|                         submitButton.classList.add('btn-success');
 | |
|                     }
 | |
|                 });
 | |
| 
 | |
|                 console.log('✅ Stripe Elements initialized successfully');
 | |
|                 return true;
 | |
| 
 | |
|             } catch (error) {
 | |
|                 console.error('❌ Error initializing Stripe Elements:', error);
 | |
|                 
 | |
|                 // Show helpful error message
 | |
|                 const errorElement = document.getElementById('payment-errors');
 | |
|                 if (errorElement) {
 | |
|                     errorElement.innerHTML = `
 | |
|                         <div class="alert alert-warning alert-dismissible" role="alert">
 | |
|                             <i class="bi bi-exclamation-triangle me-2"></i>
 | |
|                             <strong>Stripe Setup Required:</strong> ${error.message || 'Failed to load payment form'}<br><br>
 | |
|                             <strong>Next Steps:</strong><br>
 | |
|                             1. Get your Stripe API keys from <a href="https://dashboard.stripe.com/apikeys" target="_blank">Stripe Dashboard</a><br>
 | |
|                             2. Replace the placeholder publishable key in the code<br>
 | |
|                             3. Set up a server to create payment intents<br><br>
 | |
|                             <small>The integration is complete - you just need real Stripe credentials!</small>
 | |
|                             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
 | |
|                         </div>
 | |
|                     `;
 | |
|                     errorElement.style.display = 'block';
 | |
|                 }
 | |
|                 throw error;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         // Confirm payment with Stripe
 | |
|         window.confirmStripePayment = async function(clientSecret) {
 | |
|             console.log('🔄 Confirming company registration payment...');
 | |
|             console.log('🔑 Using client secret for payment confirmation');
 | |
|             
 | |
|             try {
 | |
|                 // Ensure elements are ready before submitting
 | |
|                 if (!elements) {
 | |
|                     console.error('❌ Payment elements not initialized');
 | |
|                     throw new Error('Payment form not ready. Please wait a moment and try again.');
 | |
|                 }
 | |
| 
 | |
|                 console.log('🔄 Step 1: Submitting payment elements...');
 | |
| 
 | |
|                 // Step 1: Submit the payment elements first (required by new Stripe API)
 | |
|                 const { error: submitError } = await elements.submit();
 | |
| 
 | |
|                 if (submitError) {
 | |
|                     console.error('❌ Elements submit failed:', submitError);
 | |
|                     throw new Error(submitError.message || 'Payment form validation failed.');
 | |
|                 }
 | |
| 
 | |
|                 console.log('✅ Step 1 complete: Elements submitted successfully');
 | |
|                 console.log('🔄 Step 2: Confirming payment with Stripe...');
 | |
| 
 | |
|                 // Step 2: Confirm payment with Stripe
 | |
|                 const { error, paymentIntent } = await stripe.confirmPayment({
 | |
|                     elements,
 | |
|                     clientSecret: clientSecret,
 | |
|                     confirmParams: {
 | |
|                         return_url: `${window.location.origin}/company/payment-success`,
 | |
|                     },
 | |
|                     redirect: 'if_required'
 | |
|                 });
 | |
| 
 | |
|                 if (error) {
 | |
|                     console.error('❌ Payment confirmation failed:', error);
 | |
|                     console.error('🔧 Error details:', {
 | |
|                         type: error.type,
 | |
|                         code: error.code,
 | |
|                         message: error.message
 | |
|                     });
 | |
|                     throw new Error(error.message);
 | |
|                 }
 | |
| 
 | |
|                 if (paymentIntent && paymentIntent.status === 'succeeded') {
 | |
|                     console.log('✅ Payment completed successfully!');
 | |
|                     console.log('🆔 Payment Intent ID:', paymentIntent.id);
 | |
|                     console.log('💰 Amount paid:', paymentIntent.amount_received / 100, paymentIntent.currency.toUpperCase());
 | |
|                     
 | |
|                     // Clear saved form data since registration is complete
 | |
|                     localStorage.removeItem('freezone_company_registration');
 | |
|                     console.log('🗑️ Cleared saved registration data');
 | |
|                     
 | |
|                     // Redirect to success page
 | |
|                     const successUrl = `${window.location.origin}/company/payment-success?payment_intent=${paymentIntent.id}&payment_intent_client_secret=${clientSecret}`;
 | |
|                     console.log('🔄 Redirecting to success page:', successUrl);
 | |
|                     window.location.href = successUrl;
 | |
|                     return true;
 | |
|                 } else {
 | |
|                     console.error('❌ Unexpected payment status:', paymentIntent?.status);
 | |
|                     console.error('🔧 Payment Intent details:', paymentIntent);
 | |
|                     throw new Error('Payment processing failed. Please try again.');
 | |
|                 }
 | |
| 
 | |
|             } catch (error) {
 | |
|                 console.error('❌ Payment confirmation error:', error.message);
 | |
|                 console.error('🔧 Full error details:', error);
 | |
|                 throw error;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         console.log('✅ Stripe integration ready for company registration payments');
 | |
|         console.log('🔧 Server endpoint: /company/create-payment-intent');
 | |
|         console.log('💡 Navigate to Entities → Register Company → Step 4 to process payments');
 | |
|         
 | |
|         // Add a test function for manual payment testing
 | |
|         window.testPaymentFlow = async function() {
 | |
|             console.log('🧪 Testing payment flow manually...');
 | |
|             
 | |
|             const mockFormData = {
 | |
|                 company_name: "Test Company Ltd",
 | |
|                 company_type: "Single FZC",
 | |
|                 payment_plan: "monthly",
 | |
|                 company_email: "test@example.com",
 | |
|                 company_phone: "+1234567890",
 | |
|                 company_website: "https://test.com",
 | |
|                 company_address: "123 Test Street",
 | |
|                 company_industry: "Technology",
 | |
|                 company_purpose: "Software Development",
 | |
|                 fiscal_year_end: "December",
 | |
|                 shareholders: "[]",
 | |
|                 agreements: ["terms", "privacy", "compliance", "articles"],
 | |
|                 final_agreement: true
 | |
|             };
 | |
|             
 | |
|             try {
 | |
|                 console.log('📋 Using test form data:', mockFormData);
 | |
|                 const clientSecret = await window.createPaymentIntent(JSON.stringify(mockFormData));
 | |
|                 console.log('✅ Payment intent created, initializing Stripe Elements...');
 | |
|                 await window.initializeStripeElements(clientSecret);
 | |
|                 console.log('🎉 Payment form should now be visible!');
 | |
|                 console.log('💡 Check the payment section in the UI');
 | |
|             } catch (error) {
 | |
|                 console.error('❌ Test failed:', error);
 | |
|             }
 | |
|         };
 | |
|         
 | |
|         console.log('💡 You can test the payment flow manually with: window.testPaymentFlow()');
 | |
|     </script>
 | |
|     
 | |
|     <!-- WASM Application -->
 | |
|     <script type="module">
 | |
|         async function run() {
 | |
|             try {
 | |
|                 // Load the WASM module for the Yew application
 | |
|                 const init = await import('./pkg/freezone_platform.js');
 | |
|                 await init.default();
 | |
|                 console.log('✅ Freezone Platform WASM application initialized');
 | |
|                 console.log('🏢 Company registration system ready');
 | |
|             } catch (error) {
 | |
|                 console.error('❌ Failed to initialize WASM application:', error);
 | |
|                 console.error('🔧 Make sure to build the WASM module with: trunk build');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         run();
 | |
|     </script>
 | |
| </body>
 | |
| </html> |