/* 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:
Your Razorpay payment is verified. We are unlocking your scan package...
{quote.tier_name}
Secure checkout by Razorpay. UPI, credit/debit cards, netbanking, wallets and international cards accepted.
Purchase a plan once and perform up to 3 complete scans on the same target website.
{plan.description}
{feeNote}
)}All payments are processed securely by Razorpay. Platform fee and applicable GST are shown before payment.