freezone/platform/static/js/stripe-integration.js
2025-06-27 04:13:31 +02:00

399 lines
16 KiB
JavaScript

// Stripe Integration for Company Registration
// This file handles the Stripe Elements integration for the Yew WASM application
let stripe;
let elements;
let paymentElement;
// Stripe publishable key - this should be set from the server or environment
const STRIPE_PUBLISHABLE_KEY = 'pk_test_51234567890abcdef'; // Replace with actual 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');
}
});
// Initialize Stripe Elements with client secret
window.initializeStripeElements = async function(clientSecret) {
console.log('🔧 Initializing Stripe Elements with client secret:', clientSecret);
try {
if (!stripe) {
throw new Error('Stripe not initialized');
}
// 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');
}
});
// Handle loading state
paymentElement.on('loaderstart', () => {
console.log('🔄 Stripe Elements loading...');
});
paymentElement.on('loaderror', (event) => {
console.error('❌ Stripe Elements load error:', event.error);
showAdBlockerGuidance(event.error.message || 'Failed to load payment form');
});
console.log('✅ Stripe Elements initialized successfully');
return true;
} catch (error) {
console.error('❌ Error initializing Stripe Elements:', error);
// Check if this might be an ad blocker issue
const isAdBlockerError = error.message && (
error.message.includes('blocked') ||
error.message.includes('Failed to fetch') ||
error.message.includes('ERR_BLOCKED_BY_CLIENT') ||
error.message.includes('network') ||
error.message.includes('CORS')
);
if (isAdBlockerError) {
showAdBlockerGuidance(error.message || 'Failed to load payment form');
} else {
// Show generic error for non-ad-blocker issues
const errorElement = document.getElementById('payment-errors');
if (errorElement) {
errorElement.innerHTML = `
<div class="alert alert-danger alert-dismissible" role="alert">
<i class="bi bi-exclamation-triangle me-2"></i>
<strong>Payment Form Error:</strong> ${error.message || 'Failed to load payment form'}<br><br>
Please refresh the page and try again. If the problem persists, contact support.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
errorElement.style.display = 'block';
}
}
throw error;
}
};
// Create payment intent on server
window.createPaymentIntent = async function(formDataJson) {
console.log('💳 Creating 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:', formData);
const response = await fetch('/company/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
});
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 };
}
throw new Error(errorData.error || 'Failed to create payment intent');
}
const responseData = await response.json();
console.log('✅ Payment intent created:', responseData);
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);
throw error;
}
};
// Confirm payment with Stripe
window.confirmStripePayment = async function(clientSecret) {
console.log('🔄 Confirming payment...');
try {
// Ensure elements are ready before submitting
if (!elements) {
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);
// Provide more specific error messages
if (submitError.type === 'validation_error') {
throw new Error('Please check your payment details and try again.');
} else if (submitError.type === 'card_error') {
throw new Error(submitError.message || 'Card error. Please check your card details.');
} else {
throw new Error(submitError.message || 'Payment form validation failed.');
}
}
console.log('✅ Step 1 complete: Elements submitted successfully');
console.log('🔄 Step 2: Confirming payment...');
// 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' // Handle success without redirect if possible
});
if (error) {
// Payment failed - redirect to failure page
console.error('Payment confirmation failed:', error);
window.location.href = `${window.location.origin}/company/payment-failure`;
return false;
}
if (paymentIntent && paymentIntent.status === 'succeeded') {
// Payment succeeded
console.log('✅ Payment completed successfully:', paymentIntent.id);
// Clear saved form data since registration is complete
localStorage.removeItem('freezone_company_registration');
// Redirect to success page with payment details
window.location.href = `${window.location.origin}/company/payment-success?payment_intent=${paymentIntent.id}&payment_intent_client_secret=${clientSecret}`;
return true;
} else if (paymentIntent && paymentIntent.status === 'requires_action') {
// Payment requires additional authentication (3D Secure, etc.)
console.log('🔐 Payment requires additional authentication');
// Stripe will handle the authentication flow automatically
return false; // Don't redirect yet
} else {
// Unexpected status - redirect to failure page
console.error('Unexpected payment status:', paymentIntent?.status);
window.location.href = `${window.location.origin}/company/payment-failure`;
return false;
}
} catch (error) {
console.error('❌ Payment confirmation error:', error);
throw error;
}
};
// Show comprehensive ad blocker guidance
function showAdBlockerGuidance(errorMessage) {
const errorElement = document.getElementById('payment-errors');
if (!errorElement) return;
// Detect browser type for specific instructions
const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
const isFirefox = /Firefox/.test(navigator.userAgent);
const isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
const isEdge = /Edg/.test(navigator.userAgent);
let browserSpecificInstructions = '';
if (isChrome) {
browserSpecificInstructions = `
<strong>Chrome Instructions:</strong><br>
1. Click the shield icon 🛡️ in the address bar<br>
2. Select "Allow" for this site<br>
3. Or go to Settings → Privacy → Ad blockers<br>
`;
} else if (isFirefox) {
browserSpecificInstructions = `
<strong>Firefox Instructions:</strong><br>
1. Click the shield icon 🛡️ in the address bar<br>
2. Turn off "Enhanced Tracking Protection" for this site<br>
3. Or disable uBlock Origin/AdBlock Plus temporarily<br>
`;
} else if (isSafari) {
browserSpecificInstructions = `
<strong>Safari Instructions:</strong><br>
1. Go to Safari → Preferences → Extensions<br>
2. Temporarily disable ad blocking extensions<br>
3. Or add this site to your allowlist<br>
`;
} else if (isEdge) {
browserSpecificInstructions = `
<strong>Edge Instructions:</strong><br>
1. Click the shield icon 🛡️ in the address bar<br>
2. Turn off tracking prevention for this site<br>
3. Or disable ad blocking extensions<br>
`;
}
errorElement.innerHTML = `
<div class="alert alert-warning alert-dismissible" role="alert">
<div class="d-flex align-items-start">
<i class="bi bi-shield-exclamation fs-1 text-warning me-3 mt-1"></i>
<div class="flex-grow-1">
<h5 class="alert-heading mb-3">🛡️ Ad Blocker Detected</h5>
<p class="mb-3"><strong>Error:</strong> ${errorMessage}</p>
<p class="mb-3">Your ad blocker or privacy extension is preventing the secure payment form from loading. This is normal security behavior, but we need to process your payment securely through Stripe.</p>
<div class="row">
<div class="col-md-6">
<h6>🔧 Quick Fix:</h6>
${browserSpecificInstructions}
</div>
<div class="col-md-6">
<h6>🔒 Why This Happens:</h6>
• Ad blockers block payment tracking<br>
• Privacy extensions block third-party scripts<br>
• This protects your privacy normally<br>
• Stripe needs access for secure payments<br>
</div>
</div>
<div class="mt-3 p-3 rounded">
<h6>✅ Alternative Solutions:</h6>
<div class="row">
<div class="col-md-4">
<strong>1. Incognito/Private Mode</strong><br>
<small class="text-muted">Usually has fewer extensions</small>
</div>
<div class="col-md-4">
<strong>2. Different Browser</strong><br>
<small class="text-muted">Try Chrome, Firefox, or Safari</small>
</div>
<div class="col-md-4">
<strong>3. Mobile Device</strong><br>
<small class="text-muted">Often has fewer blockers</small>
</div>
</div>
</div>
<div class="mt-3 text-center">
<button type="button" class="btn btn-primary me-2" onclick="location.reload()">
<i class="bi bi-arrow-clockwise me-1"></i>Refresh & Try Again
</button>
<button type="button" class="btn btn-outline-secondary" onclick="showContactInfo()">
<i class="bi bi-headset me-1"></i>Contact Support
</button>
</div>
<div class="mt-2 text-center">
<small class="text-muted">
<i class="bi bi-shield-check me-1"></i>
We use Stripe for secure payment processing. Your payment information is encrypted and safe.
</small>
</div>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
errorElement.style.display = 'block';
// Scroll to error
errorElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Add visual indication
const paymentCard = document.querySelector('#payment-information-section');
if (paymentCard) {
paymentCard.style.borderColor = '#ffc107';
paymentCard.style.borderWidth = '2px';
paymentCard.classList.add('border-warning');
}
}
// Show contact information
function showContactInfo() {
alert('Contact Support:\n\nEmail: support@hostbasket.com\nPhone: +1 (555) 123-4567\nLive Chat: Available 24/7\n\nPlease mention "Payment Form Loading Issue" when contacting us.');
}
// Export functions for use by Rust/WASM
window.stripeIntegration = {
initializeElements: window.initializeStripeElements,
createPaymentIntent: window.createPaymentIntent,
confirmPayment: window.confirmStripePayment
};
console.log('✅ Stripe integration script ready');