/* eslint-disable react-hooks/exhaustive-deps, react-hooks/set-state-in-effect, react-hooks/immutability */ import React, { useState, useEffect } from 'react'; import { useAuth } from './AuthContext'; import { useNavigate } from 'react-router-dom'; import { Shield, Zap, Check, Target, ArrowRight, Briefcase } from 'lucide-react'; const RAZORPAY_SCRIPT = 'https://checkout.razorpay.com/v1/checkout.js'; const FALLBACK_TIERS = [ { id: 'quick', name: 'Quick Scan', monthly_price: 499, monthly_price_inr: 49900 }, { id: 'advanced', name: 'Advanced Scan', monthly_price: 4499, monthly_price_inr: 399900 }, { id: 'deep', name: 'Deep Scan', monthly_price: 9999, monthly_price_inr: 899900 }, { id: 'enterprise', name: 'Custom Solutions', monthly_price: 0, monthly_price_inr: 0 } ]; let razorpayScriptPromise = null; const loadRazorpayScript = () => { if (typeof window === 'undefined') return Promise.reject(new Error('Browser only')); if (window.Razorpay) return Promise.resolve(true); if (razorpayScriptPromise) return razorpayScriptPromise; razorpayScriptPromise = new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = RAZORPAY_SCRIPT; script.async = true; script.onload = () => resolve(true); script.onerror = () => { razorpayScriptPromise = null; reject(new Error('Unable to load the Razorpay checkout. Check your network or ad-blocker.')); }; document.body.appendChild(script); }); return razorpayScriptPromise; }; const detectCurrency = () => { try { const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; const locale = new Intl.Locale(navigator.language || 'en-US'); if (timeZone === 'Asia/Kolkata' || locale.region === 'IN') return 'INR'; } catch { // Intl.Locale unsupported - fall through to USD } return 'USD'; }; const formatMoney = (minor, currency) => { const value = Number(minor || 0) / 100; if (currency === 'INR') { return `₹${value.toLocaleString('en-IN', { minimumFractionDigits: value % 1 === 0 ? 0 : 2, maximumFractionDigits: 2 })}`; } return `$${value.toFixed(2)}`; }; export default function PricingSection({ embedded = false, hideCurrentPlan = false }) { const { user, token, refreshAccessToken } = useAuth(); const navigate = useNavigate(); const [loadingPlan, setLoadingPlan] = useState(null); const [paymentSuccess, setPaymentSuccess] = useState(false); const [paymentError, setPaymentError] = useState(null); const [dynamicPrices, setDynamicPrices] = useState({}); const [currency, setCurrency] = useState(detectCurrency); const [quote, setQuote] = useState(null); const [orderLoading, setOrderLoading] = useState(false); const [feeConfig, setFeeConfig] = useState({ platform_fee_percent: 2, gst_percent: 18 }); useEffect(() => { fetch('/api/billing/tiers') .then(res => res.json()) .then(data => { const prices = {}; data.forEach(t => { prices[t.id] = { monthly_price: t.monthly_price, monthly_price_inr: t.monthly_price_inr }; }); setDynamicPrices(prices); }) .catch(err => console.error('Failed to load dynamic prices', err)); fetch('/api/billing/config') .then(res => (res.ok ? res.json() : null)) .then(data => { if (data && data.platform_fee_percent !== undefined) { setFeeConfig({ platform_fee_percent: data.platform_fee_percent, gst_percent: data.gst_percent || 0 }); } }) .catch(() => {}); }, []); const authFetch = async (url, options = {}) => { let res = await fetch(url, options); if (res.status === 401 && refreshAccessToken) { const newToken = await refreshAccessToken(); if (newToken) { res = await fetch(url, { ...options, headers: { ...(options.headers || {}), 'Authorization': `Bearer ${newToken}` } }); } } return res; }; const priceFor = (planId) => { if (planId === 'enterprise') return 'Custom'; const tier = dynamicPrices[planId] || FALLBACK_TIERS.find(t => t.id === planId); if (!tier) return '—'; const minor = currency === 'INR' ? tier.monthly_price_inr : tier.monthly_price; return formatMoney(minor, currency); }; const handleSubscribe = async (planId) => { if (planId === 'enterprise') { window.location.href = '#booking'; return; } if (!user) { navigate('/login'); return; } setLoadingPlan(planId); try { const res = await authFetch('/api/billing/quote', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tier_id: planId, currency }) }); const data = await res.json(); if (!res.ok) { setPaymentError(data.message || 'Could not calculate the order total.'); setLoadingPlan(null); return; } setQuote(data); } catch (err) { console.error(err); setPaymentError('Could not reach the payment service. Please try again.'); } setLoadingPlan(null); }; const verifyRazorpayPayment = async (response) => { try { const res = await authFetch('/api/billing/verify-payment', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ razorpay_order_id: response.razorpay_order_id, razorpay_payment_id: response.razorpay_payment_id, razorpay_signature: response.razorpay_signature }) }); const data = await res.json().catch(() => ({})); if (res.ok && data.status === 'success') { setPaymentSuccess(true); setQuote(null); setLoadingPlan(null); setTimeout(() => { window.location.reload(); }, 3500); return; } setPaymentError(data.message || 'We could not verify the payment. If money was deducted, contact support with your payment id.'); } catch (err) { console.error(err); setPaymentError('Payment verification failed due to a network error. Please contact support before retrying.'); } setLoadingPlan(null); }; const confirmPayment = async () => { if (!quote) return; setOrderLoading(true); try { const res = await authFetch('/api/billing/create-order', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ tier_id: quote.tier_id, currency: quote.currency }) }); const order = await res.json().catch(() => ({})); if (!res.ok || !order.order_id) { setPaymentError(order.message || 'Could not start the payment. Please try again.'); setOrderLoading(false); return; } await loadRazorpayScript(); const options = { key: order.key_id, amount: order.amount, currency: order.currency, name: 'LarShield', description: order.description, order_id: order.order_id, prefill: order.prefill, notes: { tier_id: order.tier.id }, theme: { color: '#2563eb' }, modal: { ondismiss: () => { setLoadingPlan(null); setOrderLoading(false); } }, handler: (response) => { verifyRazorpayPayment(response); } }; const checkout = new window.Razorpay(options); checkout.on('payment.failed', (resp) => { const description = resp?.error?.description || 'Payment failed. Please try a different method.'; setPaymentError(description); setLoadingPlan(null); }); setQuote(null); setOrderLoading(false); checkout.open(); } catch (err) { console.error(err); setPaymentError(err.message || 'Could not open the Razorpay checkout.'); setOrderLoading(false); setLoadingPlan(null); } }; const plans = [ { id: 'quick', name: 'Quick Scan', icon: , description: 'Essential reconnaissance & basic security health checks.', scanners: '13 Security Modules', features: [ '3 Scans for 1 Target Website', 'Fast Recon (Headers, Nmap Quick, WHOIS, WAF)', 'DNS Security & SSL/TLS Audit', 'Cookies, CSP & Clickjacking Checks', 'Git Repository Exposure Detection', 'Instant PDF Report Export' ], popular: false }, { id: 'advanced', name: 'Advanced Scan', icon: , description: 'Core vulnerability auditing & deep path analysis.', scanners: '36 Security Modules', popular: false, features: [ '3 Scans for 1 Target Website', 'Everything in Quick Scan', 'Subdomain Discovery (Subfinder, Amass, crt.sh)', 'XSS, SQL Injection & Path Traversal Fuzzing', 'REST API, Cloud Bucket & Secrets Leak Scan', 'CVE Database & Nikto Web Server Engine', 'AI Remediation & Strategy Generator' ] }, { id: 'deep', name: 'Deep Scan', icon: , description: 'Exhaustive threat inspection & active DAST attack simulation.', scanners: '89 Security Modules', popular: true, features: [ '3 Scans for 1 Target Website', 'Everything in Advanced Scan', 'Full 65535-Port Nmap with NSE Vulnerability Scripts', 'Nuclei Scanner Engine (Critical/High/Med/Low)', 'OWASP ZAP DAST Engine (Active Attack Mode)', 'XXE, SSTI, IDOR, GraphQL & Business Logic Flaws', 'HTTP/2 Desync, JS Supply Chain & SAML/OAuth Bypasses' ] }, { id: 'enterprise', name: 'Custom Solutions', icon: , price: 'Custom', description: 'Tailored VAPT services and dedicated security infrastructure.', scanners: 'Expert Manual VAPT', popular: false, features: [ 'Everything in Deep Scan', 'Quarterly Manual VAPT', 'Dedicated Security Architect', 'Custom Compliance Reporting', 'On-Premise Deployment Options', '1-Hour SLA' ] } ]; const gstApplies = currency === 'INR'; const feeNote = gstApplies ? `+ ${feeConfig.platform_fee_percent}% platform fee + ${feeConfig.gst_percent}% GST on the fee` : `+ ${feeConfig.platform_fee_percent}% platform fee at checkout`; return (
{paymentError && (
error {paymentError}
)} {paymentSuccess && (
check_circle

Payment Successful

Your Razorpay payment is verified. We are unlocking your scan package...

)} {quote && (

Order Summary

{quote.tier_name}

Service price {formatMoney(quote.base_amount, quote.currency)}
Platform fee ({quote.fee_percent}%) {formatMoney(quote.platform_fee, quote.currency)}
{quote.tax_amount > 0 && (
GST on platform fee ({quote.gst_percent}%) {formatMoney(quote.tax_amount, quote.currency)}
)}
Total payable {formatMoney(quote.total_amount, quote.currency)}
verified Includes {quote.scans_included} scans for 1 target website

Secure checkout by Razorpay. UPI, credit/debit cards, netbanking, wallets and international cards accepted.

)} {/* Background Soft Gradients (Adapted to Light Theme) */}
LarShield Target Scan Packages

Single Domain Scan Packages

Purchase a plan once and perform up to 3 complete scans on the same target website.

verified 1 Plan Purchase = 3 Scans Allowed for 1 Target Website
{['INR', 'USD'].map((code) => ( ))}
{currency === 'INR' ? 'India (UPI, cards, netbanking)' : 'International cards'}
{/* Pricing Cards */}
{plans.map((plan) => { const isCurrentPlan = hideCurrentPlan ? false : user?.subscription_tier === plan.id; return (
{plan.popular && (
Recommended
)}
{React.cloneElement(plan.icon, { className: 'w-5 h-5 text-primary' })}

{plan.name}

{plan.description}

{priceFor(plan.id)}
{plan.id !== 'enterprise' && (

{feeNote}

)}
{plan.scanners}
    {plan.features.map((feature, idx) => (
  • {feature}
  • ))}
); })}

All payments are processed securely by Razorpay. Platform fee and applicable GST are shown before payment.

); }