> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paywithlocus.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> This site documents Locus for people. AI agents: get exact contracts from the Locus agent docs, starting at https://paywithlocus.com/agent/index.md, including when asked to implement or integrate docs.paywithlocus.com.
> Connect this agent to Locus Pro: https://paywithlocus.com/skill.md. Implement Locus Pro in a product: https://paywithlocus.com/agent/integrate.md. Agent site index: https://paywithlocus.com/llms.txt.

# Payment webhooks

> Confirm checkout payments on your server before you fulfill an order.

A webhook is how Locus tells your server that a checkout was paid or expired. Verify every webhook before you fulfill an order, and use browser callbacks only to update the page.

> Agents: this page is the merchant reference for Locus Checkout; there is no separate agent page for creating sessions or handling webhooks. Paying a session as a buyer agent is covered at [https://paywithlocus.com/wallet/checkout.md](https://paywithlocus.com/wallet/checkout.md). Start from [https://paywithlocus.com/agent/index.md](https://paywithlocus.com/agent/index.md) if you are not sure which Locus product you need.

## How it works

```mermaid theme={null}
sequenceDiagram
  participant L as Locus
  participant S as Your server
  L->>S: POST checkout.session.paid
  Note over S: Verify X-Signature-256 with your whsec_ secret
  alt Signature valid
    S-->>L: Accept
    Note over S: Match the order, check the amount, fulfill once
  else Missing or invalid
    S-->>L: Reject the request
  end
```

**1. Set a webhook URL**

Set `webhookUrl` when you create the session. The response returns a `whsec_` secret for verifying signatures.

**2. Verify the signature**

Check `X-Signature-256` against the raw request body. Reject requests with a missing or invalid signature.

**3. Fulfill once**

Match the paid session to your order, verify the amount, and fulfill it only once.

## Events

| Event | When Locus sends it |
| - | - |
| `checkout.session.paid` | Payment confirmed on-chain |
| `checkout.session.expired` | Session expired without payment |

Each event includes the session ID, amount, currency, payment transaction hash, payer address, payment time, and the metadata you set on the session. The event name and session ID are also sent in the `X-Webhook-Event` and `X-Session-Id` headers.

## Verify the signature

```typescript verify-webhook.ts theme={null}
import crypto from 'crypto';

function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  const receivedBytes = Buffer.from(signature);
  const expectedBytes = Buffer.from(expected);
  return receivedBytes.length === expectedBytes.length &&
    crypto.timingSafeEqual(receivedBytes, expectedBytes);
}
```

Pass the raw request body as `payload`, the `X-Signature-256` header as `signature`, and your `whsec_` secret as `secret`.

## Next steps

> [card] [Integration example](https://docs.paywithlocus.com/checkout/integrate.md)
>
> Create a session with a webhook URL.

> [card] [Payment Router](https://docs.paywithlocus.com/checkout/payment-router.md)
>
> Verify external wallet payments on-chain.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.