You're spending money on ads, content, and sales. The real question is: how long before a customer pays back what you spent to acquire them? Stripe has the revenue side of that equation—subscriptions, charges, invoices—but you need to systematically connect it to your acquisition costs to track CAC payback.
Tag Customers With Acquisition Metadata
Stripe doesn't automatically know how a customer came to you. You need to attach acquisition source to each customer record so you can slice revenue by cohort later.
Store acquisition source when creating a customer
When a customer signs up, create them in Stripe with metadata fields for acquisition source, campaign, and acquisition cost. Use the Stripe JavaScript SDK or API to attach this data at signup time.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const customer = await stripe.customers.create({
email: '[email protected]',
name: 'Jane Smith',
metadata: {
acquisition_source: 'google_ads',
utm_campaign: 'Q1_growth',
utm_medium: 'cpc',
acquisition_cost: '45',
signup_date: new Date().toISOString().split('T')[0]
}
});Backfill acquisition data for existing customers
If you have legacy customers without this metadata, import acquisition data from your CRM or ad platform and update customers in bulk using the update endpoint.
const customerId = 'cus_xxxxx';
await stripe.customers.update(customerId, {
metadata: {
acquisition_source: 'direct_sales',
acquisition_cost: '2000',
acquired_by: 'sales_team'
}
});Calculate Total Revenue Per Customer
Once you know acquisition cost, you need revenue per customer. Pull invoices and subscriptions from Stripe to build a revenue view.
Query customer revenue using the Invoices API
Stripe's Invoices API is the cleanest way to get customer revenue. Filter by customer and status, then sum the amounts paid. This gives you total historical revenue.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const customerId = 'cus_xxxxx';
const invoices = await stripe.invoices.list({
customer: customerId,
status: 'paid',
limit: 100
});
const totalRevenue = invoices.data.reduce((sum, invoice) => {
return sum + invoice.amount_paid;
}, 0);
console.log(`Total revenue: $${(totalRevenue / 100).toFixed(2)}`);Calculate monthly recurring revenue (MRR) per customer
For payback calculation, use active subscription revenue, not historical totals. Query the subscriptions endpoint for active plans and extract the monthly amount from each item.
const subscriptions = await stripe.subscriptions.list({
customer: customerId,
status: 'active'
});
let monthlyRevenue = 0;
subscriptions.data.forEach(sub => {
sub.items.data.forEach(item => {
if (item.price.recurring?.interval === 'month') {
monthlyRevenue += (item.price.unit_amount * item.quantity) / 100;
}
});
});
console.log(`MRR: $${monthlyRevenue.toFixed(2)}`);Calculate and Track CAC Payback
Combine acquisition cost and customer revenue. CAC Payback is the number of months for a customer's revenue to equal acquisition cost.
Calculate payback months for a single customer
Divide acquisition cost (from metadata) by monthly recurring revenue. If acquisition cost was $100 and MRR is $50, payback is 2 months. This tells you how long until the customer turns cash-flow positive.
const customer = await stripe.customers.retrieve(customerId);
const acquisitionCost = parseFloat(customer.metadata.acquisition_cost);
const cacPaybackMonths = monthlyRevenue > 0
? acquisitionCost / monthlyRevenue
: null;
console.log(`CAC: $${acquisitionCost}`);
console.log(`MRR: $${monthlyRevenue}`);
console.log(`CAC Payback: ${cacPaybackMonths?.toFixed(1)} months`);Aggregate payback by acquisition source
Group customers by acquisition_source metadata and calculate median payback per channel. Run this as a weekly job to see which sources have the best unit economics.
const sourcePayback = {};
let hasMore = true;
let startingAfter;
while (hasMore) {
const customers = await stripe.customers.list({
limit: 100,
starting_after: startingAfter
});
for (const cust of customers.data) {
const source = cust.metadata?.acquisition_source || 'unknown';
const acqCost = parseFloat(cust.metadata?.acquisition_cost || 0);
const subs = await stripe.subscriptions.list({
customer: cust.id,
status: 'active'
});
let mrr = 0;
subs.data.forEach(sub => {
sub.items.data.forEach(item => {
if (item.price.recurring?.interval === 'month') {
mrr += (item.price.unit_amount * item.quantity) / 100;
}
});
});
const payback = mrr > 0 ? acqCost / mrr : null;
if (!sourcePayback[source]) sourcePayback[source] = [];
sourcePayback[source].push(payback);
}
hasMore = customers.has_more;
startingAfter = customers.data[customers.data.length - 1]?.id;
}
for (const [source, paybacks] of Object.entries(sourcePayback)) {
const sorted = paybacks.filter(p => p !== null).sort((a, b) => a - b);
const median = sorted[Math.floor(sorted.length / 2)];
console.log(`${source}: ${median?.toFixed(1)} months`);
}Common Pitfalls
- Forgetting that Stripe only knows revenue generated through Stripe. If you have consulting, support contracts, or non-Stripe upsells, your payback number will be artificially high.
- Using total historical revenue instead of MRR. A customer with $500 in total revenue over 12 months looks different from $500 in the first 2 months. Always use active subscription revenue for forward-looking payback.
- Not adjusting for churn. A customer acquired for $100 paying $50/month looks great at 2 months payback—until they churn in month 3. Adjust expectations based on cohort retention, not just breakeven math.
- Mixing acquisition costs across channels without bucketing. A $10 organic signup has different economics than a $100 paid-ad signup. Always calculate payback per acquisition source to see which channels work.
Wrapping Up
You now have acquisition metadata on every customer, you can calculate revenue per customer from Stripe's API, and you can aggregate payback by channel. Track median payback per acquisition source weekly—it's a leading indicator of whether your growth strategy is sustainable. If you want to track this automatically across tools, Product Analyst can help.