/* My Vital Cell — Checkout */
function Checkout({ onNav }) {
const { Button, Input, Select, Eyebrow, Badge } = window.VitalCellDesignSystem_fbb4ec;
const [items, setItems] = React.useState(() => window.VC_CART.get());
React.useEffect(() => {
const h = () => setItems(window.VC_CART.get());
window.addEventListener('vc-cart-change', h);
return () => window.removeEventListener('vc-cart-change', h);
}, []);
React.useEffect(() => { window.lucide && window.lucide.createIcons(); });
const [step, setStep] = React.useState(1); // 1 contact, 2 shipping, 3 payment, 4 review, 5 confirm
const [orderId, setOrderId] = React.useState('');
const [placing, setPlacing] = React.useState(false);
const [payError, setPayError] = React.useState('');
const [form, setForm] = React.useState({
email: '', firstName: '', lastName: '', organization: '',
address1: '', address2: '', city: '', region: '', postal: '', country: 'United States',
shipMethod: 'standard',
cardName: '', cardNumber: '', cardExp: '', cardCvc: '',
researchAck: false, termsAck: false, fdaAck: false,
});
const upd = (k) => (e) => setForm((f) => ({ ...f, [k]: e && e.target ? (e.target.type === 'checkbox' ? e.target.checked : e.target.value) : e }));
const subtotal = items.reduce((s, it) => s + (it.price || 0) * (it.qty || 1), 0);
const shipRates = { standard: 18, standard: 34, express: 62 };
const shipping = items.length ? shipRates[form.shipMethod] : 0;
const tax = Math.round(subtotal * 0.0725 * 100) / 100;
const total = subtotal + shipping + tax;
const fmt = (n) => '$' + n.toFixed(2);
const stepValid = {
1: /.+@.+\..+/.test(form.email) && form.firstName && form.lastName,
2: form.address1 && form.city && form.region && form.postal,
3: form.cardName && form.cardNumber.replace(/\s/g, '').length >= 13 && /^\d{2}\/\d{2}$/.test(form.cardExp) && form.cardCvc.length >= 3,
4: form.researchAck && form.termsAck && form.fdaAck,
};
const next = () => setStep((s) => Math.min(4, s + 1));
const back = () => setStep((s) => Math.max(1, s - 1));
const placeOrder = async () => {
setPlacing(true);
setPayError('');
try {
const stripe = await window.VC_STRIPE.getStripe();
const { clientSecret } = await window.VC_STRIPE.createPaymentIntent({
amount: Math.round(total * 100),
currency: 'usd',
items: items.map((it) => ({ name: it.name, qty: it.qty || 1, price: it.price })),
email: form.email,
});
const [expMonth, expYear] = form.cardExp.split('/');
const result = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: {
number: form.cardNumber.replace(/\s/g, ''),
exp_month: parseInt(expMonth, 10),
exp_year: parseInt('20' + expYear, 10),
cvc: form.cardCvc,
},
billing_details: {
name: form.cardName,
email: form.email,
address: { line1: form.address1, line2: form.address2, city: form.city, state: form.region, postal_code: form.postal, country: form.country },
},
},
__amount: Math.round(total * 100),
});
if (result.error) {
setPayError(result.error.message || 'Payment could not be processed.');
setStep(3);
setPlacing(false);
return;
}
const pi = result.paymentIntent;
const id = 'MVC-' + (pi.id.slice(-8) || Math.random().toString(36).slice(2, 7)).toUpperCase() + '-' + Date.now().toString().slice(-4);
setOrderId(id);
window.VC_CART.clear();
setStep(5);
setPlacing(false);
window.scrollTo({ top: 0, behavior: 'instant' });
} catch (err) {
setPayError((err && err.message) || 'Something went wrong. Please try again.');
setPlacing(false);
}
};
// Empty state
if (items.length === 0 && step !== 5) {
return (
Add research compounds to your cart to begin checkout. Every order is CoA-matched and shipped under shipping integrity. Your order is queued for our team. A confirmation with CoAs (available on request), shipping manifest and tracking will arrive at {form.email} within one business day.Your cart is empty
Thank you, {form.firstName || 'researcher'}.
Complete your order
{stepLabels.map((label, i) => {
const n = i + 1;
const done = step > n;
const active = step === n;
return (
{hint}
}