# Webhooks (https://prood.com/docs/apps/checkout/webhooks)

Payment providers send webhooks to notify about async payment events (Multibanco payments, 3DS completions, refunds). The checkout app receives these webhooks and forwards them to the Commerce API for order reconciliation.

## Webhook routes [#webhook-routes]

```
POST /api/webhooks/{provider}/{org}
GET  /api/webhooks/{provider}/{org}  (provider verification)
```

| Segment | Values |
| --- | --- |
| `provider` | `stripe`, `easypay`, `ifthenpay` |
| `org` | Organization ID (tenant) |

### Example URLs [#example-urls]

```
POST https://prood.com/pay/api/webhooks/stripe/org_abc123
POST https://prood.com/pay/api/webhooks/easypay/org_abc123
POST https://prood.com/pay/api/webhooks/ifthenpay/org_abc123
```

## Flow [#flow]

```mermaid
sequenceDiagram
  participant PP as Payment provider
  participant CHK as Checkout app
  participant HOST as checkout-host
  participant API as Commerce API

  PP->>CHK: POST /api/webhooks/{provider}/{org}
  CHK->>CHK: Read body + signature
  CHK->>HOST: forwardPaymentWebhook()
  HOST->>API: POST /v1/webhooks/payments/{provider}
  API->>API: verifyPaymentWebhook()
  API->>API: Update order status
```

## Per-tenant verification [#per-tenant-verification]

Each store connects its own provider in the dashboard (Integrations page). The verification flow:

1. Load the store's `integration_config` for the provider
2. Decrypt its credentials
3. Verify the call with `provider.verifyWebhook()`

What that means depends on the provider. Stripe checks the signature against the store's `webhookSecret`. Ifthenpay checks the anti-phishing key. Easypay notifications are unsigned, so the body is only a trigger: the payment is read back from Easypay's API, and that response is the source of truth.

There is no environment fallback.

```ts
// @prood/commerce
export async function verifyPaymentWebhook(tenantId, providerId, payload, signature) {
  const { provider } = await getTenantPaymentProvider(tenantId, providerId, { includeDisabled: true })
  return provider.verifyWebhook(payload, signature)
}
```

## Provider-specific setup [#provider-specific-setup]

### Stripe [#stripe]

1. In Stripe Dashboard → Webhooks → Add endpoint
2. URL: `https://prood.com/pay/api/webhooks/stripe/{orgId}`
3. Events: `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `checkout.session.expired`, `charge.refunded`
4. Copy signing secret → dashboard Integrations → Stripe → webhook secret

Or run `./scripts/stripe-webhook-setup.sh` to configure via CLI.

### Easypay [#easypay]

1. Configure webhook URL in Easypay merchant panel
2. URL: `https://prood.com/pay/api/webhooks/easypay/{orgId}`
3. Easypay sends payment status notifications

### Ifthenpay [#ifthenpay]

1. Configure callback URL in Ifthenpay backoffice
2. URL: `https://prood.com/pay/api/webhooks/ifthenpay/{orgId}`
3. Anti-phishing key used for verification

## Webhook as safety net [#webhook-as-safety-net]

Webhooks complement the sync-on-return flow:

| Scenario | Primary | Fallback |
| --- | --- | --- |
| Stripe card payment | `checkout.confirm()` on return | Webhook `checkout.session.completed` |
| Customer closes browser during 3DS | — | Webhook `checkout.session.completed` updates order |
| Stripe async payment (bank transfer) | — | Webhook `checkout.session.async_payment_succeeded` |
| Multibanco async payment | — | Webhook when customer pays at ATM |
| MB WAY push notification | — | Webhook on phone confirmation |

The checkout state machine's `handleWebhookUpdate()` can transition from any non-terminal state based on webhook data.

## API webhook endpoint [#api-webhook-endpoint]

The Commerce API also exposes a direct webhook endpoint:

```
POST /v1/webhooks/payments/{provider}?org={orgId}
Header: x-checkout-secret: {CHECKOUT_API_SECRET}
```

This is the target of `forwardPaymentWebhook()`. It can also be called directly if webhooks are routed to the API domain instead of the checkout domain.

## Related pages [#related-pages]

<Cards>
  <Card title="Webhook setup guide" href="/docs/guides/webhook-setup" description="Step-by-step webhook configuration." />
  <Card title="Checkout flow" href="/docs/architecture/checkout-flow" description="Sync-on-return vs webhook strategy." />
  <Card title="Payment integration" href="/docs/guides/payment-integration" description="Provider setup and credentials." />
</Cards>
