# Prive Webhooks

Prive is a subscription app for Shopify merchants, now part of **Recurly Commerce**. The docs, API and headers still use the Prive name, so this skill does too.

## When to Use This Skill

* How do I receive Prive webhooks?
* How do I verify the `x-prive-hmac-sha256` signature on a Prive webhook?
* How do I handle `subscriptions/created` or `billingAttempts/failed` webhooks?
* Why is my Prive webhook signature verification failing?
* How do I route Prive topics to separate handlers?

## Verification (core)

Prive signs the **raw request body** with HMAC-SHA256, keyed with the secret token Prive gives the merchant, and sends the digest in `x-prive-hmac-sha256` as a **bare base64 string** (Shopify-style, no prefix). Prive's Node sample passes `'hex'` as a third argument to `update()`, which Node ignores, so the digest is base64, not hex. There is no timestamp. Compare in constant time, without decoding the header.

Node:

```javascript
const crypto = require('crypto');

function verifyPriveSignature(rawBody, header, secret) {
  if (!header || !secret) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
  const a = Buffer.from(header, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

Python:

```python
import base64, hashlib, hmac

def verify_prive_signature(raw_body: bytes, header: str | None, secret: str) -> bool:
    if not header or not secret:
        return False
    expected = base64.b64encode(
        hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
    ).decode()
    # Encode both sides: compare_digest raises TypeError on non-ASCII str
    return hmac.compare_digest(header.encode("utf-8"), expected.encode("utf-8"))

```

Read the raw body before any JSON parsing. In Express use `express.raw({ type: '*/*' })` so any content type verifies.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/prive-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/prive-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/prive-webhooks/examples/fastapi/).

## Webhook Topics

Each Prive webhook subscribes to **one topic**, chosen when the webhook is created (`topic` field of the create-webhook endpoint).

| Topic | Description |
| --- | --- |
| `subscriptions/created` | A subscription was created |
| `subscriptions/line_removed` | A line was removed from a subscription |
| `subscriptions/status/updated` | A subscription's status changed |
| `orders/created` | A subscription order was created |
| `billingAttempts/created` | A billing attempt was created |
| `billingAttempts/success` | A billing attempt succeeded |
| `billingAttempts/failed` | A billing attempt failed |
| `paymentMethod/updated` | A payment method was updated |
| `subscriptionActivities/created` | A subscription activity was created |
| `subscription/customAttributes/updated` | A subscription's custom attributes were updated |

No topic header or body field is documented. Create **one webhook per topic, each with its own endpoint path** (for example `/webhooks/prive/billing-attempts-failed`), or one Hookdeck connection per topic, and dispatch on the path. The examples map those paths to topics.

## Setup

1. Prive (now Recurly Commerce) provides the secret token. Ask Prive support for it if you don't have it.
2. Create a webhook per topic with the Prive create-webhook endpoint, pointing at your endpoint URL.
3. With Hookdeck, create a Prive source and give it the same secret token as its signing secret, then add one connection per topic. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/prive-webhooks/references/setup.md).

## Environment Variables

```bash
PRIVE_WEBHOOK_SECRET=your_prive_secret_token   # Provided by Prive; ask Prive support if you don't have it

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 prive --path /webhooks/prive

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/prive-webhooks/references/overview.md) - Prive webhook concepts and topics
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/prive-webhooks/references/setup.md) - Getting the secret token and creating webhooks
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/prive-webhooks/references/verification.md) - Signature verification details and gotchas