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

# Connect Stripe

> Connect a business's Stripe account to sync customers, products, and orders for revenue reporting.

Stripe is integrated in **two distinct roles**. Most "business settings → Stripe" questions are about **Stripe Connect** (per business). This page documents the Connect flow in detail and gives a short note on Platform Stripe (SaaS billing) at the end.

## Two Stripe roles in the app

| Role                               | What it is                                                                                        | Where it shows up                                               |
| ---------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| **Stripe Connect (per business)**  | The client's own Stripe account, linked via OAuth.                                                | **Settings → Data Sources → Stripe**, Contacts and Sales grids. |
| **Platform Stripe (SaaS billing)** | The Agency Engineer's Stripe account for agency tenants (packages, subscriptions, token top-ups). | SaaS Packages, onboarding checkout, `/api/webhooks/stripe`.     |

Both use the same `STRIPE_SECRET_KEY` but different tables. The rest of this page focuses on **Connect**.

## Stripe panel

Open it from a business's **Settings → Data Sources → Stripe**.

The panel shows:

* **Status badge** — **Not connected** until OAuth completes; **Connected** with the account display name afterwards.
* **Description** — *Connect this business's Stripe account to sync customers, products and orders.*
* **Status row** — Repeats the current state.
* **Connect** button — Starts the Stripe Connect OAuth flow.

Once connected, you'll see the linked account name and a **Manual sync** button that triggers the same fetch path as the daily scheduler.

## What gets stored

Each business has at most **one** row in the `stripe_access` table:

| Field               | Description                                                                   |
| ------------------- | ----------------------------------------------------------------------------- |
| `stripe_account_id` | Connect account ID (`acct_…`) returned by OAuth.                              |
| `account_name`      | Display name from Stripe (business name, company, individual name, or email). |
| `date_created`      | Timestamp the link was saved.                                                 |

<Note>
  No refresh token is stored. Access goes through the platform's Stripe Connect app (`STRIPE_CONNECT_CLIENT_ID` + `STRIPE_SECRET_KEY`) acting on behalf of `stripe_account_id`.
</Note>

## How a business connects

```mermaid theme={null}
sequenceDiagram
  participant UI as Settings UI
  participant API as Backend /api/stripe
  participant Stripe as connect.stripe.com
  UI->>API: GET connect-url?business_id=
  API-->>UI: OAuth authorize URL (state=agency:business_id)
  UI->>Stripe: User approves Connect
  Stripe->>API: GET /api/stripe/oauth/callback?code&state
  API->>API: oauth.token → stripe_user_id
  API->>API: save stripe_access
  API-->>UI: Redirect ?stripe=connected
```

1. **Agency app** — `stripeSetting.js` calls `GET /api/stripe/connect-url` (requires business access).
2. The user is sent to Stripe Connect with scope **`read_write`**.
3. The callback **`GET /api/stripe/oauth/callback`** exchanges the code, saves or updates `stripe_access`, then redirects back to settings with `?stripe=connected` or `?stripe=error`.
4. **Client portal** uses the same pattern with `state=forclient:…` and client routes (`/stripe/connect-url`, `/stripe/status`, `/stripe/disconnect`).

### Environment variables

* `STRIPE_SECRET_KEY`
* `STRIPE_CONNECT_CLIENT_ID`
* `STRIPE_OAUTH_REDIRECT_URI`
* Plus frontend redirect URLs for agency vs. forclient flows.

<Tip>
  Connect the client's Stripe account, not your agency's. Each business should point to its own Stripe account so customer and order data stays isolated per client.
</Tip>

## What data is pulled

A scheduled job (`stripeScheduler`, `npm run job:stripe`) and the **Manual sync** button both call `fetchStripeData`, which uses the platform secret with **`stripeAccount: stripe_account_id`** (Connect context).

Data is synced incrementally by `last_successful_date` when set:

| Stripe API                       | Local storage                                            | Main fields                               |
| -------------------------------- | -------------------------------------------------------- | ----------------------------------------- |
| Products                         | `stripe_products`                                        | name, description, active, metadata       |
| Prices                           | `stripe_prices`                                          | amount, currency, recurring, product link |
| Customers                        | `stripe_customers`                                       | email, name, metadata                     |
| Payment intents                  | `stripe_orders` (`stripe_type: payment_intent`)          | amount, status, customer                  |
| Invoices (+ line items)          | `stripe_invoices`, `stripe_invoice_line_items`           | amounts, status, PDF URLs, subscription   |
| Checkout sessions (+ line items) | `stripe_checkout_sessions`, `stripe_checkout_line_items` | totals, payment status, customer email    |

The account display name is refreshed from `accounts.retrieve` on each sync.

### Disconnect and clear

`DELETE /api/stripe/:businessId` removes `stripe_access` plus the business's products, orders, customers, and sync jobs. It does **not** clear every Stripe-related table — invoices and checkout sessions may remain unless cleared elsewhere.

### Revoked access

The Connect webhook `account.application.deauthorized` clears `stripe_access` and marks the job failed, so the settings UI surfaces a **reconnect** prompt.

## How the data is used

### Contacts and Sales UI

| Endpoint / Component                      | Purpose                                                                                                                                                                     |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET contacts/stripe-customers`           | List customers with **total spent**, **order count**, last purchase, currency; optional match to GHL contact by email (powers the **Stripe customers** tab in `SalesGrid`). |
| `POST contacts/stripe-summary-by-emails`  | Batch lookup used by Leads, Calls, and Sales grids.                                                                                                                         |
| `StripeColumns.js`                        | Adds **Stripe Spent** and **Stripe Orders** columns to the grids when Stripe is connected and there is matching email data.                                                 |
| `stripeByEmailModel.getStripeInfoByEmail` | Powers per-email Stripe detail in contact history (customers, payments, invoices, checkout).                                                                                |

Spend is **deduplicated** across invoices, checkout sessions, and standalone payment intents so the same payment never double-counts.

### Settings

* Connection status and account name shown at the top of the Stripe panel.
* **Manual sync** (`ManualSyncBlock type="stripe"`) triggers the same fetch path as the scheduler — useful after a fresh connection or after disconnect/reconnect.

### Not used (today)

* Stripe data does **not** feed the Facebook/ad rules engine (unlike Clarity).
* ROAS and ad reporting in the [Business dashboard](/product/dashboard) still come primarily from GHL and other sources. Wire Stripe in separately if you need it as a revenue source.

## Platform Stripe (SaaS billing)

Separate from the per-business Connect account, the platform's own Stripe account handles:

* **Tenant subscriptions**, **invoices**, **package** `stripe_price_id`, and onboarding checkout.
* Webhooks: `checkout.session.completed`, `customer.subscription.*`, `invoice.*`, credit grants, seat limits, and more.

This bills **agencies on The Agency Engineer SaaS**, not the client's ad-business Stripe catalog. Configuration lives under SaaS Packages and the platform webhook handler at `/api/webhooks/stripe`.

## Troubleshooting

| Symptom                                    | Fix                                                                                                                                                   |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| Status stays **Not connected** after OAuth | Pop-up blocker may have closed the Stripe window. Retry **Connect** with pop-ups allowed, or check `?stripe=error` in the redirect URL for the cause. |
| Settings shows a **reconnect** prompt      | Connect access was revoked (in Stripe or via the `account.application.deauthorized` webhook). Run **Connect** again to re-link.                       |
| Stripe columns missing in Sales grid       | Either Stripe isn't connected for the business, or none of the synced Stripe customers match GHL contact emails.                                      |
| Stale data after a manual fix in Stripe    | Use **Manual sync** in the Stripe panel — incremental fetches use `last_successful_date`.                                                             |
| Customer spend looks doubled               | Spend should be deduped across invoices, checkout, and payment intents. If it isn't, capture the customer ID and contact support.                     |

## Summary

* **Stripe Connect** — one linked `acct_…` per business via OAuth. Daily and manual sync of customers, products, prices, payments, invoices, and checkout into local tables. Used to enrich **Contacts and Sales** grids with spend and order counts by email, plus a dedicated **Stripe customers** view.
* **Platform Stripe** — billing your tenants on The Agency Engineer itself.

## Next steps

<CardGroup cols={2}>
  <Card title="Connect GoHighLevel" icon="plug" href="/getting-started/integrations/connect-crm">
    Pair Stripe with GHL so customer spend ties back to leads and pipelines.
  </Card>

  <Card title="Business dashboard" icon="chart-line" href="/product/dashboard">
    See where business KPIs come from today (and where Stripe is not yet wired).
  </Card>
</CardGroup>
