Merchant systems can receive real-time notifications from the xMoney platform via webhooks. Currently, the available events are:
- Order payment detected (an incoming payment has been seen on-chain but is not yet confirmed)
- Order payment confirmation
- Order cancellation
The webhook URL is specified using the callback_url field when creating an order.
Webhook requests must respond with an HTTP 2xx status (specifically 200-207) to indicate success. Any other response or a lack of response is considered a failure, triggering a retry mechanism.
After the initial delivery attempt, xMoney retries up to 15 more times (for a maximum of 16 delivery attempts). Attempts are made at fixed minute offsets that follow a Fibonacci sequence, measured from the moment the event is first queued:
0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 (minutes)The last attempt happens 987 minutes (~16 hours 27 minutes) after the event is queued. If every attempt fails, the webhook is marked as failed and an email notification with error details is sent.
All webhook requests from xMoney are signed using a Webhooks Secret for validation.
- Webhooks are automatically set up and validated in our integrations.
- For custom integrations, we provide libraries for request validation.
- Each webhook request is signed to prevent unauthorized access.
Events notify merchants of order-related activities in real time. Webhooks deliver these events to the specified callback_url.
| Event Name | state | Description |
|---|---|---|
ORDER.PAYMENT.DETECTED | detected | An incoming payment has been detected on-chain but is not yet confirmed. |
ORDER.PAYMENT.RECEIVED | completed | Payment successfully confirmed. |
ORDER.PAYMENT.CANCELLED | cancelled | Payment was canceled (for example, the order expired). |
Every webhook request has the following top-level fields:
| Field | Description |
|---|---|
event_type | The event name (see the table above). |
state | The internal state corresponding to the event (detected, completed, or cancelled). |
resource | An object describing the order/payment (see below). |
signature | The HMAC-SHA256 signature of the payload (see Verifying Events). |
encrypted_signature | Optional. An additional signature included only for specific whitelisted integrations (for example, xPortal and onramp partners). Standard integrations can ignore it. |
The resource object always includes the following fields:
| Field | Description |
|---|---|
reference | Your internal reference for the order. |
amount | The fiat amount of the order, formatted as a decimal string. |
currency | The 3-letter fiat currency code (ISO 4217). |
For ORDER.PAYMENT.RECEIVED events, some whitelisted integrations additionally receive crypto payment details in resource: tx_hash, crypto_currency, blockchain_network, crypto_amount, and, where applicable, refund_crypto_amount and refundable. These extra fields are not sent for standard integrations.
To ensure authenticity, webhook events are signed using HMAC SHA256 with the Webhooks Secret as the key. The payload is sorted alphabetically before signing.
{
"event_type": "ORDER.PAYMENT.RECEIVED",
"resource": {
"reference": "1400012634",
"amount": "10.8200",
"currency": "EUR"
},
"signature": "5ef8a5994e917c14479b31f690d4d2a023dfcc6059081504e3087977b21580ab",
"state": "completed"
}{
"event_type": "ORDER.PAYMENT.RECEIVED",
"resource": {
"amount": "10.8200",
"currency": "EUR",
"reference": "1400012634"
},
"state": "completed"
}joined_payload="event_typeORDER.PAYMENT.RECEIVEDresourceamount10.8200resourcecurrencyEURresourcereference1400012634statecompleted"
signed_payload=$(echo -n "$joined_payload" | openssl dgst -sha256 -hmac "$secret")The resulting signature should match the one in the webhook request.
For a PHP validation example, see this reference.
Webhook responses must follow these rules:
- Success (200 OK) →
{ "success": true }(optional JSON response) - Invalid signature or malformed request (400 Bad Request)
- Internal processing errors (500 Internal Server Error)
If the webhook fails, retries occur as per the Fibonacci backoff schedule described above (the initial attempt plus up to 15 retries, for 16 delivery attempts in total). After that, an email notification is sent with failure details.