> ## Documentation Index
> Fetch the complete documentation index at: https://docs.datalyr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Revenue sources

> Pick the one platform that owns each transaction, connect it, and join its revenue to the visit that caused it.

We read revenue from the platform that creates the charge. Exactly one platform owns each transaction, so pick the owner first.

## Pick the owner

| Business | Source | Guide |
| - | - | - |
| Shopify store | Shopify | [Shopify](/revenue/shopify) |
| SaaS or web subscription | Stripe | [Stripe](/revenue/stripe) |
| Mobile subscription | RevenueCat **or** Superwall | [RevenueCat](/revenue/revenuecat), [Superwall](/revenue/superwall) |
| Whop storefront | Whop | [Whop](/revenue/whop) |
| Checkout Champ funnel | Checkout Champ | [Checkout Champ](/revenue/checkoutchamp) |

## SDK values

Revenue comes from payment sources and your server, not from the browser or app SDK.

| What sent the event | Counts as revenue |
| - | - |
| A payment source (Shopify, Stripe, RevenueCat, Superwall, Whop, Checkout Champ, ClickFunnels, Commas) | Always |
| Your server, through the [Node SDK](/sdk-reference/node) | Always |
| The [Web SDK](/sdk-reference/web), [iOS SDK](/sdk-reference/ios) or [React Native SDK](/sdk-reference/react-native) | Only `purchase` and `subscribe` events, and only until the workspace has recorded revenue from a payment source or your server |

A value on any other SDK event, such as `add_to_cart` or a custom event, is never revenue.

The switch happens when the first event with a value arrives from a payment source or your
server, at any time in the workspace's history:

* Connecting a payment source alone does not switch it. The first valued event does.
* After the switch, no SDK `purchase` or `subscribe` event is revenue, including the ones
  sent before it.
* Disconnecting the source later does not switch it back.

SDK purchase events still attribute revenue to the visit, count as events, and fire your
conversion rules and ad-platform postbacks.

<Warning>
  A payment source that stops sending shows as a revenue gap, not as SDK revenue. Reconnect
  it. Once a source has at least 7 days of revenue history and has been silent for at least
  72 hours and well past its usual gap, **Health** marks it **No revenue**.
</Warning>

## Endpoint and auth

`WORKSPACE_PUBLIC_ID` is the workspace public ID that the connection dialog fills into the URL.

| Source | Method and URL | Auth |
| - | - | - |
| Shopify | `POST https://webhooks.datalyr.com/shopify` | `X-Shopify-Hmac-Sha256`, base64 HMAC-SHA256 |
| Stripe | `POST https://webhooks.datalyr.com/stripe-connect` | `Stripe-Signature`, Datalyr-held Connect secret |
| RevenueCat | `POST https://webhooks.datalyr.com/revenuecat/WORKSPACE_PUBLIC_ID` | `Authorization` header |
| Superwall | `POST https://webhooks.datalyr.com/superwall/WORKSPACE_PUBLIC_ID` | `svix-id`, `svix-timestamp`, `svix-signature` |
| Whop | Datalyr creates the workspace webhook | Datalyr stores the Whop signing secret |
| Checkout Champ | `GET https://webhooks.datalyr.com/checkoutchamp/WORKSPACE_PUBLIC_ID` | `secret` query parameter |

The retired route `https://webhooks.datalyr.com/stripe/WORKSPACE_PUBLIC_ID` returns HTTP 410 and records nothing.

## The identity field

Skip this field and you'll still get the revenue, but nothing to credit it to.

| Source | Field you must set | Where the value comes from |
| - | - | - |
| Shopify | `_datalyr_visitor_id` cart attribute | The App Embed writes it |
| Stripe Checkout Session | `client_reference_id` | `datalyr.getStripeMetadata()` |
| Every other Stripe object | `metadata.visitor_id` | `datalyr.getVisitorId()` |
| RevenueCat | `datalyr_id` subscriber attribute | `getRevenueCatAttributes()` |
| Superwall | `datalyr_id` user attribute | `getSuperwallAttributes()` |
| Whop | Email or Whop Events context | Auto identity; checkout metadata is optional |
| Checkout Champ | `visitor_id` query parameter | You append it to the Export Profile URL |

Each source falls back to the customer email, then to a synthetic ID that matches no campaign.

## Gross and net revenue

Every source writes the event `value` as **gross**, before tax, store commission, and processor fees.

| Source | Net revenue metric |
| - | - |
| Shopify | Signed transaction sum: sales and captures minus refunds |
| Stripe | Cash, minus refunds, minus dispute withdrawals, plus reinstatements |
| RevenueCat | Sum of `event_data.net_revenue`, computed per event by the webhook |
| Superwall | Sum of `event_data.net_revenue`, computed per event by the webhook |
| Whop | Gross payments minus refunds. Fees, taxes, and disputes are excluded |
| Checkout Champ | None |

<Warning>
  Connect RevenueCat or Superwall, never both. Each writes the same fourteen event
  names for one store transaction, and we deduplicate inside a source rather than
  across them, so your mobile revenue doubles.
</Warning>

<Note>
  Shopify and Checkout Champ need no such care. We classify every Shopify order and drop
  the Shopify copy of an order that Checkout Champ created.
</Note>

## Verify any source

1. Open your site in a private window with a `?utm_source=doctest` parameter.
2. Open **Events**. Confirm a `pageview` row exists.
3. Create one test transaction in the source platform.
4. Open **Events**. Confirm the transaction row carries the right `value` and `currency`.
5. Confirm `visitor_id` does not start with the source name.

A `visitor_id` such as `stripe_cus_123` or `shopify_456` means the identity field never arrived.

## Next

<CardGroup cols={2}>
  <Card title="Ecommerce revenue" icon="cart-shopping" href="/revenue/ecommerce">Which Shopify metric answers which question.</Card>
  <Card title="Subscription revenue" icon="rotate" href="/revenue/subscriptions">Trials, renewals, and the metrics that exist today.</Card>
  <Card title="Refunds" icon="arrow-rotate-left" href="/revenue/refunds">Sign, reporting period, and per-source coverage.</Card>
  <Card title="Profit" icon="chart-line" href="/revenue/profit">Shopify gross profit, POAS, and their limits.</Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.