Skip to main content

Signature Specification

MD5 Signature Algorithm Deprecation

MD5 will be deprecated on 2026/03/31. Please migrate to HMAC-SHA256 before this date. Requests using MD5 will return errors after deprecation.

To ensure secure data transmission, we verify request parameters using digital signatures.

Signature Algorithms

We support the following signature algorithms:

Algorithmsign_type ValueRecommendationDescription
HMAC-SHA256HMAC-SHA256⭐ RecommendedMore secure signing algorithm
MD5MD5 or omitLegacyFor backward compatibility
For New Merchants

We strongly recommend using HMAC-SHA256. MD5 is considered insecure by modern cryptographic standards.

How to Choose

Add the sign_type parameter to your request:

{
"platform_id": "PF0002",
"sign_type": "HMAC-SHA256",
"sign": "..."
}

If sign_type is omitted, MD5 is used by default for backward compatibility.


Test Merchant Information

FieldValue
Merchant ID (platform_id)PF0002
Platform Key (platform_key)ThisIsYourSecretKey123

Signing Rules

  1. Sort parameters by key in ASCII ascending order
  2. Exclude: sign, sign_type, and empty values
  3. Concatenate as key=value pairs joined by &
  4. Sign using HMAC-SHA256 with platform_key as the secret
  5. Convert result to lowercase 64-character hex string
Difference from MD5

HMAC-SHA256 uses the key directly for signing. Do NOT append the key to the string.

Deposit Example

Request Parameters:

{
"platform_id": "PF0002",
"service_id": "SVC0001",
"payment_cl_id": "DEVPM00014581",
"amount": "50000",
"notify_url": "https://your-domain.com/callback",
"request_time": "1595504136",
"sign_type": "HMAC-SHA256"
}

Step 1: Sort and concatenate (excluding sign, sign_type)

amount=50000&notify_url=https://your-domain.com/callback&payment_cl_id=DEVPM00014581&platform_id=PF0002&request_time=1595504136&service_id=SVC0001

Step 2: HMAC-SHA256 Sign

Sign using key ThisIsYourSecretKey123.

Code Examples

cURL
# 1. Concatenate sorted parameters (excluding sign and sign_type)
PARAM_STR="amount=50000&notify_url=https://your-domain.com/callback&payment_cl_id=DEVPM00014581&platform_id=PF0002&request_time=1595504136&service_id=SVC0001"
PLATFORM_KEY="ThisIsYourSecretKey123"

# 2. HMAC-SHA256 sign
SIGN=$(echo -n "$PARAM_STR" | openssl dgst -sha256 -hmac "$PLATFORM_KEY" | awk '{print $2}')
echo $SIGN
Python
import hmac
import hashlib

def generate_sign_hmac_sha256(params: dict, platform_key: str) -> str:
filtered = {k: v for k, v in params.items()
if v and k not in ['sign', 'sign_type']}
sorted_keys = sorted(filtered.keys())
param_str = '&'.join([f'{k}={filtered[k]}' for k in sorted_keys])

return hmac.new(
platform_key.encode('utf-8'),
param_str.encode('utf-8'),
hashlib.sha256
).hexdigest().lower()
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

public class SignatureUtil {
public static String generateHmacSha256(Map<String, String> params, String key) {
TreeMap<String, String> filtered = new TreeMap<>();
for (Map.Entry<String, String> e : params.entrySet()) {
if (e.getValue() != null && !e.getValue().isEmpty()
&& !e.getKey().equals("sign") && !e.getKey().equals("sign_type")) {
filtered.put(e.getKey(), e.getValue());
}
}

StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> e : filtered.entrySet()) {
if (sb.length() > 0) sb.append("&");
sb.append(e.getKey()).append("=").append(e.getValue());
}

try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"));
byte[] hash = mac.doFinal(sb.toString().getBytes("UTF-8"));
StringBuilder hex = new StringBuilder();
for (byte b : hash) hex.append(String.format("%02x", b));
return hex.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
PHP
<?php
function generateSignHmacSha256(array $params, string $platformKey): string {
$filtered = array_filter($params, function($v, $k) {
return !empty($v) && !in_array($k, ['sign', 'sign_type']);
}, ARRAY_FILTER_USE_BOTH);

ksort($filtered);
$paramStr = http_build_query($filtered, '', '&');

return strtolower(hash_hmac('sha256', $paramStr, $platformKey));
}
?>
Go
package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"sort"
"strings"
)

func GenerateSignHmacSha256(params map[string]string, platformKey string) string {
var keys []string
for k, v := range params {
if v != "" && k != "sign" && k != "sign_type" {
keys = append(keys, k)
}
}
sort.Strings(keys)

var pairs []string
for _, k := range keys {
pairs = append(pairs, k+"="+params[k])
}
paramStr := strings.Join(pairs, "&")

h := hmac.New(sha256.New, []byte(platformKey))
h.Write([]byte(paramStr))
return strings.ToLower(hex.EncodeToString(h.Sum(nil)))
}
JavaScript
const crypto = require('crypto');

function generateSignHmacSha256(params, platformKey) {
const filtered = Object.entries(params)
.filter(([k, v]) => v && k !== 'sign' && k !== 'sign_type')
.sort(([a], [b]) => a.localeCompare(b));

const paramStr = filtered.map(([k, v]) => `${k}=${v}`).join('&');

return crypto
.createHmac('sha256', platformKey)
.update(paramStr)
.digest('hex')
.toLowerCase();
}

MD5 Signature (Legacy) - Click to expand
Not Recommended

MD5 is only kept for backward compatibility. New merchants should use HMAC-SHA256.

Signing Rules

  1. Sort parameters by key in ASCII order, exclude sign, sign_type, empty values
  2. Concatenate as key=value&key=value
  3. Append key: &{platform_key} (NOT &key={platform_key})
  4. MD5 hash, convert to lowercase 32-character string

Example

Concatenated string with key:

amount=50000&notify_url=https://your-domain.com/callback&payment_cl_id=DEVPM00014581&platform_id=PF0002&request_time=1595504136&service_id=SVC0001&ThisIsYourSecretKey123

MD5 Result: 49be5fa304b5f536c6e2ea89435e211a


Verify Callback Signature

When receiving callback notifications, verify the signature using the same algorithm.

If the callback contains sign_type, use that algorithm; otherwise use MD5.

Security

Always verify callback signatures to prevent forged callback attacks.


Common Issues

Signature Error (error_code: 0004)

Common causes:

  1. Wrong MD5 key format - Should be &{platform_key}, NOT &key={platform_key}
  2. Not excluding sign_type - Both sign and sign_type should be excluded
  3. Encoding issues - Ensure UTF-8 encoding