Skip to content

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.

POST CallbackHMAC-SHA256x-signature

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

ItemDescription
HTTP MethodPOST
Content-Typeapplication/json;charset=UTF-8
Signature AlgorithmHMAC-SHA256

Request Headers

Header KeyTypeRequiredDescriptionFormat / Constraint
Content-TypestringYesFixed as application/json;charset=UTF-8Fixed value
x-timestampstringYesUnix timestampSeconds-level timestamp string
x-signaturestringYesHex signature generated with webhook_secret64-character hex string

Signature Verification Rules

Signing plain text:

text
x-timestamp + "." + rawBody

Signing algorithm:

text
HMAC-SHA256(webhook_secret, x-timestamp + "." + rawBody) -> Hex

content Definition

text
content = x-timestamp + "." + rawBody

Where:

  • x-timestamp is the timestamp in the request header
  • rawBody is the original HTTP request body string
  • signature is the x-signature value from the request header

Verification Notes

  • rawBody must be the original HTTP request body. Do not reformat the JSON before signature calculation
  • Validate the time window of x-timestamp to protect against replay attacks
  • Use the plain-text webhook_secret provided 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

FieldTypeDescriptionFormat / Constraint
requestIdstringNotification request ID-
eventTypestringEvent type. The value depends on the business moduleDefined by the specific module
createdAtstringNotification creation timeTime string in YYYY-MM-DD HH:mm:ss format
versionstringPayload versionDefined by the specific module
dataobjectBusiness payloadDefined 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

ScenarioRequirement
Signature verified and business acceptedReturn 2xx
Business processing failedIt is recommended to persist the notification first and still return 2xx, then perform internal compensation later
Signature verification failed or request is invalidReturn non-2xx, and the platform will treat it as a failed notification

Idempotency Recommendations

  • Use requestId as 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