Signature Guide
To ensure secure data transmission, we verify request parameters using digital signatures.
Signature Algorithm
We use HMAC-SHA256 signing algorithm. Requests must include the sign_type parameter:
{
"platform_id": "PF0002",
"sign_type": "HMAC-SHA256",
"sign": "..."
}
Test Merchant Information
All examples below use the same test merchant information:
| Field | Value |
|---|---|
| Merchant ID (platform_id) | PF0002 |
| Platform Key (platform_key) | ThisIsYourSecretKey123 |
Signing Rules
- Sort parameters: Sort all parameters by key in ASCII ascending order
- Exclude parameters:
sign,sign_type, and parameters with empty values - Concatenate string: Join as
key=valuepairs connected by&(do NOT URL-encode values, use raw values) - HMAC-SHA256 sign: Use the key to sign the string with HMAC-SHA256
- Convert to lowercase: Convert the result to a 64-character lowercase hexadecimal string
Array Parameter Handling
When request parameters contain arrays (e.g., last_numbers), the array must be converted to a JSON string format for signing.
Example: When last_numbers is ["12345", "67890"]:
last_numbers=["12345","67890"]
When converting arrays to JSON strings, there must be no extra spaces. Elements should only be separated by commas.
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¬ify_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, resulting in:
e8a5c3f2d1b4a6e9c7f0d2b5a8e1c4f7d0b3a6e9c2f5d8b1a4e7c0f3d6b9a2e5
Code Examples
cURL
# 1. Concatenate sorted parameters (excluding sign and sign_type)
PARAM_STR="amount=50000¬ify_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);
$pairs = [];
foreach ($filtered as $k => $v) {
$pairs[] = $k . '=' . $v;
}
$paramStr = implode('&', $pairs);
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) {
// Filter and sort
const filtered = Object.entries(params)
.filter(([k, v]) => v && k !== 'sign' && k !== 'sign_type')
.sort(([a], [b]) => a.localeCompare(b));
// Concatenate
const paramStr = filtered.map(([k, v]) => `${k}=${v}`).join('&');
// HMAC-SHA256
return crypto
.createHmac('sha256', platformKey)
.update(paramStr)
.digest('hex')
.toLowerCase();
}
Verify Callback Signature
When receiving callback notifications, please verify the signature to ensure data has not been tampered with:
- Recalculate the signature using HMAC-SHA256 algorithm
- Compare the calculated result with the
signvalue
Always verify callback signatures to prevent forged callback attacks.
Common Issues
Signature Error (error_code: 0004)
Common causes:
- URL-encoded parameter values - The signing string uses raw values, do NOT URL-encode them (e.g.,
notify_urlshould remainhttps://...as-is, nothttps%3A%2F%2F...) - Inconsistent parameter values - Ensure the parameter values used for signing match the actual request exactly
- Not excluding sign_type - Both
signandsign_typeshould be excluded from signature calculation - Encoding issues - Ensure UTF-8 encoding is used