Growth Strategy
D2C Repeat-Purchase Economics: Does the Second Order Actually Repay CAC?
Build a fixed-horizon customer contribution bridge after refunds, fulfilment and acquisition cost. Reproduce a synthetic cohort, test the arithmetic and distinguish realised payback from optimistic lifetime value.
A brand can improve its repeat-purchase rate and still lose more money on each acquired cohort. Discounting may bring the next order forward while reducing its contribution. Larger acquisition campaigns may bring customers who repeat less often. A revenue dashboard can make both changes look encouraging. The useful question is narrower: has a defined group of new customers generated enough realised contribution to cover the cost of acquiring that same group within a stated horizon?
Editorial disclosure: Original, AI-assisted Growthcraft Editorial analysis. All numerical examples are synthetic, not Akshay's client results. This is an operational measurement convention, not an accounting standard, investment recommendation or claim to reproduce a platform dashboard. The JavaScript example is verified with Node.js 24; no customer data, API key or paid service is needed.
Four decisions this guide helps you make
- Decide whether an acquisition cohort has repaid its scoped cost using observed contribution rather than predicted revenue.
- Separate more repeat buyers from more orders per repeat buyer; they imply different lifecycle questions.
- Identify the missing evidence before increasing discounts, acquisition spend or retention activity.
- Build a reproducible comparison without selecting only customers who eventually returned.
Why a repeat customer report is not a payback report
A returning-customer count answers a behaviour question. Payback also needs product economics, operating costs, a cohort boundary and time. Shopify's customer-report documentation describes first-order cohorts and notes that some customer reports use a customer's entire order history, not only orders inside the selected reporting period. That makes report configuration relevant: a lifetime returning-customer classification is not automatically a 60-day repeat-purchase rate. The documentation was checked on 24 September 2026; its publication date is unspecified.
Use the store and payment records to establish the commercial ledger, then reconcile behavioural analytics against them. Google's GA4 ecommerce documentation describes purchase and refund events with transaction identifiers and item information. Implementing those events is useful for journey analysis, but it does not supply every product, fulfilment or service cost needed here. An analytics implementation and a contribution model solve related but different tasks.
Freeze the cohort and the clock
Choose the first qualifying purchase rule before examining repeat outcomes. Specify whether test orders, cancelled orders, wholesale buyers and fraudulent orders are excluded, and why. Do not later remove disappointed customers simply because they refunded or never returned. Those outcomes may be exactly what the commercial review needs to see. Keep an exclusion ledger with counts and reasons.
Assign one customer key inside approved storage and document identity limitations. Two emails may belong to one person; a household may share an account. The model operates at the defined customer-key grain, not a magically complete identity. Its first-order timestamp must come from sufficient purchase history. If older orders are missing, label the population “first observed purchasers” rather than confidently calling every record a new customer.
Select a horizon H measured from each customer's qualifying first order. For a 60-day report, every included customer must have 60 full elapsed days of observation. Count qualifying repeat orders after the first order and on or before that customer's endpoint. A second transaction at the same timestamp needs an explicit sequence rule; do not let unstable database order decide what counts as a repeat.
Prefer waiting until the entire acquisition cohort matures before claiming cohort payback. If you instead report a mature subset, its acquisition-cost allocation must be independently defensible. Dividing a calendar month's total spend across only its older customers will distort the comparison. Show immature cohorts as pending, not as zero-repeat failures and not as early successes cherry-picked into the numerator.
Write the contribution contract before the formula
Define realised order contribution as net customer revenue less the included variable costs. Net revenue should specify tax, shipping charged to the customer, discounts and refunds. Costs might include net product cost, fulfilment, payment fees, variable service cost and return processing. State the treatment of recovered inventory rather than assuming every returned item is a complete product-cost loss.
Use mutually exclusive fields. If discounts are already reflected in net revenue, do not subtract them again. If lifecycle campaign cost is included in repeat contribution, do not deduct it a second time at cohort level. Keep acquisition spending outside both first-order and repeat-order contribution so the bridge can show it once. Fixed overhead, financing and tax are outside this illustrative operating contribution model; a positive output is not net profit.
There are two clocks to document for refunds: when the original order occurred and when its refund became known. This model freezes information at the customer's horizon endpoint. A refund observed afterwards belongs in a labelled later restatement or a separate return-complete view. Comparing one cohort before its returns mature with another after returns mature can manufacture an improvement. Publish the extraction cutoff and the refund policy beside the result.
A synthetic cohort you can reconcile by hand
Assume 100 first-time customers, all observed for 60 days, and €3,600 in acquisition spending assigned to that complete cohort under the stated convention. Their first orders generated €8,000 net revenue and €5,600 included variable costs. First-order contribution is therefore €2,400, or €24 per acquired customer. Scoped CAC is €36. The first-order gap is €12 per customer.
Twenty-five customers then placed 30 qualifying repeat orders within their windows. Repeat net revenue was €1,800 and included repeat costs were €1,080, producing €720 repeat contribution. The cohort generated €3,120 contribution before acquisition and minus €480 after acquisition. On average that is minus €4.80 per acquired customer, even though 25% became repeat buyers.
| Measure | Calculation | Synthetic result |
|---|---|---|
| Repeat-buyer rate | 25 / 100 | 25% |
| Repeat orders per acquired customer | 30 / 100 | 0.30 |
| Contribution per repeat order | €720 / 30 | €24 |
| Contribution before acquisition | €2,400 + €720 | €3,120 |
| Contribution after acquisition | €3,120 − €3,600 | −€480 |
At an unchanged €24 contribution per additional repeat order, 20 more such orders would cover the remaining €480. This is a conditional scenario, not a prediction or an instruction to email everyone more often. Additional orders may have different margins and require additional incentives or service cost. If the marginal contribution is zero or negative, more of those orders cannot close the gap.
Reproduce the aggregate bridge
The function below accepts validated cohort aggregates in integer minor units, with 100 minor units per currency unit in the example. Keep currencies separate. Negative contribution is allowed because real cohorts can lose money; negative spending or counts are not. The function rejects immature cohorts rather than pretending it can allocate their acquisition cost. It also refuses contradictory repeat counts and unsafe integer totals.
function repeatContribution(x) {
const fields = ['customers', 'repeatBuyers', 'repeatOrders',
'firstContributionMinor', 'repeatContributionMinor', 'acquisitionMinor'];
if (fields.some(key => !Number.isSafeInteger(x[key])))
throw new Error('Expected safe integer counts and minor units');
if (x.fullyObserved !== true) throw new Error('Wait for cohort maturity');
if (x.customers <= 0 || x.repeatBuyers < 0 || x.repeatOrders < 0 ||
x.acquisitionMinor < 0 || x.repeatBuyers > x.customers ||
x.repeatOrders < x.repeatBuyers ||
(x.repeatBuyers === 0 && x.repeatOrders !== 0) ||
(x.repeatOrders === 0 && x.repeatContributionMinor !== 0))
throw new Error('Inconsistent cohort');
const before = x.firstContributionMinor + x.repeatContributionMinor;
const after = before - x.acquisitionMinor;
if (!Number.isSafeInteger(before) || !Number.isSafeInteger(after))
throw new Error('Unsafe aggregate');
return {
repeatBuyerRate: x.repeatBuyers / x.customers,
repeatOrdersPerCustomer: x.repeatOrders / x.customers,
contributionBeforeAcquisitionMinor: before,
contributionAfterAcquisitionMinor: after,
afterPerCustomerMinor: after / x.customers,
costCoverage: x.acquisitionMinor ? before / x.acquisitionMinor : null
};
}
console.log(repeatContribution({
customers: 100, repeatBuyers: 25, repeatOrders: 30,
firstContributionMinor: 240000, repeatContributionMinor: 72000,
acquisitionMinor: 360000, fullyObserved: true
}));
// repeatBuyerRate: 0.25; repeatOrdersPerCustomer: 0.3;
// before: 312000; after: -48000; afterPerCustomerMinor: -480;
// costCoverage: 0.8666666666666667 (a ratio, not a probability)
Zero acquisition cost produces an undefined coverage ratio, not infinite performance. A ratio above one means scoped acquisition cost has been covered by contribution at the endpoint; it does not establish the exact day of payback. A true time-to-payback calculation needs the dated contribution path and a policy for later reversals. For refunds and margins, the snapshot convention remains as important as the arithmetic.
Validate the export, not only the function
At order grain, reconcile unique qualifying order IDs, customer assignment, currency and first-versus-repeat classification. Keep refund allocations tied to orders. Test that an order cannot be counted in both contribution buckets. Verify the same customer population underlies the counts, revenue and costs. Add an explicit missing-cost state; missing fulfilment cost should not become a numeric zero.
Known-answer tests should cover the example, no repeat orders, zero acquisition cost, loss-making repeats, contradictory counts, immature cohorts and integer overflow. An invariant should confirm that contribution before acquisition minus scoped acquisition spending always equals contribution after acquisition. These checks catch implementation mistakes; they cannot prove that the export contains all eligible customers or that a chosen cost allocation is fair.
Turn the bridge into the next intervention
If first-order contribution is weak, inspect offer, discount, product mix and fulfilment before assuming the lifecycle team must rescue the model. If repeat-buyer rate is low, inspect the product's natural repurchase cycle, customer experience and eligibility for a useful next offer. A durable product may not need the same repeat frequency as a replenishment product. If repeat buyers increase but repeat contribution falls, investigate incentives and low-margin order mix.
Keep intervention evaluation separate from the accounting bridge. Customers who receive or open a message differ from those who do not; observed repeat purchasing does not establish message incrementality. A credible holdout or another suitable causal design may be needed to estimate the additional contribution caused by a lifecycle programme. Measure customer complaints, unsubscribes and returns as well as revenue where relevant.
Use the B2C diagnostic guide to identify the first investigation, the existing checkout case to understand conversion evidence and its limits, and the incremental-profit calculator only when you have a separate incremental-revenue assumption. That calculator is not this repeat-cohort model. The goal is a defensible decision on the next customer cohort, not a larger lifetime-value number created by extending the horizon until the report looks profitable.