Skip to content

Webhooks

Webhooks are how digid pay tells your server what happened. Register an HTTPS endpoint, subscribe to events, verify signatures, and reconcile — never fulfil an order on a client callback alone.

Register an endpoint

bash
curl https://api.digid.cc/v1/webhook_endpoints \
  -u sk_live_...: \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/digidpay/webhooks",
    "events": ["payment_intent.succeeded", "payment_intent.payment_failed"]
  }'

The response includes your signing secret — shown once. Keep it server-side.

Event catalog

EventMeaning
payment_intent.processingConfirmed, authorising.
payment_intent.succeededAuthorised and captured. Fulfil the order.
payment_intent.payment_failedDeclined / failed.
payment_intent.cancelledCancelled before completion.
payment_intent.requires_actionSCA challenge surfaced (informational).
payment_intent.requires_approvalAgent intent awaiting human approval.
refund.createdRefund request recorded.
refund.succeededRefund completed.
refund.failedRefund rejected.

Verify signatures (HMAC)

Every delivery carries a Digidpay-Signature header:

Digidpay-Signature: t=1725794400,v1=9a8f...b2

Verification: build the signed payload as {timestamp}.{body} where body is the raw request body, compute HMAC-SHA256 with your endpoint secret, and compare v1.

python
import hashlib, hmac, json, time

SECRET = "whsec_..."  # your endpoint secret

def verify(raw_body: bytes, header: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts, sig = parts["t"], parts["v1"]
    if abs(int(ts) - time.time()) > 300:   # replay window
        return False
    expected = hmac.new(SECRET.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)
javascript
import { createHmac, timingSafeEqual } from 'node:crypto'

const SECRET = 'whsec_...'

export function verify(rawBody, header) {
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
  const { t: ts, v1: sig } = parts
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
  const expected = createHmac('sha256', SECRET).update(`${ts}.`).update(rawBody).digest('hex')
  const a = Buffer.from(expected), b = Buffer.from(sig)
  return a.length === b.length && timingSafeEqual(a, b)
}
bash
# Verify with openssl (header values filled from the delivery)
payload="$DIGIDPAY_BODY"            # raw request body
ts="1725794400"
sig="9a8f..."                        # v1 value from Digidpay-Signature
computed=$(printf '%s.%s' "$ts" "$payload" | openssl dgst -sha256 -hmac "whsec_..." | awk '{print $2}')
[ "$computed" = "$sig" ] && echo VALID || echo INVALID

Idempotent processing

Deliveries are at-least-once and may retry. Processing must be idempotent — key on the event id and ignore duplicates:

python
seen = set()
def handle(event_id):
    if event_id in seen:
        return
    seen.add(event_id)
    # fulfil / update order

Retries & backoff

  • Deliveries retry with exponential backoff until acknowledged (2xx) or the retry budget is exhausted.
  • Always return 2xx promptly once you have accepted the event.

Manage endpoints

bash
curl https://api.digid.cc/v1/webhook_endpoints -u sk_live_...:            # list
curl https://api.digid.cc/v1/webhook_endpoints/we_1Ef... -u sk_live_...:  # retrieve
curl -X POST https://api.digid.cc/v1/webhook_endpoints/we_1Ef... \
  -u sk_live_...: -H "Content-Type: application/json" -d '{"enabled": false}'   # update
curl -X DELETE https://api.digid.cc/v1/webhook_endpoints/we_1Ef... -u sk_live_...:  # delete

Delivery log & replay

Every delivery is logged and retained 12 months. Inspect failures and replay from the dashboard or the API:

bash
curl "https://api.digid.cc/v1/webhook_endpoints/we_1Ef.../deliveries" -u sk_live_...:
curl -X POST "https://api.digid.cc/v1/webhook_endpoints/we_1Ef.../deliveries/dlv_.../replay" \
  -u sk_live_...:

Signature key rotation

Rotate an endpoint's signing secret by updating the endpoint, capturing the new secret once, and updating your verifier. Keep the old secret verifying during a short overlap if deliveries may still be in flight.

digid pay — built in Europe.