Skip to content

Troubleshooting

Start with the delivery history under Online Payments in the portal. It shows the response code your endpoint returned for the ten most recent attempts. This usually identifies the problem on its own.

What you seeCauseFix
401 or 403Signature check failing, or the endpoint rejecting usVerify against the raw body, confirm the secret matches the one in the portal, and accept any matching v1= value during a rotation
404URL no longer existsUpdate the endpoint in the portal. We do not follow redirects
3xxEndpoint redirectsRegister the final URL. A redirect counts as a failure
400 and other 4xxYour handler rejected the payloadTreated as permanent and not retried. Check the code before returning 4xx
5xxYour handler erroredRetried on the schedule
TimeoutHandler took longer than 5 secondsQueue the event and return 2xx first
TLS errorHandshake failedServe a valid certificate over TLS 1.2 or 1.3. Older versions are refused
Connection errorHost unreachableThe endpoint must resolve to a public address on port 443

Work through these in order:

  1. Webhooks are disabled. The toggle in Online Payments gates all delivery. Deliveries made while disabled are recorded as SKIPPED and are not retried later.

  2. Your framework is rejecting the POST before your handler runs. Rails, Django and Laravel apply CSRF protection to every POST by default, which rejects webhooks with a 403 your handler never sees. Exempt the webhook route:

    app/controllers/webhooks_controller.rb
    class WebhooksController < ApplicationController
    protect_from_forgery except: :paydot
    end
    webhooks/views.py
    from django.views.decorators.csrf import csrf_exempt
    @csrf_exempt
    def paydot_webhook(request): ...
    bootstrap/app.php
    ->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
    'paydot/webhook',
    ]);
    })
  3. The payment never reached a final status. Only EXECUTED, SETTLED, FAILED and CANCELLED dispatch. A payment sitting in PENDING produces no event.

  4. Your endpoint is unreachable from the public internet. Private, loopback and reserved addresses are rejected before any request is sent.

Use Send test event to tell “our delivery is broken” apart from “no event has happened yet”. It exercises the same signing and delivery path as a real payment.

Almost always one of these:

  • The body was re-encoded. Parsing JSON and re-serializing it changes whitespace and key order. Sign the raw bytes.
  • A rotation is in progress. Several v1= values arrive. Accept the request if any one matches.
  • Clock drift. If you reject stale timestamps, an unsynchronized server clock rejects valid events.