API Reference
WEBHOOK description
Understand Issuing webhook headers, payload envelope, merchant acknowledgement, and HMAC-SHA256 signature verification.
Use this page before implementing Issuing webhook callbacks. Onerway pushes card operation, card transaction, and 3DS events to the callback URL configured for the merchant.
Request headers
| Header | Type | Required | Description |
|---|---|---|---|
Content-Type | String | Yes | Fixed to application/json;charset=UTF-8. |
x-timestamp | String | Yes | Unix timestamp in seconds. |
x-signature | String | Yes | Generate the HMAC-SHA256 signature from webhook_secret and x-timestamp + "." + raw_body. See Signature and verification. |
Payload structure
| Field | Type | Required | Description |
|---|---|---|---|
request_id | String | Yes | Unique event identifier for deduplication. |
event_type | String | Yes | Event type. Issuing currently sends issuing.cardOperateEvent, issuing.cardTransactionEvent, and issuing.card3dsEvent. |
created_at | String | Yes | Event creation time in ISO 8601 format. |
version | String | No | Webhook API version selected when the subscription is created. Defaults to 1.0 when not specified. |
data | Object | Yes | Business data. The structure depends on event_type. |
Merchant response requirements
The merchant callback must return a JSON response. Onerway uses this response to decide whether the push was received successfully.
| Field | Type | Description |
|---|---|---|
respCode | String | Return 20000 to confirm receipt. |
respMsg | String | Response message. |
If the callback response code is not 20000, Onerway retries every 15 seconds, up to 10 times.
Signature and verification
Onerway signs asynchronous notifications such as card operation, transaction, and 3DS events. Verify the signature before processing the event.
Build the signed content as:
x-timestamp + "." + raw_body
Generate the expected signature with HMAC-SHA256 and compare it with x-signature case-insensitively.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.HexFormat;
public final class HmacSHA256Util {
private HmacSHA256Util() {
}
public static String signHmacSHA256(String webhook_secret, String content) throws Exception {
byte[] key = Base64.getDecoder().decode(webhook_secret);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
return HexFormat.of().formatHex(mac.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}
public static boolean verifyHmacSHA256(String webhook_secret, String content, String signed) throws Exception {
String expected = signHmacSHA256(webhook_secret, content);
return MessageDigest.isEqual(
expected.toLowerCase().getBytes(StandardCharsets.UTF_8),
signed.toLowerCase().getBytes(StandardCharsets.UTF_8)
);
}
}
import base64
import hashlib
import hmac
def sign_hmac_sha256(webhook_secret: str, content: str) -> str:
key = base64.b64decode(webhook_secret)
return hmac.new(key, content.encode("utf-8"), hashlib.sha256).hexdigest()
def verify_hmac_sha256(webhook_secret: str, content: str, signed: str) -> bool:
expected = sign_hmac_sha256(webhook_secret, content)
return hmac.compare_digest(expected.lower(), signed.lower())
<?php
final class HmacSHA256Util
{
public static function signHmacSHA256(string $webhook_secret, string $content): string
{
$key = base64_decode($webhook_secret, true);
if ($key === false) {
throw new InvalidArgumentException('Invalid webhook secret.');
}
return hash_hmac('sha256', $content, $key);
}
public static function verifyHmacSHA256(string $webhook_secret, string $content, string $signed): bool
{
$expected = self::signHmacSHA256($webhook_secret, $content);
return hash_equals(strtolower($expected), strtolower($signed));
}
}