If you're tracking CAC payback—the time it takes a customer to generate enough revenue to cover their acquisition cost—Stripe doesn't calculate this automatically. You need to wire up webhooks and add custom logic to monitor when your CAC payback window is at risk. This guide walks you through setting alerts that fire when payback extends beyond your target.
Set Up a Webhook Listener for Subscription Events
Stripe webhooks give you real-time access to subscription and invoice events. You'll use these to track when customers generate revenue.
Create a webhook endpoint in your backend
Set up an HTTP POST endpoint that Stripe can call whenever subscription or invoice events occur. This is where you'll receive data about customer revenue.
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.post('/webhook', express.raw({type: 'application/json'}), async (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 === 'invoice.paid') {
const invoice = event.data.object;
console.log(`Invoice paid for customer ${invoice.customer}: $${invoice.total}`);
}
res.json({received: true});
});
app.listen(3000, () => console.log('Webhook listening'));
Register the webhook endpoint in your Stripe Dashboard
Go to Developers > Webhooks in your Stripe Dashboard and add your endpoint URL. Select the events you want to listen for: invoice.paid, invoice.payment_failed, and customer.subscription.updated are the key ones for CAC payback tracking.
Calculate CAC Payback Period
When a customer makes a payment, compare their lifetime revenue against their acquisition cost. This tells you how many months until they've paid back what you spent to acquire them.
Query customer revenue from Stripe
Use the Stripe API to fetch all invoices for a customer. Sum up the paid amounts to get their lifetime revenue so far.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
async function getCustomerLTV(customerId) {
const invoices = await stripe.invoices.list({
customer: customerId,
status: 'paid',
limit: 100
});
let totalRevenue = 0;
invoices.data.forEach(invoice => {
totalRevenue += invoice.total;
});
return totalRevenue / 100; // Convert from cents to dollars
}
const ltv = await getCustomerLTV('cus_ABC123');
console.log(`Customer LTV: $${ltv}`);
Extract MRR from the active subscription
Retrieve the customer's subscription and get the monthly recurring revenue from the price object. This tells you how much they're paying each month going forward.
async function getMonthlyMRR(customerId) {
const customer = await stripe.customers.retrieve(customerId, {
expand: ['subscriptions']
});
const subscription = customer.subscriptions.data[0];
if (!subscription) return 0;
const priceItem = subscription.items.data[0];
const monthlyMRR = priceItem.price.unit_amount / 100; // Cents to dollars
return monthlyMRR;
}
const mrr = await getMonthlyMRR('cus_ABC123');
console.log(`Monthly MRR: $${mrr}`);
Calculate estimated payback months
Divide the CAC by monthly MRR to get how many months until payback. Compare this to your target. If it's extending beyond your threshold, the account is at risk.
async function getPaybackMetrics(customerId, cacAmount) {
const mrr = await getMonthlyMRR(customerId);
if (mrr === 0) return null;
const paybackMonths = cacAmount / mrr;
return {
customerId,
mrr,
cac: cacAmount,
paybackMonths: Math.round(paybackMonths * 10) / 10,
isAtRisk: paybackMonths > 6
};
}
const metrics = await getPaybackMetrics('cus_ABC123', 150);
if (metrics.isAtRisk) {
console.log(`⚠️ Payback extended: ${metrics.paybackMonths} months`);
}
Send Alerts When Payback Extends Beyond Target
Store target payback windows for your accounts. When calculated payback exceeds that threshold, notify your team immediately so they can investigate or intervene.
Integrate alert logic into your webhook handler
When an invoice.paid event arrives, check the customer's CAC payback metrics. If they're at risk, send a notification via Slack or email with the key details.
const axios = require('axios');
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
const sig = req.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
if (event.type === 'invoice.paid') {
const invoice = event.data.object;
const cacData = await fetchCACData(invoice.customer);
if (!cacData) return res.json({received: true});
const metrics = await getPaybackMetrics(invoice.customer, cacData.cac);
const targetPayback = cacData.target_payback_months || 6;
if (metrics.paybackMonths > targetPayback) {
await axios.post(process.env.SLACK_WEBHOOK_URL, {
text: '⚠️ CAC Payback Alert',
blocks: [{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Customer:* ${invoice.customer}\n*Payback:* ${metrics.paybackMonths}m (target: ${targetPayback}m)\n*MRR:* $${metrics.mrr}`
}
}]
});
}
}
res.json({received: true});
});
Add a cooldown to avoid alert spam
Track when you last sent an alert for each customer. Only send another alert after a set interval (e.g., 7 days) to prevent notification fatigue while maintaining visibility into at-risk accounts.
const lastAlertCache = {};
async function shouldSendAlert(customerId) {
const lastAlert = lastAlertCache[customerId];
if (!lastAlert) return true;
const daysSinceAlert = (Date.now() - lastAlert) / (1000 * 60 * 60 * 24);
return daysSinceAlert > 7; // Send alert only once per week
}
if (metrics.isAtRisk && (await shouldSendAlert(invoice.customer))) {
// Send alert and cache the timestamp
lastAlertCache[invoice.customer] = Date.now();
// ... send Slack message ...
}
invoice.paid events.Common Pitfalls
- Forgetting that refunds and chargebacks reduce lifetime revenue but CAC stays constant—this makes payback appear to extend suddenly
- Using the price amount directly without checking the billing interval; if a customer pays annually, monthly MRR is annual_amount / 12, not annual_amount
- Not accounting for subscription downgrades or cancellations; payback metrics only apply to active subscriptions with positive MRR
- Querying all invoices with limit: 100 when a customer has been active for years; implement pagination or use Stripe Sigma for high-volume accounts
Wrapping Up
You now have webhooks tracking customer payments and custom logic calculating CAC payback in real time. Your team gets alerts when acquisition spend isn't paying off quickly enough, letting you intervene early with at-risk accounts. If you want to track CAC payback automatically across all your tools and automate these alerts, Product Analyst can help.