# PISP payment status webhook

When you initiate a bank payment (PIS) for a Payout checkout, notify Payout of its final result by sending a **signed** JSON request. `data.resource_id` is the ID of the Payout checkout.

Send the status once the payment is final:

- `completed`: the payment succeeded. Payout creates the payment and the checkout succeeds.
- Any other value, such as `failed`, `rejected` or `canceled`: the payment failed. Payout marks the checkout as failed.

> [!IMPORTANT]
> Do not send intermediate states such as `pending`. Every status other than `completed` fails the checkout.

## Endpoint

```http
POST https://{our-domain}/api/v1/integrations/pisp
Content-Type: application/json
```

`{our-domain}` is provided during onboarding, together with your `client_id` and the secret key for the signature.

## Request body

| Field | Type | Description |
| --- | --- | --- |
| `type` | string | Always `order_status` |
| `data.resource_id` | string | ID of the Payout checkout |
| `data.status` | string | `completed`, or the reason the payment failed |
| `data.nonce` | string | Standard Base64 of at least 16 cryptographically random bytes, unique per request |
| `data.client_id` | string | The ID Payout assigned to you (your account ID) |
| `signature` | string | HMAC signature of the request, see [Signature](#signature) |

The body is UTF-8 JSON. Base64 values use the standard alphabet, not the URL-safe one.

## Signature

1. **Build the canonical string.** Join `resource_id`, `status`, `nonce` and `client_id` with `|`, in this order and without spaces:

   ```text
   resource_id|status|nonce|client_id
   ```

2. **Sign it.** Compute HMAC-SHA256 of the canonical string with your secret key as the key.
3. **Encode it.** Encode the raw HMAC bytes with standard Base64 and send the result as the top-level `signature`.

> [!IMPORTANT]
> The canonical string must use exactly the values in the body.

```text
payload_string = resource_id + "|" + status + "|" + nonce + "|" + client_id
raw_hmac       = HMAC_SHA256(key = secret_key, message = payload_string)
signature      = BASE64_ENCODE(raw_hmac)
```

## Example

With the secret key `example-pisp-api-key`, the canonical string

```text
12345|completed|bC8w3o7M0y7o0t4cC8h3jg==|123
```

gives this request:

```http
POST /api/v1/integrations/pisp HTTP/1.1
Host: {our-domain}
Content-Type: application/json

{
  "type": "order_status",
  "data": {
    "resource_id": "12345",
    "status": "completed",
    "nonce": "bC8w3o7M0y7o0t4cC8h3jg==",
    "client_id": "123"
  },
  "signature": "gDw25w5+/1+5SAhZBXvqdfestQ6IJqz5bYwEwbDqeU4="
}
```

## Responses

| Case | HTTP status | Body |
| --- | --- | --- |
| Status accepted | `200` | `"ok"` |
| `signature` missing | `400` | `{"error": "missing_signature", "message": "Signature must be provided"}` |
| Signature does not match, or unknown `client_id` | `401` | `{"error": "invalid_signature", "message": "Signature verification failed"}` |
| No checkout with this `resource_id` for your `client_id` | `404` | `{"error": "not_found", "message": "Resource does not exist"}` |
| Any other problem, for example the checkout already has a payment | `422` | `{"error": "invalid_payload"}` |
