API Documentation

Signatures

Requests, responses and callbacks all carry a signature sign. The two APIs sign differently; do not mix them up.

Payout API std1: HMAC-SHA256

  1. Take every parameter (every field when verifying a response or callback) except sign.
  2. Leave out empty strings (an empty string means “not sent”); 0 and a space are signed.
  3. Sort the names by UTF-8 bytes, ascending: case-sensitive, uppercase before lowercase, and a numeric name 20 before 3.
  4. Join name=value pairs with &. Use the raw values: no URL encoding, no trimming, no HTML entities.
  5. Compute HMAC-SHA256 with your API key. The key is the text shown under “Security” in the merchant portal itself (a 64-character hex string for new merchants), used as its UTF-8 bytes; never hex-decode it into bytes.
  6. Write the result as 64 lowercase hex characters, keeping leading zeros. We compare signatures case-insensitively.

Requests, responses and callbacks use the same rule and the same key. Extra parameters you add that are not documented must be signed too (we ignore them after verifying).

Examples

Example key (fake)

0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

Use the key text's UTF-8 bytes as the HMAC key; never hex-decode it. hexDecodedKeySign is the WRONG result you get by decoding.

Parameters

[["mchId","DEMO0001"],["mchOrderNo","ORDER-20260917-0001"],["amount","10000"]]

String to sign

amount=10000&mchId=DEMO0001&mchOrderNo=ORDER-20260917-0001

sign

9baf546e1f95057438cab0379a8abe79f65110f4edb21ae3c1131b7b87694509

Wrong way (hex-decoding the key into 32 bytes first) gives

b21db2c6def6135e35d9fca688449a9bb1a14b226be5c21adff1b267a6c3a1bb

Empty strings and nulls are left out; "0" and a single space are signed; sign itself is left out

Parameters

[["a","1"],["b",""],["c",null],["d"," "],["e","0"],["sign","ffff"]]

String to sign

a=1&d= &e=0

sign

e73689ddfba7cb0e1fee38f7450ced153080e6e9ec2e29b986a6f5259dc6ef09

Names sort by UTF-8 bytes, case-sensitive: digits < upper case < underscore < lower case; numeric names compare as bytes too

Parameters

[["b","1"],["a","2"],["B","3"],["_","4"],["0","5"],["20","6"],["3","7"],["aB","8"],["a_b","9"],["a1","10"],["Z","11"],["ab","12"]]

String to sign

0=5&20=6&3=7&B=3&Z=11&_=4&a=2&a1=10&aB=8&a_b=9&ab=12&b=1

sign

cc98598fb90d59b6603d65a813ad54959ce38cdbe6e1fb2ed1c0b7baead5cf3d

The signature starts with 00: hex output must keep leading zeros (64 chars)

Parameters

[["mchId","DEMO0001"],["nonce","112"]]

String to sign

mchId=DEMO0001&nonce=112

sign

00e9a48bd200689f0a7e7a91e650f022f7e54d7f6816d869ddce36f19c1e5c1a

Verifying responses and callbacks

Collection API compat1 (legacy): MD5

Exactly as in the old system:

  1. Take every parameter received except sign; leave out null values and values that are exactly the empty string. A value that is only spaces is signed.
  2. A value that is a JSON object (only payParams in responses) is written as compact JSON (no spaces) with its first-level keys sorted by character code, and that text is the value.
  3. Build name=value pairs and sort the whole name=value strings case-insensitively. Note this is not a sort by name alone: param10=… comes before param1=… because 0 sorts before =.
  4. Join with & and append &key=your merchant private key.
  5. MD5 the UTF-8 bytes and write 32 uppercase hex characters. We compare case-insensitively.
  6. Every non-empty parameter you send is signed, including undocumented ones.

Examples

Create order request:

Example key (fake):demo-key-NOT-REAL-do-not-use-0001

Parameters

mchId=20000001
productId=8001
mchOrderNo=ORDER20260918001
amount=10000
notifyUrl=https://merchant.example.com/notify
subject=goods
body=goods
param1=shop-7
param2=张三
reqTime=20260918120000

String to sign

amount=10000&body=goods&mchId=20000001&mchOrderNo=ORDER20260918001&notifyUrl=https://merchant.example.com/notify&param1=shop-7&param2=张三&productId=8001&reqTime=20260918120000&subject=goods&key=demo-key-NOT-REAL-do-not-use-0001

sign

86032564C62F16F220F9DBAD7D88C690

Request body

mchId=20000001&productId=8001&mchOrderNo=ORDER20260918001&amount=10000&notifyUrl=https%3A%2F%2Fmerchant.example.com%2Fnotify&subject=goods&body=goods&param1=shop-7&param2=%E5%BC%A0%E4%B8%89&reqTime=20260918120000&sign=86032564C62F16F220F9DBAD7D88C690

Callback (sign is in the body; empty values are not signed):

Example key (fake):demo-key-NOT-REAL-do-not-use-0001

Your notifyUrl

https://merchant.example.com/notify

Sent to

https://merchant.example.com/notify

Callback body

income=10000&payOrderId=ORDER20260918001&amount=10000&mchId=20000001&productId=8001&mchOrderNo=ORDER20260918001&paySuccTime=1789704005000&sign=9447B44CF966CB47A625CADF63C539A8&channelOrderNo=UP20260918000123&backType=2&param1=shop-7&param2=%E5%BC%A0%E4%B8%89&channelAttach=&status=2

String to sign

amount=10000&backType=2&channelOrderNo=UP20260918000123&income=10000&mchId=20000001&mchOrderNo=ORDER20260918001&param1=shop-7&param2=张三&payOrderId=ORDER20260918001&paySuccTime=1789704005000&productId=8001&status=2&key=demo-key-NOT-REAL-do-not-use-0001