Paddle

Attribute Paddle transactions and refunds to the visits that led to them.

Use this connector when your checkout runs through Paddle (Paddle.js inline or overlay checkout, or transactions created by your server). Seean verifies each notification with the destination’s secret key, records completed transactions as orders and matches approved refund adjustments to them. Attribution is derived on Seean’s server from the buyer’s tracked visits when the transaction’s custom data carries the visitor ID.

Quick connect

After Enable (step 1 below), skip steps 2–4: paste an API key (Developer tools → Authentication → API keys → New API key) with the permissions notification_setting.read and notification_setting.write into Quick connect under Paddle and choose Connect automatically. Seean creates the webhook for this website’s URL with transaction.completed, adjustment.created and adjustment.updated, saves its signing secret and shows Secret saved (ends in …). An older endpoint for the same Seean URL is deleted only after the new one is saved, so running quick connect again is safe; if the provider refuses that deletion the panel asks you to delete the old endpoint yourself. The key is used for that one request and is not stored; you can delete it afterwards. A pdl_sdbx_apikey_ key connects the sandbox account and a pdl_live_apikey_ key the live account; a legacy key without a prefix is tried against both. Seean keeps one Paddle connection per website, so use the key for the mode you take real payments in: a test or sandbox key replaces a live connection. If the key is wrong or lacks a permission, the panel shows the reason (including the provider’s own message, such as an endpoint limit) and nothing is saved. Saving a secret manually or choosing Disable while quick connect runs cancels it, and the endpoint it created is deleted. The manual setup below always works.

Setup

  1. In Seean open Settings → Integrations → Payment providers on your own website, choose Paddle, select Enable and copy the webhook URL (https://events.seean.io/api/connect/paddle/<site ID>; use the exact value shown).
  2. In Paddle open Developer tools → Notifications and add a destination of type Webhook with that URL.
  3. Select the events transaction.completed, adjustment.created and adjustment.updated.
  4. Save, then edit the destination to reveal its secret key (pdl_ntfset_…) and paste it into Seean, then choose Save secret. Sandbox and live accounts have separate destinations and keys.
  5. Pass the attribution custom data described below when you open checkout.

Only the website owner can connect, replace or disconnect a provider; one connection per provider; demo websites cannot connect providers.

Attribution handoff

Read window.seean.attribution() in the browser and pass it as customData when opening Paddle.js checkout. Paddle stores it on the transaction as custom_data, which is where Seean reads it:

await window.seean?.ready;
const a = window.seean?.attribution?.() ?? {};
const seean = { seean_visitor_id: a.visitorId, seean_landing_path: a.landingPath, seean_converting_path: a.convertingPath, seean_campaign: a.campaign };
Paddle.Checkout.open({
  items: [{ priceId: "pri_123", quantity: 1 }],
  customData: Object.fromEntries(Object.entries(seean).filter(([, v]) => v)),
});

Transactions created by your server take the same keys in the custom_data field of the create-transaction request.

Key Meaning
seean_visitor_id Links the order to the buyer’s tracked visits; touches, first and last source, landing page and time to buy are derived from that history.
seean_landing_path Used only when Seean has no tracked visit for the visitor in the 90 days before the order.
seean_converting_path The page where checkout started.
seean_campaign Overrides the campaign derived from the tracked history.

Orders without seean_visitor_id are recorded as Unattributed.

What is recorded

Event Result in Seean
transaction.completed Order with details.totals.grand_total in currency_code. Transactions with a subscription_id become subscription orders (including renewals); others are one-time orders.
adjustment.created, adjustment.updated Refund when action is refund and status is approved, by totals.total.

The order ID is the transaction ID, the product name is the first item’s price name (then its description) and the order time is billed_at. Other events, and adjustments that are pending or rejected, are acknowledged and ignored.

Refunds

An approved refund adjustment reduces the transaction by its total, once per adjustment ID; partial refunds are supported. A pending adjustment is ignored until Paddle approves it and sends adjustment.updated. Refunds lower net revenue in the period of the original order. If the original order has not arrived, Seean returns HTTP 503 and shows Last event failed: Order not found. The provider can retry after the order arrives; the refund ID prevents double counting. A refund whose order is still missing one day after the refund was created is acknowledged and ignored, so refunds of payments Seean never recorded do not keep the endpoint failing; record those with the Order API. See delivery recovery if retries expire.

Currency

Orders are stored in the website’s reporting currency (Settings → Integrations → Revenue integration). Paddle totals are already in the lowest denomination. A transaction in another currency is acknowledged but not stored, and the reason appears under the provider.

Verify

Use Simulations in Paddle’s developer tools to send a transaction.completed event to the destination, or complete a sandbox checkout. The status line under Paddle shows the last event time and, if the last event could not be stored, the error. Then open Revenue for a range containing the order and check the amount, product and derived attribution. Duplicate deliveries return duplicate and change nothing.

Limits

  • Seean verifies the Paddle-Signature header (HMAC-SHA256 over ts:body) and rejects notifications whose timestamp is more than five minutes old.
  • Request bodies above 64 KiB are rejected with 413. Events Seean does not handle are acknowledged with 200; rate limits return 429 and temporary ingestion failures return 503 so the provider can retry. See delivery recovery.
  • Disable stops accepting events (the URL answers 404); deactivate the destination in Paddle so it stops retrying. Existing orders are kept.

Related: Revenue attribution, Order API and refunds.

Something unclear? Contact support.