Guide
Webhook
Onerway supports sending asynchronous notifications to the merchant system through webhook. Merchants should provide a reachable callback URL and complete signature verification, idempotent control, and business processing on the server side.
Integration Requirements
- Build a notification receiving endpoint based on your business needs
- Make sure the callback URL can be accessed by Onerway
- Make sure the endpoint can verify signatures
- Make sure the endpoint supports idempotent processing
Important
Please provide a stable public callback URL. If the callback address changes, contact the Onerway technical team to update the configuration in time.
Delivery Method
| Item | Description |
|---|---|
| HTTP Method | POST |
Content-Type | application/json;charset=UTF-8 |
| Signature Algorithm | HMAC-SHA256 |
Request Headers
| Header Key | Type | Required | Description | Format / Constraint |
|---|---|---|---|---|
Content-Type | string | Yes | Fixed as application/json;charset=UTF-8 | Fixed value |
x-timestamp | string | Yes | Unix timestamp | Seconds-level timestamp string |
x-signature | string | Yes | Hex signature generated with webhook_secret | 64-character hex string |
Signature Verification Rules
Signing plain text:
text
x-timestamp + "." + rawBodySigning algorithm:
text
HMAC-SHA256(webhook_secret, x-timestamp + "." + rawBody) -> Hexcontent Definition
text
content = x-timestamp + "." + rawBodyWhere:
x-timestampis the timestamp in the request headerrawBodyis the original HTTP request body stringsignatureis thex-signaturevalue from the request header
Verification Notes
rawBodymust be the original HTTP request body. Do not reformat the JSON before signature calculation- Validate the time window of
x-timestampto protect against replay attacks - Use the plain-text
webhook_secretprovided by Onerway directly for signature verification - Process business logic only after the signature is verified successfully
Signature Verification Examples
Java
java
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HmacSHA256Util {
private static final String HMAC_SHA256 = "HmacSHA256";
public static String signHmacSHA256(String webhookSecret, String content) throws Exception {
Mac mac = Mac.getInstance(HMAC_SHA256);
SecretKeySpec keySpec = new SecretKeySpec(
webhookSecret.getBytes(), HMAC_SHA256);
mac.init(keySpec);
byte[] result = mac.doFinal(content.getBytes());
return HexUtil.encodeHexStr(result);
}
public static boolean verifyHmacSHA256(String webhookSecret, String content, String signature)
throws Exception {
String expected = signHmacSHA256(webhookSecret, content);
return StrUtil.equalsAnyIgnoreCase(expected, signature);
}
}Python
python
import hashlib
import hmac
HMAC_SHA256 = hashlib.sha256
def sign_hmac_sha256(webhook_secret: str, content: str) -> str:
return hmac.new(
webhook_secret.encode("utf-8"),
content.encode("utf-8"),
HMAC_SHA256
).hexdigest()
def verify_hmac_sha256(webhook_secret: str, content: str, signature: str) -> bool:
expected = sign_hmac_sha256(webhook_secret, content)
return hmac.compare_digest(expected.lower(), signature.lower())PHP
php
<?php
class HmacSHA256Util
{
private const HMAC_SHA256 = 'sha256';
public static function signHmacSHA256(string $webhookSecret, string $content): string
{
return hash_hmac(self::HMAC_SHA256, $content, $webhookSecret);
}
public static function verifyHmacSHA256(string $webhookSecret, string $content, string $signature): bool
{
$expected = self::signHmacSHA256($webhookSecret, $content);
return hash_equals(strtolower($expected), strtolower($signature));
}
}Standard Request Structure
| Field | Type | Description | Format / Constraint |
|---|---|---|---|
requestId | string | Notification request ID | - |
eventType | string | Event type. The value depends on the business module | Defined by the specific module |
createdAt | string | Notification creation time | Time string in YYYY-MM-DD HH:mm:ss format |
version | string | Payload version | Defined by the specific module |
data | object | Business payload | Defined by the module-specific document |
For event types, field definitions, and sample payloads of a specific business module, refer to the corresponding module document.
Merchant Response Requirements
| Scenario | Requirement |
|---|---|
| Signature verified and business accepted | Return 2xx |
| Business processing failed | It is recommended to persist the notification first and still return 2xx, then perform internal compensation later |
| Signature verification failed or request is invalid | Return non-2xx, and the platform will treat it as a failed notification |
Idempotency Recommendations
- Use
requestIdas the notification-level idempotency identifier - Store the original notification first, then process business updates asynchronously
- If your business module requires a finer-grained idempotency key, follow the corresponding module documentation