Error Tracking for E-Commerce Checkout Flows: Catching the Bugs That Kill Sales
A silent JS error in your checkout can bleed thousands before anyone notices. Here's how to catch it.
The Slack message came in at 4:17pm on a Friday — because of course it did.
"Conversions are down 23% since yesterday. Marketing says nothing changed. Did we deploy?"
We'd deployed. A minor update to the payment form's loading spinner. Nothing that should've touched the actual payment logic. I pulled up Sentry, expecting the problem to jump out at me. Error volume looked normal. No spike. Nothing flagged.
I pulled up GA4. Checkout funnel showed users making it to the payment step, then vanishing. Cart abandonment had jumped from 31% to 54% on that step alone. But GA4 couldn't tell me why. It just knew they left.
Three hours later — after exporting CSVs, matching timestamps by hand, and a lot of swearing — I found it.
A race condition in the Stripe SDK initialization.
The new spinner was rendering before the payment element mounted, and on slower connections, Stripe's clientSecret wasn't ready when the component tried to access it. No visible error to the user. Just a frozen "Continue" button that did nothing. (I'm still a little bitter about this one. The spinner looked great.)
That's when I stopped treating error tracking and checkout analytics as separate concerns.
What We're Building
By the end of this tutorial, you'll have:
- Checkout-specific error tracking that captures exceptions only on payment-critical pages
- Funnel step tagging that links errors to cart_review, shipping_form, payment_form stages
- Cart value attribution so you can calculate actual revenue at risk
- Real-time alerts when error rates spike on high-value checkout steps
The whole setup takes about 45 minutes and works with Shopify, BigCommerce, WooCommerce, and custom React/Vue/Next.js checkouts. If you're already running JustAnalytics for site analytics, you'll add maybe 20 lines of code. If you're on Sentry + GA4, this guide shows you what you're missing — and how to get it without the CSV export dance I've come to deeply resent. We've covered the unified observability approach in detail if you want the full rationale.
Prerequisites
- A JustAnalytics account (free tier works fine for most stores)
- Access to your checkout page templates or component code
- Basic familiarity with JavaScript event tracking
- Optional: Stripe, PayPal, or Braintree SDK documentation handy for payment-specific edge cases
Step 1: Instrument Your Checkout Funnel Steps
Before you can correlate errors with checkout abandonment, you need to know which step the user was on when something broke.
Drop this into each checkout page or component:
// On your cart review page
import { JA } from '@justanalytics/browser';
useEffect(() => {
JA.track('funnel_step', {
step: 'cart_review',
cart_value: cart.total,
item_count: cart.items.length,
currency: 'USD'
});
}, []);
Repeat for each step: shipping_form, payment_form, order_confirmation. The step names are arbitrary — pick whatever matches your mental model. I've seen teams use step_1, step_2, step_3. Works fine, just less readable in dashboards.
The cart_value property is critical. When you query "sessions with errors on payment_form," you can sum that value to see exactly how much revenue was at risk. Even a small error rate can add up quickly on a high-volume checkout flow.
Why this matters: Sentry tracks errors globally. You'll see "TypeError on /checkout" but not "TypeError on payment_form when cart was $347." That context is what separates actionable debugging from noise. And honestly? It's the difference between "we should probably look at that sometime" and "fix this now, we're losing money." For teams handling high call volumes alongside e-commerce, VeloCalls applies similar funnel tracking to phone conversions.
Step 2: Enable Checkout-Specific Error Capture
JustAnalytics captures unhandled exceptions by default. But here's what bit me: checkout flows often have handled errors that still kill conversions — payment declines, validation failures, timeout recoveries that technically "work" but frustrate users into leaving. The error doesn't show up in your dashboard because your code caught it. Great. User still left.
Add explicit error tracking for these:
// Wrap your payment submission
async function handlePaymentSubmit() {
JA.track('funnel_step', { step: 'payment_submit_attempt', cart_value: cart.total });
try {
const result = await stripe.confirmPayment({ /* ... */ });
if (result.error) {
// Stripe declined or errored — capture it
JA.track('checkout_error', {
step: 'payment_form',
error_type: 'stripe_decline',
error_code: result.error.code,
error_message: result.error.message,
cart_value: cart.total
});
// Show user-facing error, but we've logged the real cause
}
} catch (err) {
// Network failure, SDK crash, or something worse
JA.track('checkout_error', {
step: 'payment_form',
error_type: 'exception',
error_message: err.message,
cart_value: cart.total
});
throw err; // Let the global handler also catch it
}
}
Gotcha: Stripe's confirmPayment returns errors in the response object, not as thrown exceptions. If you only rely on try/catch, you'll miss most payment failures. Same pattern applies to PayPal's SDK and Braintree's Drop-in. Read your payment provider's error handling docs — they're all slightly different, and the differences matter.
I learned this the hard way. Twice.
Step 3: Set Up the Correlation Query
Now you've got funnel steps and errors flowing into the same event stream. Here's how to find the bugs killing your conversions. (I'm not going to pretend I wrote this SQL from scratch — I borrowed the pattern from our docs and tweaked it. You should too.)
In the JustAnalytics dashboard, create a saved query:
SELECT
properties->>'step' as checkout_step,
COUNT(*) as total_sessions,
COUNT(CASE WHEN event = 'checkout_error' THEN 1 END) as error_sessions,
ROUND(100.0 * COUNT(CASE WHEN event = 'checkout_error' THEN 1 END) / COUNT(*), 2) as error_rate,
SUM(CASE WHEN event = 'checkout_error' THEN (properties->>'cart_value')::numeric END) as revenue_at_risk
FROM events
WHERE event IN ('funnel_step', 'checkout_error')
AND timestamp > NOW() - INTERVAL '24 hours'
GROUP BY checkout_step
ORDER BY error_rate DESC
When you run this and payment_form shows a 12% error rate while every other step is under 1%, you've found your problem. Click into those error sessions to see stack traces, browser versions, and — if you've enabled session replay — exactly what the user saw before they bounced.
Expected output (hypothetical):
| Checkout Step | Total Sessions | Error Sessions | Error Rate | Revenue at Risk |
|---|---|---|---|---|
| payment_form | 3,847 | 461 | 11.98% | $127,493 |
| shipping_form | 4,102 | 38 | 0.93% | $10,412 |
| cart_review | 5,891 | 47 | 0.80% | $14,088 |
In a hypothetical scenario like this, those payment_form errors would represent real carts hitting real bugs. Fix the bug, recover the revenue. Simple math. (Rarely is it actually this clean, but a dev can dream.)
Step 4: Configure Real-Time Alerts
Error tracking is useless if nobody sees the errors until Monday. Set up alerts that fire when checkout health degrades.
In JustAnalytics, go to Alerts → Create Alert:
- Condition:
event = 'checkout_error' AND properties->>'step' = 'payment_form' - Threshold: More than 50 events in 15 minutes
- Channel: Slack, PagerDuty, email — whatever your on-call uses
I'd also add a second alert for error rate spikes:
- Condition: Error rate on payment_form exceeds 5%
- Window: Rolling 1 hour
- Comparison: 2x baseline from previous 7-day average
The second alert catches gradual degradation that doesn't hit the absolute threshold. A payment provider having a partial outage might only bump your error rate from 1% to 4% — not enough for the count-based alert, but absolutely worth investigating at 3am before it becomes a customer service nightmare. I am not a morning person. I really don't want to find out about these things at 9am when my inbox is already on fire.
Ask me how I know this.
Common Errors and How to Fix Them
These are the checkout errors I see over and over. If you're debugging a conversion drop, start here.
TypeError: Cannot read property 'clientSecret' of undefined
Stripe's PaymentIntent isn't ready when your component renders. The clientSecret comes from your server, and if the API call is slow or your component mounts too early, you get this. Fix: Use a loading state that blocks the payment form until clientSecret is populated. Don't just hide the button — prevent the entire Stripe element from rendering.
NetworkError: Failed to fetch on payment submission
User's connection dropped mid-checkout, or their corporate firewall blocked the payment provider's domain. You can't fix their network, but you can detect this and show a retry prompt instead of a generic error. Log it as checkout_error with error_type: 'network' so you can track frequency.
Error: Card declined appearing at unusually high rates
If decline rates suddenly spike, it's probably not your users' cards. More likely: your Stripe secret key expired, your PayPal business account has a hold, or your payment provider is having an incident. Cross-check with Stripe Dashboard status and their status page. We've written about correlating errors with funnel drop-off if you want a deeper dive on this pattern.
Form validation errors that never surface to users
A validation library throwing on blur before the user finishes typing. They see nothing wrong, can't submit, and leave. Session replay catches these instantly — look for rage clicks on the submit button with no visible error message. The fix is usually debouncing validation or only showing errors on submit.
(This one is particularly frustrating because it feels like a feature working correctly — the validation IS catching invalid input. It's just catching it at the wrong moment.)
What This Won't Fix
Being honest here: checkout error tracking doesn't solve everything.
If your abandonment is caused by high shipping costs, surprise fees at checkout, or requiring account creation, no amount of error tracking helps. Those are UX problems, not bugs. You'll see high drop-off rates with zero errors, which is its own useful signal — means the tech is working fine and the problem is elsewhere.
Similarly, if you're running on Shopify's hosted checkout, you don't control the JavaScript. Shopify has their own error monitoring. You can still track funnel steps (Shopify fires dataLayer events), but you can't instrument the payment form itself. Consider this a trade-off of hosted platforms. (For teams running multi-account e-commerce operations, JustBrowser handles the browser fingerprinting side of scaling across storefronts.)
And if your errors are happening server-side — payment webhook failures, inventory mismatches, fraud rule rejections — you need APM, not just client-side error tracking. JustAnalytics includes distributed tracing for this, but it's a different integration than what we've covered here.
I know, I know — "you need a different tool" isn't the answer you wanted. But I'd rather be honest about scope than promise something that won't actually help.
Next Steps
You've got checkout-specific error tracking running. Here's where to go from here:
Add session replay to payment pages. When someone hits an error, you want to see exactly what they saw. Was the button grayed out? Did the spinner hang forever? Was there a validation message they missed? Session replay answers these questions faster than guessing. JustAnalytics includes it — enable it in your init config with sessionReplay: true. Fair warning though: the first time you watch a real user struggle with your checkout, it's painful. You'll see every hesitation, every wrong click. Humbling stuff.
Correlate with ad spend. If you're running paid acquisition through ClickzProtect, your checkout errors might be concentrated in traffic from specific campaigns or publishers. A high error rate in organic traffic is annoying. That same error rate in your paid traffic is an emergency. Cross-referencing error sessions with UTM parameters shows you where the problem hurts most.
Tag errors by payment method. If Apple Pay has a 0.3% error rate and PayPal has a 7% error rate, you want to know that before your support team does. Add payment_method to your checkout_error properties. (For crypto-funded ad spend that doesn't hit checkout flows at all, VeloCards handles virtual card provisioning.)
Set up deploy correlation. When errors spike, the first question is always "did we deploy?" Tagging events with your git SHA or release version makes this a one-click filter instead of a guessing game. Our Next.js 15 tutorial covers this pattern.
For teams managing the broader observability stack — not just checkout, but the whole app — we've covered replacing GA4 + Sentry + Pingdom + LogRocket with a single integration. And if you're running Django on the backend, the middleware tutorial shows server-side instrumentation that pairs with this client-side checkout tracking. Look, I know "unified observability" sounds like marketing fluff. I used to think so too. Then I spent three hours matching timestamps across four dashboards.
The bug in my intro story? It took three hours to find with separate tools. With unified tracking, it would've taken five minutes. That's the real ROI here — not just catching bugs, but catching them before they cost you a weekend. (And if you're running AI agents in your CI/CD pipeline, DevOS can tie deploy events directly to error spikes.)
Frequently Asked Questions
How do I track JavaScript errors specifically in my checkout flow?
Instrument each checkout step with a funnel_step event that includes step name, cart value, and timestamp. When an error fires on that page, the shared session ID links it to that funnel step automatically. Filter your error dashboard by funnel_step equals payment_form or cart_review to see only checkout-specific exceptions — no more digging through site-wide error noise.
What checkout errors cause the most cart abandonment?
Payment SDK initialization failures (Stripe, PayPal, Braintree) top the list — they often fail silently without throwing a visible error to the user. Form validation race conditions rank second, especially on mobile where keyboard dismissal triggers blur events before the user finishes typing. Third is network timeouts on address verification APIs, which can hang the UI for 8+ seconds without feedback.
Can I correlate checkout errors with actual revenue loss?
Yes, if you're passing cart value in your funnel_step events. Query sessions where funnel_step equals payment_form AND has exception equals true, then sum the cart_value property. That gives you the dollar amount sitting in carts when users hit errors. Compare against completed purchases to calculate your error-attributed revenue loss.
How is this different from just using Sentry on my checkout pages?
Sentry shows you errors happened. It does not show you which checkout step had the error, what the cart value was, or whether the user completed the purchase anyway. With unified error tracking and analytics, you see the full session — user hit cart_review, triggered a TypeError on payment_form with a $247 cart, and dropped off. Sentry alone gives you the TypeError. The rest requires manual correlation with GA4 exports.
Try JustAnalytics
All-in-one observability in one under-5KB script: cookieless analytics + error tracking + APM + session replay + uptime + structured logs. Replaces GA4 + Sentry + Datadog + Pingdom + LogRocket. Free tier (100K events/mo), Pro $49/month ($39 annual).
Author at JustAnalytics.