# QuickBooks Online integration — setup

One-time platform setup for the QBO integration. Once done, every
merchant gets a working **Connect to QuickBooks** button in their
Settings → Integrations page.

## Architecture (Phase 1 — what shipped)

```
merchant clicks Connect           ┌─────────────────────────┐
       │                          │ public.settings         │
       ▼                          │   intuit_client_id      │
quickbooks-oauth-start ─reads ────│   intuit_client_secret  │
       │                          │   intuit_redirect_uri   │
       ▼                          │   intuit_environment    │
returns authorize_url             └─────────────────────────┘
       │
       ▼
browser → appcenter.intuit.com/connect/oauth2
       │      user authorises
       ▼
Intuit redirects → quickbooks-oauth-callback
       │           with ?code=…&state=…&realmId=…
       │
       ├── verifies state token (CSRF + replay)
       ├── exchanges code for tokens
       ├── fetches Company name from QBO API
       └── upserts into merchant_quickbooks
       │
       ▼
HTML auto-redirect → dashboard.html#quickbooks=ok:CompanyName
```

Tokens live in `public.merchant_quickbooks` keyed by `user_id`:
- `access_token` — short-lived (~1h). Refresh before each sync.
- `refresh_token` — long-lived (~100 days). Rotates on each refresh.
- `realm_id` — Intuit's "Company ID". Identifies which QBO company.
- `environment` — `sandbox` or `production`.

## Step 1 — Create the Intuit Developer app

1. Go to <https://developer.intuit.com/app/developer/myapps> and
   sign in. (Free account; no card required.)
2. Click **Create an app** → **QuickBooks Online and Payments**.
3. App name: **FireBuddy** (or your white-label equivalent).
4. Select scopes: **com.intuit.quickbooks.accounting** only. We
   don't ask for payment processing.
5. Click **Create app**.

## Step 2 — Configure OAuth redirect URI

In your new app, go to **Keys & OAuth** in the left sidebar. You'll
see two environments (Sandbox + Production) with separate keys for
each.

For **Sandbox** first (always test here):

1. Under **Redirect URIs**, click **Add URI**.
2. Paste exactly:

   ```
   https://srjrxyobhfegimgqfpor.supabase.co/functions/v1/quickbooks-oauth-callback
   ```

3. Save.
4. Copy the **Client ID** and **Client Secret** displayed above.

For **Production**, repeat the same — Intuit requires app review
for production access (takes ~1-3 business days). Sandbox works
immediately.

## Step 3 — Paste credentials into FireBuddy admin

1. Sign in to <https://firebuddy.info/admin.html> as foundye.
2. Settings → **Intuit / QuickBooks** tab.
3. Fill in:
   - **Environment**: Sandbox to start
   - **Client ID**: from Step 2
   - **Client Secret**: from Step 2
   - **Redirect URI**: the same URL you registered in Step 2
4. **Save Intuit settings**. (Secret is stored server-side and
   never echoed back to the browser.)

## Step 4 — Deploy the edge functions

From your terminal:

```bash
supabase functions deploy quickbooks-oauth-start    --project-ref srjrxyobhfegimgqfpor
supabase functions deploy quickbooks-oauth-callback --project-ref srjrxyobhfegimgqfpor
supabase functions deploy quickbooks-disconnect     --project-ref srjrxyobhfegimgqfpor
```

The functions don't need extra env vars — they read everything
from `public.settings`.

## Step 5 — Run the SQL migration

In the Supabase SQL editor, paste and run the contents of:

```
supabase/migrations/20260527100000_quickbooks_integration.sql
```

This creates `merchant_quickbooks`, `quickbooks_oauth_states`,
adds the `intuit_*` columns to `public.settings`, and wires up
RLS per `_RLS_CONVENTIONS.md` (TO clause + scope by auth.uid() /
admin_users / get_owner_user_id).

## Step 6 — Test the flow

1. Open <https://firebuddy.info/dashboard.html> as a merchant.
2. Sidebar → Settings group → **Integrations**.
3. Click **Connect** on the QuickBooks card.
4. You'll be redirected to Intuit. Sign in with sandbox creds
   (Intuit gives you a fake company on first login).
5. Click **Connect**. Intuit redirects back to FireBuddy.
6. The card should now show **Connected to <Company>** with a
   **SANDBOX** badge.

## Switching to production

Once Intuit approves your app for production:

1. Go back to admin → Settings → Intuit / QuickBooks.
2. Change **Environment** to **Production**.
3. Re-paste the Production **Client ID** and **Client Secret**
   (these are different from the sandbox values).
4. Make sure the **Redirect URI** is registered in the production
   keys section too.
5. Save.

Any merchant already connected in sandbox needs to disconnect and
re-connect — the realm_id namespace is different per environment.

## Phase 2 — invoice + expense push (not shipped yet)

Coming in a follow-up commit. Will add:

- `quickbooks-sync-invoice` edge function — pushes a paid invoice
  as a QBO Sales Receipt.
- `quickbooks-sync-payout` edge function — pushes a tagged payout
  as a QBO Expense with the VAT amount + category mapping.
- Token-refresh helper called before each sync (re-mints access
  token using the stored refresh token, rotates refresh on each
  call).
- UI toggles per merchant: "Auto-push paid invoices", "Auto-push
  tagged payouts".
- Conflict / retry handling.

## Rotation / incident response

- **If a refresh token is leaked**: a merchant disconnects via
  the dashboard, which calls `quickbooks-disconnect`. That function
  POSTs to Intuit's revoke endpoint AND deletes the local row.
  Both happen — even if the revoke call fails, the local token is
  gone so we can't accidentally re-use it.
- **If the client_secret is leaked**: in the Intuit Developer
  dashboard, regenerate it. Then paste the new value into admin →
  Settings → Intuit / QuickBooks (leave Client Secret blank to
  keep the old one, fill in to replace). Every existing connection
  must be disconnected + reconnected.
- See `docs/secret-rotation.md` for the general rotation framework.

## Privacy / GDPR

- We store the merchant's QBO `realm_id`, refresh token, access
  token and the QBO company display name. That's the minimum to
  perform sync. We do **not** store any merchant accounting data
  from QBO locally — every read is live.
- Disconnecting deletes the connection row (RLS DELETE policy
  scoped to `auth.uid()`). The 30-day GDPR retention timer starts
  on disconnect.
- See `docs/breach-notification.md` if a leak is suspected.
