> For the complete documentation index, see [llms.txt](https://codemash.gitbook.io/codemash/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codemash.gitbook.io/codemash/cloud/payments/integrations/providers/paddle.md).

# Paddle

Connect Paddle to Payments — keys, webhook URL and signing secret.

Paddle is a merchant of record for software sellers: it takes the payment and handles sales tax for you. Norbix gets Paddle's webhook events (transactions, adjustments, subscriptions).

## Get your credentials

1. Sign in to Paddle — the [sandbox](https://sandbox-vendors.paddle.com/) for tests, the [live dashboard](https://vendors.paddle.com/) for real sales.
2. Open **Developer Tools → Authentication** and create an API key (see [API keys](https://developer.paddle.com/api-reference/about/api-keys)). Copy it (`pdl_sdbx_apikey_…` in the sandbox).
3. Optional: on the same page copy a **client-side token** if your web page uses Paddle.js.
4. Save the integration in Cloud once (below) to get its **Webhook URL**.
5. Open **Developer Tools → Notifications → New destination**, paste the webhook URL and pick the events. Copy the destination's **secret key** (`pdl_ntfset_…`; see [signature verification](https://developer.paddle.com/webhooks/signature-verification)) into **Webhook Endpoint Secret Key**, type the API key again, and **Save**.

## Connect it in Cloud

**Where:** Payments → Integrations → **Add New Integration** → Paddle → **Connect** · `/projects/<project>/payments/integrations/new/paddle`

| Field                       | Required | What it does                                                                                                                   | Example             |
| --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------- |
| API Key                     | yes      | Lets Norbix call Paddle. Secret; encrypted on save and never shown again — type it again whenever you press **Save**.          | `pdl_sdbx_apikey_…` |
| Webhook Endpoint Secret Key | yes      | Checks the `Paddle-Signature` header of each call. It is shown as plain text while you type and is not shown again after save. | `pdl_ntfset_…`      |
| Environment                 | yes      | `sandbox` or `live` — type the word.                                                                                           | `sandbox`           |
| Client-Side Token           | no       | For Paddle.js checkout on your web page.                                                                                       | `test_…`            |
| Integration Name            | yes      | The name in your integrations list.                                                                                            | `Paddle sandbox`    |

<figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-dc7b02366e9bf1ae2d42eb01a3160940423784d9%2Fpaddle-form-1.png?alt=media" alt="New Paddle Integration form filled with example values"><figcaption><p>The filled form (placeholder values).</p></figcaption></figure>

Press **Save**. The page turns into **Editing Paddle integration** with the badges **ENABLED**, **NOT TESTED**, **NOT CONFIRMED**, the integration id (`int_5iTU3kInjX4BVFr02bHBlH`) and the **Webhook URL** — `<Hub address>/v3/payments/webhooks/<account id>/<project id>/paddle/<integration id>`; outside PROD it ends with `?env=<ENV>`. Copy it with its copy button and paste it at Paddle (see **Get your credentials**). **Received webhooks** at the bottom lists every call Paddle makes to it.

<figure><img src="https://712692422-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHy372bqsaex2cqS3ikhL%2Fuploads%2Fgit-blob-5efbbc6ea5d61dbd93b32bb95cec58a45f42a4ed%2Fpaddle-saved-2.png?alt=media" alt="Editing Paddle integration after Save: ENABLED, NOT TESTED, NOT CONFIRMED, the integration id and the Webhook URL"><figcaption><p>After save: the webhook URL to paste at Paddle.</p></figcaption></figure>

Events Norbix accepts from Paddle (pick them at Paddle and in your [trigger](/codemash/cloud/payments/triggers/new.md)): `transaction.completed`, `transaction.payment_failed`, `transaction.canceled`, `adjustment.created`, `subscription.created`, `subscription.updated`, `subscription.canceled`.

## Test and set as default

**Test integration** signs in to Paddle with the API key. With the made-up key of this example it failed: *Paddle authentication failed for integration int\_5iTU3kInjX4BVFr02bHBlH: HTTP 403 Forbidden*. See [Test an integration](/codemash/cloud/payments/integrations/test.md). Payments has no default integration — every enabled integration gets its own calls ([Default integration](/codemash/cloud/payments/integrations/set-default.md)).

## Common errors

| Message                                           | Cause                                                                                 | Fix                                                                          |
| ------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Paddle authentication failed … HTTP 403 Forbidden | Wrong API key, or a sandbox key with **Environment** `live` (or the other way round). | Check the key and the environment, type the key again, **Save**, test again. |
| **Received webhooks** shows **400**               | The secret key of the notification destination does not match.                        | Copy the destination's secret key again and **Save**.                        |
| Required                                          | A required field is empty — secrets count too on every save.                          | Fill the field; type every secret again before **Save**.                     |

## Do it from code

```ts
const { id } = await norbix.hub.payments.savePaymentsIntegration({
  integration: {
    provider: 'Paddle',
    integrationName: 'Paddle live',
    isEnabled: true,
    apiKey: 'pdl_live_apikey_your_key',
    webhookEndpointSecretKey: 'pdl_ntfset_your_secret',
    environment: 'live',
  } as any, // the TS type knows only the shared fields
});
console.log(id); // int_…
```

```sh
norbix hub payments integration save --integration '{"provider":"Paddle","integrationName":"Paddle live","isEnabled":true,"apiKey":"pdl_live_apikey_your_key","webhookEndpointSecretKey":"pdl_ntfset_your_secret","environment":"live"}'
```

The client setup is on the [Payments page](/codemash/cloud/payments.md#use-it-from-code). API: [Save Payments Integration](/codemash/api-reference/payments/integrations/save-payments-integration.md)
