Skip to content
Merxian

Get started

Receive the result

The result of a payment arrives after the buyer leaves your site. Merxian sends it to your webhook endpoint as a signed event.

On this page

Why the redirect is not enough#

Merxian redirects the buyer to your successUrl after payment. Do not treat that visit as proof of payment:

  • The buyer can close the browser before the redirect.
  • The buyer can open your successUrl without paying.
  • The payment result can arrive after the redirect.

Use webhook events to learn the result. Show the buyer a “thank you” page on successUrl, but fulfil only after a verified event or a read on your server.

Set up an endpoint#

  1. Add a route on your server that accepts POST requests with a JSON body.

    For example, https://shop.example.com/webhooks/merxian. Live endpoints must use https.

  2. Register the endpoint in the dashboard, in the sandbox environment.

    Give the URL and select the event types. For this guide, select checkout.completed, payment.succeeded, and payment.failed.

  3. Copy the signing secret.

    The secret starts with whsec_. The dashboard shows it once, when you create the endpoint. Store it on your server like an API key, for example as MERXIAN_WEBHOOK_SECRET.

See Configure an endpoint for all options.

Handle an event#

For each request that your endpoint receives:

  1. Verify the Merxian-Signature header against the raw request body. Reject the request with 400 if the check fails. See Verify signatures.
  2. Store the event id. If you already processed that id, return 200 and stop. Merxian can send an event more than once.
  3. Return a 2xx status quickly. Do slow work after the response.
  4. Act on the event type.
Event Meaning What to do
checkout.completed The buyer paid on the hosted page. Mark the checkout as done. Use client_reference_id to find your record.
payment.succeeded The payment was captured. Fulfil the sale. transaction_id names the transaction.
payment.failed The payment failed. Do not fulfil. The buyer can try again while the session is open.

Webhook payloads use snake_case field names, such as transaction_id. The API uses camelCase.

payment.succeeded · request body
{
  "id": "whevt_7Kd2VpXn4TqB9mLzR",
  "object": "event",
  "type": "payment.succeeded",
  "created_at": "2026-09-14T09:24:53Z",
  "api_version": "2026-08-01",
  "account_id": "acct_5Rn8bQ2xW7mK4tLzP",
  "data": {
    "object": {
      "id": "pay_3RtN6wYc8mK2hQvJd",
      "object": "payment",
      "status": "succeeded",
      "transaction_id": "txn_0F8mQ2rXbT4kL9pZa",
      "checkout_id": "cs_6HvB2nQx9LmT4kWpR",
      "amount": 14280,
      "currency": "EUR"
    }
  }
}

The example shows some fields only. The amount 14280 in EUR is €142.80. The full payload is on each event page.

Events can arrive in any order. For example, payment.succeeded can arrive before checkout.completed. Write each handler so that it works in either order.

Read the transaction as a fallback#

If an event is late, or you need the current state, read the transaction from your server:

Retrieve a transaction
curl https://api.merxian.com/v1/transactions/txn_0F8mQ2rXbT4kL9pZa \
  -H "Authorization: Bearer $MERXIAN_API_KEY"

status is completed and paymentStatus is paid when the transaction is fully paid. This read needs the transactions:read scope.

Try refund, payment.succeeded,POST /v1/transactions, orIdempotency-Key.