# How to calculate ROAS per campaign from Stripe payments

> The formula, how a Stripe payment gets its campaign, and what the number leaves out.

Canonical: https://atrby.site/blog/roas-per-campaign-stripe
Last updated: 2026-10-26

## The formula

Return on ad spend for one campaign is the revenue that campaign brought in, divided by what you spent on it, over the same period.

For example, if a campaign brought in $4,000 of revenue and cost $1,000, its ROAS is 4.0. Every $1 spent came back as $4 of revenue. That is the gross version. Most teams want the version net of refunds, and atrby reports that one: refunds come off the revenue before the division.

The hard part is not the division. It is getting the revenue and the spend to agree on which campaign they belong to.

## Why Stripe payments need a campaign

A Stripe payment on its own says nothing about the marketing that led to it. Stripe records the amount, the customer and the product. To tie the payment to a campaign, the campaign has to travel with the checkout.

Stripe allows that with metadata. According to [Stripe's metadata documentation](https://docs.stripe.com/metadata), metadata you set on a Checkout Session is included in the `checkout.session.completed` event your webhook receives. An object can hold 50 key-value pairs, with keys up to 40 characters and values up to 500. Stripe returns metadata only to requests made with a secret key, so it never reaches the browser.

atrby uses that. Its script stores a visit id when the visitor arrives. At checkout, the id goes into the session's metadata. When the webhook arrives, atrby finds the visit, takes the campaign from its `utm_campaign` tag, and stores that campaign on the payment. Refunds and disputes copy the campaign from the original payment, so the numbers stay consistent.

## Step by step

1. **Tag your ads.** Set `utm_campaign` on every link to the value your ad platform calls the campaign. Matching is case-insensitive and ignores spaces at the ends, so `Summer Sale` and `summer sale ` are the same campaign.
2. **Export the spend, one row per campaign per day.** Google's guidance for importing campaign cost data asks for a daily breakdown with an ISO date, `YYYY-MM-DD`, in [its Analytics import guide](https://support.google.com/analytics/answer/16748649). Export the same granularity from your ad platform.
3. **Import the file and read the review.** atrby shows what it read and what it could not read, with a reason for each row. Nothing goes live until you publish.
4. **Open Campaigns on the Revenue screen.** Each row shows spend, revenue, ROAS, profit and the number of payments.

The spend import is covered step by step in [the Google Ads import guide](https://atrby.site/blog/import-google-ads-spend).

## What the number leaves out

- **Tax.** Revenue is shown before tax. Profit also takes off processing fees, and only the fees your processor reports or that you have given a read-only key to read.
- **Payments with no campaign.** A payment whose visit had no `utm_campaign` shows as *(no campaign)*. It still counts in the totals, so the campaign rows and the total revenue reconcile.
- **Which visit gets the credit.** The attribution model you pick for the site decides which visit the payment is credited to. Last touch is the simplest. The campaign in the table follows that choice.
- **Days in UTC.** Spend is assigned to a UTC day. A campaign that runs late in your local evening can move a day.

## The limits of matching by name

Matching is by name, so two campaigns with the same name on different platforms are added together. Use a distinct name per platform, such as `summer-sale-google` and `summer-sale-meta`, if you want them apart.

A ROAS of 0.5 with a campaign still running is not a verdict. It is a number at one point in time, with the attribution model you chose. Compare campaigns over the same window, and look at the payments count before you act on a small sample.

## Check it yourself

Run one campaign with a test tag and a test purchase. Confirm that the purchase appears under that campaign, and that the spend for the same day matches your ad platform's figure. If the revenue lands under *(no campaign)*, the tag is missing or spelled differently from the campaign name.

## Related
- [Revenue attribution without cookies, explained](https://atrby.site/blog/attribution-without-cookies)
- [Import Google Ads spend into your analytics, step by step](https://atrby.site/blog/import-google-ads-spend)
- [Meta Ads in another currency: converting spend for ROAS](https://atrby.site/blog/meta-ads-currency-roas)
