The system pushes card operation and transaction events in real-time to the merchant's configured callback URL.
| Header | Type | Required | Description |
|---|---|---|---|
| Content-Type | String | Yes | application/json;charset=UTF-8 |
| x-timestamp | String | Yes | Unix timestamp (seconds) |
| x-signature | String | Yes | HMAC-SHA256(webhook_secret, x-timestamp + "." + raw_body) → Hex encoded, where webhook_secret is obtained from the merchant portal, raw_body is the original payload structure content, refer to the table below |
| Field | Type | Required | Description |
|---|---|---|---|
request_id | String | Yes | Unique event identifier for deduplication, 64-bit |
event_type | String | Yes | Event type: issuing.cardOperateEvent card operation event change / issuing.cardTransactionEvent card transaction event change |
created_at | String | Yes | Event creation time (ISO 8601 format) |
version | String | No | API version (selected when creating the Webhook subscription, default 1.0) |
data | Object | Yes | Business data, structure varies by event_type |
The merchant callback must return the following JSON structure. The system uses this to determine if the push was successful. If the response code is not 20000, the system will retry every 15 seconds, up to 10 times.
| Field | Type | Description |
|---|---|---|
respCode | String | Response code, return 20000 to confirm receipt |
respMsg | String | Response message |
event_type = issuing.cardOperateEvent
data
| Field | Type | Description |
|---|---|---|
operateRecordId | number | Operation record ID, required |
cardId | number | Card ID, required |
clientRequestId | String | Idempotent request ID, required |
type | String | Operation type: CREATE / FREEZE / UNFREEZE / DEPOSIT |
status | String | Operation status, required (P Processing / S Success / F Failed) |
remark | String | Remark |
amount | number | Amount |
currency | String | Currency |
feeList[] | Array | Fee detail list |
feeList[].feeType | String | Fee type: CREATE card creation fee / DEPOSIT deposit fee |
feeList[].feeAmount | number | Fee amount |
feeList[].feeCurrency | String | Fee currency |
{
"operateRecordId": 200001,
"cardId": 100001,
"clientRequestId": "REQ_20260101_001",
"type": "DEPOSIT",
"status": "S",
"remark": null,
"amount": 100.00,
"currency": "USD",
"feeList": [
{ "feeType": "DEPOSIT", "feeAmount": 1.00, "feeCurrency": "USD" }
]
}
Pushed when transactions (authorization, clearing, refund, etc.) occur.
event_type = issuing.cardTransactionEvent
data
| Field | Type | Description |
|---|---|---|
txnType | String | Transaction type: AUTHORIZATION / REVERSAL / CLEARING / REFUND / VERIFICATION |
txnStatus | String | Transaction status: S Success / F Failed / P Processing |
txnOrderNo | String | Transaction order number |
originTxnOrderNo | String | Original transaction order number |
originTxnOrderNoForRefund | String | Original payment order number for refund |
cardId | String | Card ID |
authTime | number | Authorization time (timestamp, milliseconds) |
authorizationCode | String | Authorization code |
authMessageDesc | String | Authorization description |
transactionAmount | number | Transaction amount |
transactionCurrency | String | Transaction currency |
transactionTime | number | Transaction time (timestamp, milliseconds) |
cardAmount | number | Card amount |
cardCurrency | String | Card currency |
settleAmount | number | Settlement amount |
settleCurrency | String | Settlement currency |
settleTime | number | Settlement time (timestamp, milliseconds) |
transactionFee | number | Transaction fee |
transactionFeeCurrency | String | Transaction fee currency |
crossBorderFee | number | Cross-border fee |
crossBorderFeeCurrency | String | Cross-border fee currency |
exchangeFee | number | Exchange fee |
exchangeFeeCurrency | String | Exchange fee currency |
merchantId | String | Merchant ID |
merchantName | String | Merchant name |
merchantCountry | String | Merchant country |
merchantCountryCode | String | Merchant country code |
merchantStateProvince | String | Merchant state/province |
merchantCity | String | Merchant city |
merchantPostalCode | String | Merchant postal code |
merchantMccCode | String | Merchant MCC code |
{
"txnType": "AUTHORIZATION",
"txnStatus": "S",
"txnOrderNo": "TXN20260101001",
"originTxnOrderNo": null,
"originTxnOrderNoForRefund": null,
"cardId": "411111******1111",
"authTime": 1735689600000,
"authorizationCode": "AUTH001",
"authMessageDesc": "Approved",
"transactionAmount": 50.00,
"transactionCurrency": "USD",
"transactionTime": 1735689600000,
"cardAmount": 50.00,
"cardCurrency": "USD",
"settleAmount": 50.00,
"settleCurrency": "USD",
"settleTime": 1735776000000,
"transactionFee": 0.50,
"transactionFeeCurrency": "USD",
"crossBorderFee": null,
"crossBorderFeeCurrency": null,
"exchangeFee": null,
"exchangeFeeCurrency": null,
"merchantId": "MCH001",
"merchantName": "Amazon",
"merchantCountry": "United States",
"merchantCountryCode": "US",
"merchantStateProvince": "WA",
"merchantCity": "Seattle",
"merchantPostalCode": "98101",
"merchantMccCode": "5411"
}
Signing is used to verify the integrity and authenticity of messages, ensuring that requests or notifications have not been tampered with during transmission, while supporting server-side verification and auditing.
When Onerway pushes asynchronous notifications (such as transaction/card events) to merchants, the notification payload is signed. HMAC-SHA256 signing algorithm is supported. Reference code is as follows.
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class HmacSHA256Util {
// Signing
public static String signHmacSHA256(String plainCert, String content)
throws NoSuchAlgorithmException, InvalidKeyException {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(
Base64.decodeBase64(plainCert), "HmacSHA256");
mac.init(keySpec);
byte[] result = mac.doFinal(content.getBytes());
return HexUtil.encodeHexStr(result);
} catch (Exception e) {
throw e;
}
}
// Verification
private static boolean verifyHmacSHA256(String plainCert, String sign, String signed)
throws NoSuchAlgorithmException, InvalidKeyException {
String hmacSHA256Str = signHmacSHA256(plainCert, sign);
return StrUtil.equalsAnyIgnoreCase(hmacSHA256Str, signed);
}
}
import hmac
import hashlib
import base64
def sign_hmac_sha256(plain_cert: str, content: str) -> str:
"""Sign"""
key = base64.b64decode(plain_cert)
signature = hmac.new(key, content.encode("utf-8"), hashlib.sha256).hexdigest()
return signature
def verify_hmac_sha256(plain_cert: str, content: str, signed: str) -> bool:
"""Verify"""
expected = sign_hmac_sha256(plain_cert, content)
return hmac.compare_digest(expected.lower(), signed.lower())
<?php
class HmacSHA256Util
{
// Signing
public static function signHmacSHA256(string $plainCert, string $content): string
{
$key = base64_decode($plainCert);
return hash_hmac('sha256', $content, $key);
}
// Verification
public static function verifyHmacSHA256(string $plainCert, string $content, string $signed): bool
{
$expected = self::signHmacSHA256($plainCert, $content);
return hash_equals(strtolower($expected), strtolower($signed));
}
}