Webhooks Overview
This section defines the webhook protocol shared across Acquiring, Payout, Batch Payout, and verification flows.
Use this page for:
- common headers
- signature verification
- retry and delivery rules
- verification-event response contracts
Use the product-specific pages for business semantics:
Unified Webhook Rules
All standard webhooks follow the same protocol.
Headers
Every webhook request includes:
Content-Type: application/jsonX-Beyounger-Webhook-IdX-Beyounger-EventX-Beyounger-Delivery-IdX-Beyounger-Signature
Signature Verification
All webhook events use the same signature rule:
X-Beyounger-Signature = hex(HMAC_SHA256(raw_body, webhook_secret))
Important notes:
raw_bodymust be the original HTTP request body bytes.- Do not re-serialize JSON before signature verification.
- Hex comparison is case-insensitive.
- The current version does not include timestamp or nonce in the signature, so the receiver must implement idempotency and replay protection.
- Node.js
- Go
- PHP
- Java
const crypto = require('crypto');
function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return expected.trim().toLowerCase() === String(signature).trim().toLowerCase();
}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strings"
)
func VerifySignature(rawBody []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal(
[]byte(strings.ToLower(strings.TrimSpace(expected))),
[]byte(strings.ToLower(strings.TrimSpace(signature))),
)
}
<?php
function verifySignature(string $rawBody, string $signature, string $secret): bool
{
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals(
strtolower(trim($expected)),
strtolower(trim($signature))
);
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class WebhookVerifier {
public static boolean verifySignature(String rawBody, String signature, String secret) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] digest = mac.doFinal(rawBody.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString().trim().equalsIgnoreCase(signature.trim());
}
}
Delivery Rule
A webhook delivery is considered successful when the receiver returns any 2xx HTTP status code.
Idempotency Recommendation
The receiver should implement idempotency using:
event_idas the primary event-level idempotency key- business identifiers such as
order_id,payment_id,payout_id, orbatch_id - optionally
X-Beyounger-Delivery-Idfor troubleshooting and retry tracking
Standard Webhook Retry Policy
Standard Webhook Deliveries
Standard merchant webhook deliveries use the following retry policy by default:
- worker count:
4 - max attempts:
10 - max retry window:
72 hours - request timeout per attempt:
10 seconds - retry delays in minutes:
1, 2, 5, 10, 20, 40, 80, 240, 1020
Interpretation:
- The first delivery attempt is sent immediately.
- If it fails, retries are scheduled using the delay sequence above.
- Delivery is considered successful only when the receiver returns any
2xxHTTP status code. - Any non-
2xxresponse or network/timeout failure is treated as a failed attempt. - Duplicate deliveries are possible due to retries, so consumers must be idempotent.
Delivery Status Semantics
Webhook delivery records use:
pending: waiting for next retry or currently retryingdelivered: receiver returned2xxfailed: retry limit exhausted or final failure reached
Special Verification Events
The following events are used for verification and auto-review workflows. They are not a separate webhook protocol:
payout.verify.requestbatch.verify.request
Important Rule
Special verification events use the same:
- headers
- signature verification rule
- delivery behavior
- retry behavior
as all other webhook events.
They differ only in payload purpose and additional fields.
Verification Delivery Behavior
These verification events are single-shot verification calls in the current implementation.
Current behavior:
- delivery attempts:
1 - no retry schedule
- timeout is controlled separately per verification flow
- if the receiver does not return an accepted verification result, the verification is treated as rejected
Timeout
Current defaults:
payout.verify.request:8 secondsbatch.verify.request:8 seconds
Response Contract For Verification Events
Accepted Verification Signals
A verification response is treated as verified only when:
- HTTP status is
2xx - and the response body contains:
"verified": true
If this signal is not present, the verification result is treated as not verified.
Accepted Response Examples
{
"verified": true
}
Rejected / Not Verified Examples
{
"verified": false
}
{
"ok": true
}
Any non-2xx response is also treated as verification failure.
Query Fallback
Webhook delivery is the real-time update path. Query APIs remain the reconciliation fallback:
| Scenario | Query API |
|---|---|
| Payin order | GET /payment/acquiring/orders/{order_id} |
| Payin payment investigation | GET /payment/acquiring/orders/{order_id}/payments |
| Payout | GET /payment/payouts/{payout_id} |