# Payroc Webhooks

Payroc (payment processor) sends a CloudEvents 1.0 JSON `POST` to your endpoint when something
changes on a processing account or terminal order. Covers event subscriptions only, not Hosted Payments Page callbacks.

Requests are **not signed**. Each one carries the subscription's `secret` as plain text in a
`Payroc-Secret` header, and your endpoint authenticates the sender by comparing that header with
the secret you chose when you created the subscription.

> **Security note:** a static shared credential authenticates the sender, not the body, and gives
> no replay protection (there is no signature and no timestamp). Serve the endpoint over HTTPS,
> dedupe on the event `id`, and rotate the credential if it leaks.

## When to Use This Skill

* How do I receive Payroc webhooks / event subscriptions?
* How do I verify the `Payroc-Secret` header?
* Does Payroc sign webhook payloads? (No: shared secret header)
* How do I handle `processingAccount.status.changed` or `terminalOrder.status.changed`?
* Why does my Payroc webhook endpoint return 401, or why does Payroc keep retrying?

## Verification (core)

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

function verifyPayrocRequest(headers, secret = process.env.PAYROC_WEBHOOK_SECRET) {
  if (!secret) return false;                    // never accept when unconfigured
  const received = headers['payroc-secret'];    // whole header value, plain text
  if (!received) return false;
  const a = Buffer.from(received);
  const b = Buffer.from(secret);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

* Header: `Payroc-Secret: <secret>` (case-insensitive lookup). Compare the **whole value** to
  `PAYROC_WEBHOOK_SECRET`, in constant time. In Python use `hmac.compare_digest` on **bytes**
  (encode both sides; passing `str` raises on non-ASCII).
* Return `401` when the header is missing or wrong, `400` for a body that isn't a JSON object.
* Read the body raw (`express.raw({ type: '*/*' })`) so any content type works, check the header,
  then parse.
* Return exactly `200` on success. Otherwise Payroc retries five times, then emails the
  subscription's support address.
* Dispatch on `type`; dedupe on `id`.

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

## Event Types

The body is a CloudEvent: `specversion` (`1.0`), `type`, `version`, `source` (`payroc`), `id`,
`time`, `datacontenttype` and `data`.

| Event (`type`) | Sent When |
| --- | --- |
| `processingAccount.status.changed` | A processing account's status changes |
| `processingAccount.riskStatus.changed` | A processing account's risk status changes |
| `processingAccount.signature.signed` | A processing account's agreement is signed |
| `terminalOrder.status.changed` | A terminal order's status changes |

## Environment Variables

```bash
PAYROC_WEBHOOK_SECRET=your-subscription-secret   # notifications[].secret, 16 to 64 characters

```

## Local Development

Payroc can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 payroc --path /webhooks/payroc

```

Use the printed URL as the notification `uri` of your subscription.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/payroc-webhooks/references/overview.md) - What Payroc webhooks are, events and payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/payroc-webhooks/references/setup.md) - Create an event subscription with the API
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/payroc-webhooks/references/verification.md) - Secret header check, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/payroc-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests