Skip to main content

Webhook URL

Overview

This feature delivers the data of a paid invoice to the merchant's server. It exists so the merchant's store can process the payment automatically and deliver the product or service to your customer.

The Webhook URL is set individually for each project and points to the payment handler scripts inside the merchant's store. The address must use HTTPS and a domain name: IP addresses are not accepted, and the certificate is verified on every delivery.

Every time an invoice's status changes to Paid, the service sends a POST request to this URL in the following format:

{
"wallet":{
"id":"47aa71e2-07a0-482e-9172-7114d7376ba0",
"name":"usdt-tron",
"blockchain":"tron",
"cryptocurrency":"usdt",
"address":"TKbstUwMzLrfTAGL4erYb7gc7ghmHQ9zG7"
},
"project":{
"id":"9deea1e2-0c08-41a3-bdc2-a34eada3892d",
"name":"My project",
"commissionPayer":"seller",
"commissionRate":1
},
"invoice":{
"id":"a4c9e2ee-9a03-43e5-a1a1-00caf679d16a",
"uid":"AFhygKX21ecd",
"createDatetime":"2024-02-26 13:29:24",
"timeToPayDatetime":"2024-02-27 01:29:24",
"commissionFiatUSD":0.05,
"amountFiatUSD":5.02,
"amountFiat":5,
"calcAmountFiat":5.02,
"currencyFiat":"USD",
"description":null,
"serviceData":null,
"status":"paid"
},
"payment":{
"id":"f986ad8d-2298-473d-982a-efbc817b975d",
"amount":5.02,
"hash":"74763b65e43bcc9492a6ce9a7f26fbfdbd7635aecd3454420b5e9534cba50ee6",
"transactionDatetime":"2024-02-26 13:32:57"
}
}

The notification is sent on every transition to Paid—both when the service found the payment automatically and when the merchant matched the payment to the invoice manually.

Request parameters

ParameterDescription
wallet.idWallet ID in UUID format
wallet.nameWallet name
wallet.blockchainThe crypto wallet's blockchain
wallet.cryptocurrencyThe wallet's cryptocurrency
wallet.addressThe crypto wallet's address
project.idProject ID in UUID format
project.nameProject name
project.commissionPayerWho pays the service fee
project.commissionRateFee rate in %
invoice.idInvoice ID in UUID format
invoice.uidInvoice ID (for the customer)
invoice.createDatetimeInvoice creation date and time (UTC)
invoice.timeToPayDatetimeDate and time until which the invoice is valid for the customer (UTC). The payment search continues for 1 more hour after this mark—to account for slow networks. So a notification may arrive for an invoice that was already shown as expired
invoice.commissionFiatUSDService fee amount. Calculated from the invoice.amountFiatUSD amount
invoice.amountFiatUSDAmount in USD. At invoice creation it is calculated from invoice.amountFiat at the current exchange rate. At the moment of payment it is overwritten with the amount actually received, converted to USD at the rate at that moment. The fee in invoice.commissionFiatUSD is recalculated from it as well
invoice.amountFiatThe original invoice amount in fiat currency. Does not change
invoice.calcAmountFiatThe calculated amount in the invoice.currencyFiat currency at the crypto exchange rate at the moment of payment. It can differ from invoice.amountFiat because the customer may have paid the invoice some time after creation rather than right away. Over that time, the crypto rate against invoice.currencyFiat could move either way
invoice.currencyFiatFiat currency
invoice.descriptionThe description set at invoice creation
invoice.serviceDataThe service data set at invoice creation
invoice.statusInvoice status
payment.idPayment ID in UUID format
payment.amountPayment amount in cryptocurrency
payment.hashThe on-chain transaction hash
payment.transactionDatetimeDate and time of the on-chain transaction (UTC)

Notification signature

Every request is signed with the webhook secret. The signature travels in the X-Timestamp and X-Signature headers and lets you make sure the notification came from the service, not from an outsider who learned your handler's address.

Verify the signature before processing the order. The mechanics and ready-made examples are in Webhook signature verification.

Handling recommendations

Do not reject a notification because the payment deadline has passed. A check like “the invoice is expired, so the payment is invalid” looks logical but cuts off a share of real payments: on slow networks a transaction may confirm after the deadline, and the merchant can manually match a payment to an expired invoice. The notification itself confirms the payment.

Verify the amount against the original field invoice.amountFiat. This is the invoice amount as issued, and it does not change. The amountFiatUSD field reflects the amount actually received and can differ from the issued one—both because of rate movement and because of manual payment matching.

Parse amounts as numbers. Trailing zeros are dropped: an amount of 10.00 arrives as 10; format it on your side for display. Amounts below 0.0001 arrive in exponential notation, for example 1.0e-6—standard JSON parsing returns the correct number; only manual string parsing breaks.

Handle notifications idempotently. The same notification can arrive again—for example, if your script processed the payment successfully but returned a non-2xx code. Before fulfilling the order, check whether this invoice.id has already been processed.

Escape values on output. The invoice.description and invoice.serviceData fields come back exactly as the merchant submitted them. If you render them in HTML, escape them on your side.

Delivery schedule

The server at the Webhook URL must respond with a 2xx HTTP code. Any other code, a timeout, or a dropped connection counts as a failed delivery.

Redirects are not followed: a 301 or 302 response is a failed delivery, not a hop to the new address. Set the handler's final address.

The connection is allowed 5 seconds, the whole request 10 seconds. If the handler does not fit within that, the delivery counts as failed.

After a failed delivery, the service retries on the following schedule:

  • 5 minutes after the last failed delivery
  • After 15 minutes
  • After 30 minutes
  • After 1 hour
  • After 3 hours
  • After 6 hours
  • After 12 hours
  • After 24 hours

After that, delivery attempts stop.

Disabling the Webhook URL

Sometimes a store's webhook script processes the payment correctly but returns a non-2xx HTTP code. This leads to frequent retries from our server on the schedule above, putting extra load on both our server and yours.

To prevent such cases, we have a mechanism that disables the Webhook URL in a project. To avoid it, take the following steps:

  1. Change your webhook handler code so it returns a 2xx code, usually 200, on successful payment processing. Test it with any emulator, for example Postman.
  2. Contact technical support to correct the settings.
  3. Re-enable the Webhook URL in the project settings and save the project.