# Square Webhooks

## When to Use This Skill

* How do I receive Square webhooks?
* How do I verify Square webhook signatures?
* How do I handle `payment.updated` or `refund.created` events?
* Why is my Square webhook signature verification failing?
* Setting up Square webhook handlers for payments, refunds, invoices, or orders

## Verification (core)

Square signs each webhook with an HMAC-SHA256 over the notification URL
concatenated with the raw request body, base64-encoded, delivered in the
`x-square-hmacsha256-signature` header. The notification URL is part of the
signed content, so it must exactly match the URL configured in your Square
subscription. Always verify the raw body — never `JSON.parse` first.

Node (official Square SDK — recommended):

```javascript
const { WebhooksHelper } = require('square');

// requestBody is the raw HTTP body string; notificationUrl must match Square exactly
const isValid = await WebhooksHelper.verifySignature({
  requestBody: rawBody,
  signatureHeader: req.headers['x-square-hmacsha256-signature'],
  signatureKey: process.env.SQUARE_WEBHOOK_SIGNATURE_KEY,
  notificationUrl: process.env.SQUARE_WEBHOOK_URL,
});
if (!isValid) return res.status(400).send('Invalid signature');

```

Python (manual — mirrors what the SDK does, timing-safe):

```python
import hmac, hashlib, base64

def is_valid(raw_body: bytes, signature: str, key: str, url: str) -> bool:
    payload = url.encode() + raw_body            # notification URL + raw body
    digest = hmac.new(key.encode(), payload, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode()
    return hmac.compare_digest(expected, signature)

```

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

## Common Event Types

Square delivers the event type in the body's `type` field (not a header).

| Event | Description |
| --- | --- |
| `payment.created` | A new payment was created |
| `payment.updated` | A payment changed state (e.g. completed) |
| `refund.created` | A refund was initiated |
| `refund.updated` | A refund changed state |
| `invoice.payment_made` | A payment was made against an invoice |
| `order.created` | An order was created |
| `order.updated` | An order was updated |
| `customer.created` | A new customer was created |

> For the full event reference, see [Square Webhook Events](https://developer.squareup.com/docs/webhooks/overview).

## Environment Variables

```bash
SQUARE_WEBHOOK_SIGNATURE_KEY=your_signature_key   # From the webhook subscription in Developer Console
SQUARE_WEBHOOK_URL=https://your-app.com/webhooks/square  # Must match the subscription's notification URL exactly

```

## Local Development

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

```

When testing locally, set `SQUARE_WEBHOOK_URL` to the public tunnel URL you
registered as the notification URL in Square — the value is part of the signed
content, so a mismatch causes verification to fail.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/square-webhooks/references/overview.md) - Square webhook concepts and common events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/square-webhooks/references/setup.md) - Developer Console configuration and signature key
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/square-webhooks/references/verification.md) - Signature verification details and gotchas