# Paddle

Attribute Paddle Billing transactions from the overlay checkout or the API.

## 1. Connect Paddle

1. In VisitTrack, open **Settings → Revenue** and pick **Paddle**.
2. Copy the webhook URL shown there. It looks like `https://visitrack.app/api/paddle-webhook/YOUR_SITE_ID`.
3. In Paddle: Developer tools → Notifications → **New destination**. Paste the URL and subscribe to `transaction.completed` and `adjustment.created`.
4. Copy the secret key (`pdl_ntfset_…`) Paddle gives you back into VisitTrack and press **Connect**. It's stored encrypted and only used to verify signatures.

## 2. Pass the visitor id

Paddle calls it custom data. Set `visitrack_visitor_id` on the checkout or the transaction.

Paddle.js overlay:
```
const visitorId = localStorage.getItem("_ana_vid");

Paddle.Checkout.open({
  items: [{ priceId: "pri_...", quantity: 1 }],
  customData: { visitrack_visitor_id: visitorId ?? "" },
});
```

Transactions API:
```
curl -X POST https://api.paddle.com/transactions \
  -H "Authorization: Bearer $PADDLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{ "price_id": "pri_...", "quantity": 1 }],
    "custom_data": { "visitrack_visitor_id": "VISITOR_ID" }
  }'
```

## Events

| Event | What VisitTrack does |
| --- | --- |
| `transaction.completed` | Records the transaction (grand total, currency) against the visitor in `custom_data.visitrack_visitor_id`. |
| `adjustment.created` | When the action is `refund`, subtracts it from the original transaction. Credits and chargebacks are ignored. |
| anything else | Acknowledged with `200` and ignored. |

## Test it

1. Open your site in a normal browser tab so the tracker records a visit (localhost is ignored unless the script tag has `data-allow-local`).
2. Complete a checkout in test mode.
3. Open **Revenue** in the dashboard. The payment shows up within a few seconds, with the referrer, campaign and landing page that brought that visitor in.

> **Nothing showed up?** — See [Missing or unattributed payments](https://visitrack.app/docs/revenue-troubleshooting). The webhook response body always says why a payment was skipped.
