Listen for Webhooks

Which webhooks a Hosted Checkout payment sends, and a handler that confirms the order safely.

Webhooks are the most reliable way to learn the result of a payment. Instead of polling the API, Tonder sends an HTTP POST to your server each time the payment changes status.

Which webhooks you receive

Hosted Checkout 2.0 processes payments through the Direct API, so you receive the standard Tonder payment webhooks — the same ones described in How webhooks work. The event arrives in event_type:

event_typeMeaning
payment_PendingThe payment was initiated and is processing (a 3D Secure challenge, a voucher or a transfer waiting to be paid).
payment_SuccessThe payment completed successfully.
payment_FailedThe payment failed.

Payloads and the full list are in the Events catalog.

There are no session-level webhooks: session.created, session.completed and session.expired are not sent. To follow a session, use the payment webhooks, the embedded events or Get a session. Enrollment sessions create no payment, so they send no webhook.

Which webhooks you receive is set per account. If your account still runs on the legacy flow, you receive the payloads described in Legacy webhooks. Not sure which applies to you? Check with Tonder support.

Prerequisites

You need a publicly accessible URL on your server — for example https://your-store.com/webhooks/tonder — that can receive POST requests. This cannot be a localhost URL.

Configure and listen

  1. Log in to the Dashboard: dashboard-stage.tonder.io (Sandbox) or dashboard.tonder.io (Production).
  2. Go to Developers → Webhooks.
  3. Click Create endpoint, paste your public endpoint URL and select the payment events (Endpoints covers every field).
  4. Click Create and keep the signing secret it shows. Register an endpoint in each environment. On accounts without Webhooks 2.0 the button reads Add Endpoint and the URL receives every event.

Create the session with your order reference inside metadata (metadata.external_id). It comes back in every webhook, and it is the key you reconcile on. See Correlation keys.

Verify the signature, then respond 200 OK immediately, before running business logic — within 10 seconds. With Webhooks 2.0 a delivery that gets no 2xx is retried automatically (see Delivery and retries); without it, each event is delivered once and a failed delivery is not resent.

const express = require('express');
const { Webhook } = require('standardwebhooks');

const app = express();
const wh = new Webhook(process.env.TONDER_WEBHOOK_SECRET); // "whsec_...", server-side only

app.post('/webhooks/tonder', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    // 1. Verify the signature on the raw body
    event = wh.verify(req.body, req.headers);
  } catch {
    return res.sendStatus(400);
  }

  // 2. Acknowledge receipt immediately
  res.sendStatus(200);

  // 3. Process the event — once per webhook-id (req.headers['webhook-id'])
  const orderId = event.metadata && event.metadata.external_id;
  switch (event.event_type) {
    case 'payment_Success':
      // TODO: confirm server-side, then fulfill the order `orderId`.
      break;
    case 'payment_Failed':
      // The customer can retry in the same session; do not cancel the order yet.
      break;
    case 'payment_Pending':
      // Waiting for 3D Secure, a voucher payment or a transfer.
      break;
    default:
      // endpoint.test and events you don't handle: nothing to do
  }
});

app.listen(3000, () => console.log('Listening for webhooks on port 3000'));

Without Webhooks 2.0 webhooks are not signed: check the authentication header you configured instead of calling verify. Details in Verify signatures.

Do not rely on the payload alone. Before fulfilling the order, read the session or the payment from the API — see Check the status.

A customer can retry inside one session, so you may receive a failed attempt followed by a successful one for the same order. Process webhooks idempotently and treat payment_Success as the only event that completes the order. See Delivery.

Next steps

Was this page helpful?

On this page