6 min read

How to Track CAC Payback in Stripe

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.

javascript
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]
  }
});
Attach acquisition source and cost to every customer at signup

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.

javascript
const customerId = 'cus_xxxxx';

await stripe.customers.update(customerId, {
  metadata: {
    acquisition_source: 'direct_sales',
    acquisition_cost: '2000',
    acquired_by: 'sales_team'
  }
});
Tip: Keep metadata keys consistent across all customers. Use lowercase, underscores, and a fixed set of acquisition sources so you can query and group reliably.

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.

javascript
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.

javascript
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)}`);
Watch out: Don't count refunds twice—Stripe invoices already reflect refunded amounts. Also exclude setup fees or pro-rations if they distort your payback math.

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.

javascript
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.

javascript
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`);
}
Tip: Export this data weekly to a database or sheet so you can track trends. Payback changes as cohorts mature—a 3-month payback from January might become 2 months by April as those customers settle into their usage pattern.

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.

Track these metrics automatically

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

Try Product Analyst — Free