Webhook Notifications
Integrating webhook notifications is a significant step in the process, enabling your system to respond quickly to changes in payment status.
When registering in the system, specify the URL where transaction status notifications will be sent, both successful and unsuccessful. In the merchant account, you can configure a separate webhook for each terminal and select which types of webhook you wish to receive.
General Logic
Webhooks are used to synchronize statuses between WATA and the merchant system in real time.
Typical scenario:
- A payment transaction or refund status changes in WATA.
- WATA sends an HTTP request to the merchant webhook URL.
- The merchant system processes the notification.
- The merchant system must return an HTTP
200 OKresponse.
If the webhook was not delivered successfully, further behavior depends on the notification type.
Notification Types
Webhook notifications have three types:
| Webhook type | When it arrives | What it is used for |
|---|---|---|
| Pre-payment | Immediately after the customer clicks "Pay", but before the request to the bank | Product availability check, price relevance check, fraud monitoring, or another merchant-side order check |
| Post-payment | After successful transaction confirmation by the bank or after a payment error | Executing business logic: granting access, changing order status in the database, sending a receipt, and so on. In the merchant account terminal settings, you can disable receiving webhooks with the Declined status if needed |
| Refund | After a successful refund or a refund error | Balance adjustment, order cancellation, or service deactivation |
Processing Rules and Timeouts
Your server must return HTTP 200 OK for every webhook notification.
If no response is received or the response is not 200:
| Webhook type | Behavior when response fails | Response timeout |
|---|---|---|
| Pre-payment | The transaction is considered not approved and is automatically declined. The request to the bank is not sent | 10 seconds |
| Post-payment | WATA retries sending with an increasing interval for the next 32 hours | 1 minute |
| Refund | WATA retries sending with an increasing interval for the next 32 hours | 1 minute |
Webhook Notification Parameters
| Parameter | Type | Comment |
|---|---|---|
| transactionType | String | Transaction type: CardCrypto for ruble and foreign-currency cards, SBP for the Faster Payments System, TPay for the T-Bank method, SberPay for the Sberbank method |
| kind | String | Transaction kind: Payment for payment, Refund for refund |
| id | UUID | Transaction identifier in the WATA system. This is not the payment link identifier |
| transactionId | UUID | Identifier of the created refund transaction in the WATA system |
| originalTransactionId | UUID | Used for refunds. Identifier of the original transaction for which a partial or full refund is made |
| terminalPublicId | UUID | Public identifier of the merchant terminal |
| transactionStatus | String | Transaction status (Created, Pending, Paid, Declined). See Transaction Statuses |
| errorCode | String | Error code for an unsuccessful request. See the error code reference |
| errorDescription | String | Error description for an unsuccessful request |
| terminalName | String | Merchant terminal name |
| amount | Number | Payment amount. Up to 2 digits after the decimal point are allowed for kopecks or cents. Example: 1188.00 |
| currency | String | Payment currency (RUB, USD, EUR) |
| orderId | String | Unique order identifier in the merchant system |
| orderDescription | String | Order description |
| commission | Number | Transaction fee |
| paymentTime | Date | Date and time of transaction payment in UTC, meaning the transition to Paid or Declined |
| String | Payer email, if it was provided on the form | |
| paymentLinkId | UUID | Payment link identifier in the WATA system |
| payerData | Object | Contains payer information |
| payerData.payerId | String | Masked payer phone number for an SBP transaction |
Security and Signature Verification
To guarantee that the notification was sent by WATA, each message contains an 'X-Signature' header featuring a digital RSA signature. This header can be used to verify that the notification was sent by WATA, ensuring the integrity and authenticity of the payload.
X-Signature: <signature>
Verification Algorithm
- Get the public key. Use the current key (PKCS1 format), available at: api.wata.pro/api/h2h/public-key.
- Prepare the data. Use raw JSON, the unprocessed request body received in the webhook, as the message.
- Verify. Use the
SHA512withRSAalgorithm to check that the signature from theX-Signatureheader matches the message body.
Use the raw request body exactly as received. If JSON was formatted, rebuilt, or changed by middleware, signature verification may fail.
PHP signature verification example
<?php
class SignatureVerificationService {
public static function verify($rawWebhookJson, $signature, $publicKey) {
$publicSignature = openssl_get_publickey($publicKey);
$signatureBytes = base64_decode($signature);
$result = openssl_verify($rawWebhookJson, $signatureBytes, $publicSignature, OPENSSL_ALGO_SHA512);
openssl_free_key($publicSignature);
return $result === 1;
}
}
Java signature verification example
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
public class SignatureVerificationService {
public static boolean verify(String rawWebhookJson, String signature, PublicKey publicKey) throws Exception {
Signature publicSignature = Signature.getInstance("SHA512withRSA");
publicSignature.initVerify(publicKey);
publicSignature.update(rawWebhookJson.getBytes(UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
return publicSignature.verify(signatureBytes);
}
}
Getting the Public Key
The public key for signature verification can be received through the API:
GET https://api.wata.pro/api/h2h/public-key
Content-Type: application/json
Response example
{
"value": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}
Webhook Notification Example
Content-Type: application/json
X-Signature: <signature>
{
"transactionType": "CardCrypto",
"kind": "Payment",
"id": "3a1cf611-abc6-8d30-c4cd-521c9f6eeeb0",
"transactionId": "3a16a4f0-27b0-09d1-16da-ba8d5c63eae3",
"transactionStatus": "Paid",
"terminalPublicId": "3b16a2f1-dead-4e5d-abff-90865d1e13b1",
"errorCode": null,
"errorDescription": null,
"terminalName": "string",
"amount": 1188.00,
"currency": "RUB",
"orderId": "string",
"orderDescription": "string",
"paymentTime": "2024-12-04T17:41:44.434598Z",
"commission": 10,
"email": null,
"paymentLinkId": null
}
Processing Recommendations
- Return
200 OKonly after successful notification processing. - Process webhook notifications idempotently: repeated delivery of the same event must not break business logic.
- Store
transactionIdandtransactionStatusfor later reconciliation. - Verify the
X-Signatureheader before executing any business logic. - Do not use polling as the main way to receive statuses. Use webhook notifications for status updates.