5 min read

How to Set Up Alerts for Refund Rate in Stripe

A spike in refunds is often your first signal that something's broken—product quality, checkout friction, or customer expectation mismatch. Stripe doesn't have built-in refund rate alerts, but you can build them in minutes using the Events API and webhooks to catch spikes before they become a problem.

Capture refund events with webhooks

Start by setting up a webhook endpoint to receive real-time notifications whenever a refund occurs. This is where you'll log every refund event for later analysis.

Create a webhook endpoint

Build an endpoint that Stripe will POST to whenever a charge.refunded event happens. This endpoint is your connection point—it captures the refund data in real time so you can process it.

javascript
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'charge.refunded') {
    const charge = event.data.object;
    console.log(`Refund: ${charge.id}, amount: $${charge.refunded / 100}`);
    // Log to database or cache for alert calculation
  }

  res.json({ received: true });
});
Express webhook handler that validates and processes Stripe refund events

Register the webhook in Stripe Dashboard

Go to Developers > Webhooks in your Stripe Dashboard. Click Add endpoint, paste your webhook URL, and select charge.refunded as the event to listen for. Stripe will generate a signing secret—save it as STRIPE_WEBHOOK_SECRET in your environment.

Watch out: Stripe retries failed webhooks. Handle refund events idempotently by storing refund IDs in your database—if you see the same refund ID twice, skip processing it.

Calculate refund rate from Stripe charges

With webhooks running, fetch historical charges and refunds to calculate your current refund rate. This becomes your baseline for comparison.

Pull charges and refunds over a time window

Query all charges from the past 24 hours using the Stripe API. Calculate the total refunded amount and divide by total charged amount to get your refund rate percentage.

javascript
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

async function calculateRefundRate(hoursBack = 24) {
  const startTime = Math.floor((Date.now() - hoursBack * 3600000) / 1000);

  const charges = await stripe.charges.list({
    created: { gte: startTime },
    limit: 100
  });

  let totalCharged = 0;
  let totalRefunded = 0;

  for (const charge of charges.data) {
    totalCharged += charge.amount;
    if (charge.refunded) {
      totalRefunded += charge.refunded;
    }
  }

  const refundRatePercent = totalCharged > 0 ? (totalRefunded / totalCharged) * 100 : 0;
  return { refundRatePercent, totalCharged, totalRefunded };
}

const { refundRatePercent } = await calculateRefundRate(24);
console.log(`Refund rate (24h): ${refundRatePercent.toFixed(2)}%`);
Calculate refund percentage from charges in the past 24 hours
Tip: Stripe returns max 100 items per request. If you have high volume, loop through results using the starting_after parameter to fetch all charges.

Trigger alerts when refunds exceed threshold

Define your acceptable refund rate, then automatically notify your team when it's exceeded. This is where you move from monitoring to action.

Check refund rate against your threshold

Set a threshold that makes sense for your business (2% for SaaS, 5% for e-commerce). Compare the calculated rate to this threshold and flag it if exceeded.

javascript
async function checkRefundRateAlert(thresholdPercent = 3) {
  const { refundRatePercent } = await calculateRefundRate(24);

  if (refundRatePercent > thresholdPercent) {
    console.warn(
      `ALERT: Refund rate ${refundRatePercent.toFixed(2)}% exceeds threshold ${thresholdPercent}%`
    );
    return { alerted: true, refundRatePercent };
  }

  return { alerted: false, refundRatePercent };
}

await checkRefundRateAlert(3); // Alert if refund rate > 3%

Send notifications to your team

When an alert fires, send a message to Slack, email, or PagerDuty with the current rate and a link to investigate in Stripe. Include the threshold so your team knows why the alert triggered.

javascript
const axios = require('axios');

async function alertTeam(refundRate, threshold) {
  const message = {
    text: `Refund rate alert`,
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `⚠️ *Refund rate: ${refundRate.toFixed(2)}%* (threshold: ${threshold}%)\nInvestigate: https://dashboard.stripe.com/transactions`
        }
      }
    ]
  };

  await axios.post(process.env.SLACK_WEBHOOK_URL, message);
  console.log('Alert sent to Slack');
}

const { alerted, refundRatePercent } = await checkRefundRateAlert(3);
if (alerted) {
  await alertTeam(refundRatePercent, 3);
}
Send a Slack message when refund rate exceeds the threshold
Tip: Run this check on a schedule (every 4 hours, daily) using a cron job or Lambda. Don't check every minute—you'll spam your team with noisy alerts. Add a cooldown so alerts only fire once per incident.

Common Pitfalls

  • Skipping webhook signature verification. Always validate with stripe.webhooks.constructEvent()—otherwise malicious actors can send fake refund events to your endpoint.
  • Forgetting pagination. Stripe returns max 100 items per API call. If you have thousands of charges, you'll only see the first 100. Use the has_more flag to loop through all results.
  • Comparing absolute refund amounts instead of rates. A $50 refund on $500 in sales (10%) is worse than $50 on $5000 (1%). Always calculate percentage.
  • Alerting too often. Checking every minute and notifying on every spike creates alert fatigue. Check hourly or daily and set a window—e.g., only alert if rate is elevated for 2 consecutive checks.

Wrapping Up

You now have real-time visibility into your Stripe refund rate and automatic notifications when it spikes. The moment an alert fires, your team can jump into the Dashboard to investigate the root cause—whether it's a specific product, cohort, or payment method. If you want to track refund rate automatically alongside other metrics like churn, conversion, and revenue across all your tools, Product Analyst can help.

Track these metrics automatically

Product Analyst connects to your stack and surfaces the insights that matter.

Try Product Analyst — Free