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> |