# Checkout verification webhook

Payout sends the `checkout.verification` webhook when a bank payment is matched to a checkout during statement reconciliation. It carries the payer's name and IBAN from the bank statement, encrypted, so that you can verify the payer's identity.

The webhook is sent only for bank payment methods: bank transfer, bank button and PIS.

> [!NOTE]
> To receive this webhook, ask support to enable the `webhook_verification` feature for your account.

## Webhook payload

`checkout.verification` payload:

```json
{
    "external_id": "order-5001",
    "object": "webhook",
    "type": "checkout.verification",
    "data": {
        "object": "checkout",
        "id": 141801,
        "external_id": "order-5001",
        "amount": 300,
        "currency": "EUR",
        "redirect_url": "https://eshop.example.com/payment/redirect",
        "customer": {
            "first_name": "John",
            "last_name": "Doe",
            "email": "john.doe@example.com",
            "iban": null
        },
        "account_details": {
            "name": "H24qnEt9A+haL5xh1Ljgc1gmpo22ULK5W+OIaKskz5w=",
            "iban": "jD1ef5obLE1ujwobPF1+n0RKaiEIcFXcRgJZrCrUNz60s7X2m1SQ9OWLTm3suiRA"
        },
        "payment": {
            "object": "payment",
            "status": "successful",
            "payment_method": "bank_transfer",
            "failure_reason": "",
            "created_at": 1759744800,
            "fee": 8,
            "net": 292
        },
        "metadata": {},
        "status": "succeeded",
        "is_status_final": true
    },
    "nonce": "Sk9RR2EycmIxOUZsWTBtRw",
    "signature": "79be670c341f6acfb04cc1c8ac8f3a94ba296261b72a50c0770e658d82b78013"
}
```

`account_details.name` and `account_details.iban` hold the payer's name and IBAN, encrypted with AES-256-CBC. See [Decrypting account details](#decrypting-account-details).

## Signature verification

Verify `signature` as for every other webhook, see [Simple payment](https://developers.payout.tech/guides/payment-gateway-use-cases-simple-payment.html#step-8):

```text
Pattern: external_id|type|nonce|client_secret
Input:   order-5001|checkout.verification|Sk9RR2EycmIxOUZsWTBtRw|q3dpHpYtDrH-KmGD4HMn5OTEx6IsZPBokQ8CqMONWqMSEePWy9bXd3Ua3KvO7f6C
SHA-256: 79be670c341f6acfb04cc1c8ac8f3a94ba296261b72a50c0770e658d82b78013
```

> [!NOTE]
> The hash is in lowercase hex. Some libraries return uppercase hex; convert it to lowercase before you compare it.

## Decrypting account details

Each encrypted value is decrypted with a key derived from the `client_id` of your API key:

1. **Derive the key**

   Hash `client_id` with SHA-512, encode the hash as lowercase hex and take the first 32 characters. Use these 32 characters as the 32-byte key, as they are; don't hex-decode them.

2. **Split the value**

   Base64-decode the value. The first 16 bytes are the IV, the rest is the ciphertext.

3. **Decrypt**

   Decrypt the ciphertext with AES-256-CBC and remove the PKCS#7 padding.

In Node.js:

```js
const crypto = require("crypto");

function decryptAccountDetail(clientId, value) {
    const key = crypto.createHash("sha512").update(clientId).digest("hex").slice(0, 32);
    const data = Buffer.from(value, "base64");
    const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(key, "utf8"), data.subarray(0, 16));
    return Buffer.concat([decipher.update(data.subarray(16)), decipher.final()]).toString("utf8");
}

decryptAccountDetail("DC995618-7ED8-4070-9DA0-48B6F86551C3", "H24qnEt9A+haL5xh1Ljgc1gmpo22ULK5W+OIaKskz5w=");
// "John Doe"
```

With the `client_id` from the examples, `DC995618-7ED8-4070-9DA0-48B6F86551C3`, the key is `7d47c1ea4bafb442ea5a1ecf114b871c`, and the payload above decrypts to `John Doe` and `SK3112000000198742637541`.

[Retrieve checkout](https://developers.payout.tech/api/payment.html#retrieve_checkout) encrypts the payer details of bank payments the same way.
