/* Calculator engine + UI components for NetSellerProfit */ const { useState, useEffect, useMemo, useRef } = React; // ---------- Platform definitions ---------- const PLATFORMS = { tiktok: { id: 'tiktok', name: 'TikTok Shop', color: '#22d3ee', fees: [ { key: 'commission', label: 'Commission', formula: '6% of revenue', calc: (i) => i.revenue * 0.06 }, { key: 'tx_fee', label: 'Transaction fee', formula: '$0.30 per order', calc: () => 0.30 }, ], }, amazon: { id: 'amazon', name: 'Amazon FBA', color: '#fb923c', fees: [ { key: 'referral', label: 'Referral fee', formula: '15% of revenue', calc: (i, t) => i.revenue * (t.referralPct / 100) }, { key: 'fba', label: 'FBA pick & pack', formula: 'flat per unit', calc: (i, t) => t.fbaFee }, ], }, etsy: { id: 'etsy', name: 'Etsy', color: '#f87171', fees: [ { key: 'listing', label: 'Listing fee', formula: '$0.20', calc: () => 0.20 }, { key: 'transaction', label: 'Transaction fee', formula: '6.5% of revenue', calc: (i) => i.revenue * 0.065 }, { key: 'processing', label: 'Payment processing', formula: '3% + $0.25', calc: (i) => i.revenue * 0.03 + 0.25 }, ], }, shopify: { id: 'shopify', name: 'Shopify', color: '#a78bfa', fees: [ { key: 'gateway', label: 'Gateway fee', formula: 'pct + flat', calc: (i, t) => i.revenue * (t.gatewayPct / 100) + t.gatewayFlat }, ], }, }; // ---------- Format helpers ---------- const fmt = (n) => { const sign = n < 0 ? '-' : ''; const abs = Math.abs(n); return sign + '$' + abs.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }; const fmtBig = (n) => { const sign = n < 0 ? '-' : ''; const abs = Math.abs(n); return sign + '$' + abs.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 }); }; // ---------- Compute ---------- function compute(platformId, inputs, tunables) { const p = PLATFORMS[platformId]; const platformFees = p.fees.map(f => ({ ...f, amount: f.calc(inputs, tunables) })); const platformFeesTotal = platformFees.reduce((s, f) => s + f.amount, 0); const variableCosts = [ { key: 'cogs', label: 'Cost of goods', amount: inputs.cogs }, { key: 'ads', label: 'Ad spend', amount: inputs.ads }, { key: 'shipping', label: 'Shipping', amount: inputs.shipping }, ]; const variableTotal = variableCosts.reduce((s, c) => s + c.amount, 0); const totalCost = platformFeesTotal + variableTotal; const netProfit = inputs.revenue - totalCost; const margin = inputs.revenue > 0 ? (netProfit / inputs.revenue) * 100 : 0; const breakeven = totalCost; // simplified: revenue must cover total cost return { platformFees, platformFeesTotal, variableCosts, variableTotal, totalCost, netProfit, margin, breakeven }; } // ---------- Donut chart ---------- function Donut({ segments, total }) { const radius = 56; const stroke = 14; const c = 2 * Math.PI * radius; let acc = 0; return ( {segments.map((seg, i) => { const pct = total > 0 ? seg.value / total : 0; const len = pct * c; const offset = c - acc; acc += len; return ( ); })} Total cost {fmtBig(total)} ); } // ---------- Number input ---------- function MoneyInput({ value, onChange, placeholder = '0.00', suffix }) { // Local string state lets the user type freely (e.g. "0.", "", "24.9") // without the controlled input snapping back to 0 mid-entry. const [display, setDisplay] = useState(() => value ? String(value) : ''); const displayRef = useRef(display); // Sync display when value changes from an external source (slider, scenario load). // Using a ref avoids stale-closure issues inside the effect. useEffect(() => { const parsed = parseFloat(displayRef.current) || 0; if (parsed !== value) { const next = value === 0 ? '' : String(value); displayRef.current = next; setDisplay(next); } }, [value]); const handleChange = (e) => { const raw = e.target.value; // Allow digits and a single decimal point only if (!/^\d*\.?\d*$/.test(raw)) return; displayRef.current = raw; setDisplay(raw); const num = parseFloat(raw); onChange(isNaN(num) ? 0 : num); }; const handleBlur = () => { // On blur normalise: drop trailing dot, treat empty as 0 const num = parseFloat(display) || 0; const next = num > 0 ? String(num) : ''; displayRef.current = next; setDisplay(next); onChange(num); }; return (
{!suffix && $} e.target.select()} onChange={handleChange} onBlur={handleBlur} /> {suffix && {suffix}}
); } // ---------- Calculator page ---------- function Calculator({ onNavigate }) { const [platform, setPlatform] = useState(() => localStorage.getItem('nsp.platform') || 'tiktok'); const [showCompare, setShowCompare] = useState(false); const [scenarios, setScenarios] = useState(() => { try { return JSON.parse(localStorage.getItem('nsp.scenarios') || '[]'); } catch { return []; } }); const [activeScenarioId, setActiveScenarioId] = useState(null); const [inputs, setInputs] = useState({ revenue: 49.99, cogs: 8.50, ads: 6.00, shipping: 4.20, }); const [tunables, setTunables] = useState({ referralPct: 15, fbaFee: 4.75, gatewayPct: 2.9, gatewayFlat: 0.30, }); useEffect(() => { localStorage.setItem('nsp.platform', platform); }, [platform]); const result = useMemo(() => compute(platform, inputs, tunables), [platform, inputs, tunables]); const allResults = useMemo(() => { return Object.keys(PLATFORMS).map(id => ({ id, name: PLATFORMS[id].name, ...compute(id, inputs, tunables), })); }, [inputs, tunables]); const bestPlatform = useMemo(() => { return allResults.reduce((best, r) => r.netProfit > best.netProfit ? r : best, allResults[0]); }, [allResults]); const updateInput = (k, v) => setInputs(s => ({ ...s, [k]: v })); const updateTunable = (k, v) => setTunables(s => ({ ...s, [k]: v })); const saveScenario = () => { const name = prompt('Name this scenario'); if (!name) return; const s = { id: Date.now(), name, platform, inputs, tunables }; const next = [...scenarios, s]; setScenarios(next); setActiveScenarioId(s.id); localStorage.setItem('nsp.scenarios', JSON.stringify(next)); }; const loadScenario = (s) => { setPlatform(s.platform); setInputs(s.inputs); setTunables(s.tunables); setActiveScenarioId(s.id); }; const deleteScenario = (id, e) => { e.stopPropagation(); const next = scenarios.filter(s => s.id !== id); setScenarios(next); localStorage.setItem('nsp.scenarios', JSON.stringify(next)); if (activeScenarioId === id) setActiveScenarioId(null); }; // Donut segments: platform fees (combined) + cogs + ads + shipping const donutSegments = [ { key: 'platform', label: 'Platform fees', value: result.platformFeesTotal, color: PLATFORMS[platform].color }, { key: 'cogs', label: 'Cost of goods', value: inputs.cogs, color: '#94a3b8' }, { key: 'ads', label: 'Ad spend', value: inputs.ads, color: '#fbbf24' }, { key: 'shipping', label: 'Shipping', value: inputs.shipping, color: '#64748b' }, ]; return (
Live calculation

Master the Math of Your Multi-Channel Business. Know exactly what you keep.

Stop guessing your margins. NetSellerProfit is a high-precision estimator built to strip away the complexity of selling online. Instantly calculate your true take-home pay across TikTok Shop, Etsy, Amazon FBA, and Shopify by accounting for real-time platform fees, shipping costs, and ad spend.

{/* Scenario chips */} {scenarios.length > 0 && (
Scenarios {scenarios.map(s => ( ))}
)}
{/* LEFT: inputs */}

Channel

Live
{Object.values(PLATFORMS).map(p => ( ))}
updateInput('revenue', v)} />
updateInput('cogs', v)} />
updateInput('ads', v)} />
updateInput('shipping', v)} />
{/* Platform-specific tunables */} {platform === 'amazon' && (
updateTunable('referralPct', v)} suffix="%" />
updateTunable('fbaFee', v)} />
)} {platform === 'shopify' && (
updateTunable('gatewayPct', v)} suffix="%" />
updateTunable('gatewayFlat', v)} />
)} {/* What-If slider */}
What-if · Ad spend {fmt(inputs.ads)}
updateInput('ads', parseFloat(e.target.value))} />
$0 $5 $10 $15 {fmtBig(Math.max(20, inputs.revenue * 0.6))}
{/* Breakdown */}

Cost breakdown · {PLATFORMS[platform].name}

Gross revenue
unit sale price
{fmt(inputs.revenue)}
{result.platformFees.map(f => (
{f.label}
{f.formula}
{fmt(f.amount)}
))} {result.variableCosts.map(c => (
{c.label}
variable
{fmt(c.amount)}
))}
Net profit
margin {result.margin.toFixed(1)}%
{fmt(result.netProfit)}
{/* Comparison */}

Cross-channel comparison

{showCompare && (
{allResults.map(r => (
setPlatform(r.id)} style={{ cursor: 'pointer' }} > {r.id === bestPlatform.id && Best}
{r.name}
= 0 ? 'var(--success)' : 'var(--danger)' }}>{fmt(r.netProfit)}
margin {r.margin.toFixed(1)}%
))}
)} {!showCompare && (

Run this same SKU across all four channels and see who keeps the most cents per sale.

)}
{/* RIGHT: results */}
Net profit · per unit
{fmt(result.netProfit)}
Margin
{result.margin.toFixed(1)}%
Total cost
{fmt(result.totalCost)}
Break-even
{fmt(result.breakeven)}

Where the dollar goes

{donutSegments.map(s => { const pct = result.totalCost > 0 ? (s.value / result.totalCost) * 100 : 0; return (
{s.label} {pct.toFixed(0)}%
); })}
You keep {result.margin.toFixed(0)}%

Detailed fee breakdown

Gross revenue {fmt(inputs.revenue)}
Marketplace commission i{ platform === 'tiktok' ? 'TikTok Shop charges a 6% commission on every sale.' : platform === 'amazon' ? `Amazon's referral fee varies by category — currently set to ${tunables.referralPct}% of revenue.` : platform === 'etsy' ? 'Etsy charges a 6.5% transaction fee on every sale.' : 'Shopify itself charges no commission — only the gateway fee shown below.' } −{fmt( platform === 'tiktok' ? inputs.revenue * 0.06 : platform === 'amazon' ? inputs.revenue * (tunables.referralPct / 100) : platform === 'etsy' ? inputs.revenue * 0.065 : 0 )}
Processing fees i{ platform === 'tiktok' ? 'A flat $0.30 transaction fee per order.' : platform === 'amazon' ? `FBA pick & pack fee of ${fmt(tunables.fbaFee)} per unit.` : platform === 'etsy' ? 'Etsy stacks a $0.20 listing fee plus 3% + $0.25 payment processing per order.' : `Default Shopify Payments: ${tunables.gatewayPct}% + ${fmt(tunables.gatewayFlat)} per transaction.` } −{fmt( platform === 'tiktok' ? 0.30 : platform === 'amazon' ? tunables.fbaFee : platform === 'etsy' ? (0.20 + inputs.revenue * 0.03 + 0.25) : (inputs.revenue * (tunables.gatewayPct / 100) + tunables.gatewayFlat) )}
Fulfilment costs iCost of goods sold plus outbound shipping to the customer. Add packaging here if it isn't already in your landed COGS. −{fmt(inputs.cogs + inputs.shipping)}
Ad investment iMarketing drag — your customer acquisition cost per unit. Use real spend, not blended ROAS. −{fmt(inputs.ads)}
The bottom line {fmt(result.netProfit)}
Best channel for this SKU
{bestPlatform.name}
{fmt(bestPlatform.netProfit)}
{(bestPlatform.netProfit - result.netProfit).toFixed(2) === '0.00' ? 'You are on the optimal channel.' : `${fmt(bestPlatform.netProfit - result.netProfit)} more per unit vs. current.`}
{/* ── FAQ ──────────────────────────────────────────────────── */}
); } // ---------- Calculator FAQ ---------- function CalculatorFAQ({ onNavigate }) { const [open, setOpen] = useState(null); const toggle = (i) => setOpen(o => o === i ? null : i); const faqs = [ { q: 'How does the NetSellerProfit calculator work?', a: `The calculator applies the current fee schedule for each platform directly to your inputs. You enter a sale price (revenue per unit), your cost of goods, ad spend, and shipping cost. The engine then deducts every applicable fee — marketplace commission, payment processing, and fulfilment costs — and returns your true net profit and margin in real time. Nothing is estimated or averaged: every formula matches the platform's published rate card for 2026.`, }, { q: 'What is net profit, and why does it differ from gross profit?', a: `Gross profit subtracts only the cost of goods sold from revenue. Net profit goes further: it strips out every additional cost required to make and deliver the sale, including platform commissions, payment processing fees, outbound shipping, and ad spend. A product with a 60% gross margin can easily deliver under 10% net margin once marketplace fees and advertising costs are accounted for. NetSellerProfit always shows you the net number — the amount that actually lands in your bank account.`, }, { q: 'What fees does TikTok Shop charge sellers?', a: `TikTok Shop currently charges a 6% commission on every completed order, plus a $0.30 flat transaction fee per sale. There is no monthly subscription fee for most sellers. If you run a TikTok Shop affiliate programme, the creator commission (typically 5–20% of sale price) comes out of your pocket on top of TikTok's own 6% — enter that additional cost in the Ad Spend field as a per-unit affiliate commission. New seller promotions can temporarily reduce the commission rate; always confirm current rates in your TikTok Shop Seller Center.`, }, { q: 'What is the Amazon FBA referral fee and how does it vary by category?', a: `Amazon charges a referral fee on every sale, calculated as a percentage of the total sale price (including shipping). The rate varies by product category: most categories sit at 15%, but it ranges from 8% (consumer electronics) to 20%+ (Amazon Device accessories). On top of the referral fee, FBA sellers pay a per-unit fulfilment fee that depends on item size and weight — standard small items typically run $3–$7. The calculator defaults to 15% referral and $4.75 FBA fee; adjust both tunables to match your specific category and package dimensions for an accurate result.`, }, { q: 'How are Etsy fees calculated?', a: `Etsy stacks three fees on every sale. First, a $0.20 listing fee is charged each time an item is listed or auto-renewed (every four months). Second, a 6.5% transaction fee is applied to the total order amount including shipping. Third, Etsy Payments charges 3% plus $0.25 per transaction for payment processing. The calculator combines all three into a single cost line so you can see the total platform drag at a glance. Note that Etsy also charges a 15% Offsite Ads fee on orders driven by Etsy's own external marketing if your shop earned over $10,000 in the past 12 months — you can approximate this by increasing the Ad Spend field.`, }, { q: 'Does Shopify charge a transaction fee?', a: `Shopify itself does not charge a per-sale commission the way Amazon or Etsy does. However, you pay a payment processing fee to your gateway — typically 2.9% + $0.30 per transaction with Shopify Payments on the Basic plan. If you use a third-party gateway, Shopify also charges an additional 2% transaction fee (0.5% on Advanced). The calculator defaults to a 2.9% + $0.30 gateway configuration. You can override both the percentage and the flat fee in the Shopify tunables panel to match your plan and processor. Always include your Shopify monthly subscription cost ($29–$299/month) in your overhead model for an accurate true cost of channel.`, }, { q: 'What should I include in my Cost of Goods (COGS)?', a: `COGS — or Cost of Goods Sold — should represent the fully landed cost to get one unit into the fulfilment centre or ready to ship. This includes the factory price or manufacturing cost, inbound freight (ocean, air, or domestic trucking), import duties and customs clearance fees, inspection costs, and any prep or labelling fees. It should not include outbound shipping to the customer (enter that separately in the Shipping field) or advertising spend (enter that in Ad Spend). A common mistake is using only the supplier invoice price, which understates COGS by 15–40% once freight and duties are added.`, }, { q: 'How should I calculate Ad Spend per unit?', a: `Ad Spend in this calculator is the per-unit customer acquisition cost (CAC). To derive it: divide your total ad spend for a product in a period by the number of units sold in that period. For example, if you spent $500 on TikTok Ads in a week and sold 80 units, your per-unit ad cost is $6.25. This is distinct from ROAS (Return on Ad Spend), which is a ratio rather than a dollar figure. Using a blended ROAS will understate your true advertising cost unless you convert it to a per-unit dollar amount first. If you run no paid advertising, set this field to $0 — but remember that affiliate commissions on TikTok Shop or Etsy Offsite Ads should be added here.`, }, { q: 'Which platform gives me the best margins?', a: `The answer depends entirely on your product category, price point, and fulfilment model. As a general rule: Shopify offers the lowest platform fees (no commission, gateway only) but requires you to drive your own traffic — making marketing costs higher. Amazon FBA charges a high referral fee (8–15%) plus fulfilment fees, but provides enormous organic reach. Etsy stacks moderate fees and provides a built-in audience for handmade and vintage goods. TikTok Shop has competitive fees (6%) and explosive organic reach through short video, but the audience skews toward impulse purchases under $50. Use the Cross-Channel Comparison panel to run your actual SKU numbers against all four platforms simultaneously.`, }, { q: 'What is the Break-Even figure in the results panel?', a: `The Break-Even figure shown in the results panel is the minimum revenue per unit required to cover all your costs at zero profit. It is calculated as the sum of all variable costs: platform fees, COGS, ad spend, and shipping. If your sale price equals the break-even number, you are neither making nor losing money on each unit. Any sale price above break-even generates net profit; below it, you are losing money per unit sold. Use this number to set a minimum viable price floor when negotiating with suppliers or planning promotional pricing.`, }, { q: 'How accurate are the fee calculations?', a: `NetSellerProfit uses the published Q1 2026 fee schedules for all four platforms. Platform fees do change — Amazon adjusts FBA rates annually, TikTok Shop has modified its commission structure multiple times since launch, and Etsy updated its transaction fee from 5% to 6.5% in 2022. We update rate tables with each significant change. You should always cross-reference the final number against your platform's own seller fee calculator or recent payout statement, especially for Amazon FBA where dimensional weight and category-specific rates can produce significant variation. The calculator is a high-precision estimation tool, not a substitute for your accountant.`, }, { q: 'Can I save and compare multiple scenarios?', a: `Yes. After configuring any set of inputs, click the "Save Scenario" button in the Cost Breakdown panel and give it a name (for example, "Summer sale price", "Supplier A quote", or "FBA vs FBM"). Saved scenarios appear as chips above the calculator. Click any chip to instantly reload that configuration. You can save as many scenarios as you like — they persist in your browser's local storage, so they survive page reloads without requiring an account. To compare two scenarios against each other, load one, note the net profit, then load the other and use the Cross-Channel Comparison panel to see the full picture.`, }, ]; return (
Common questions

Frequently Asked Questions

Everything you need to know about calculating net profit across TikTok Shop, Amazon FBA, Etsy, and Shopify.

{faqs.map((item, i) => (
toggle(i)}>
{item.q} {open === i ? '−' : '+'}
{open === i && (
{item.a}
)}
))}

Need a deeper dive?{' '}

); } window.Calculator = Calculator; window.PLATFORMS = PLATFORMS; window.fmt = fmt;