Skip to main content

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:

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

Signing Rules

  1. Sort parameters: Sort all parameters by key in ASCII ascending order
  2. Exclude parameters: sign, sign_type, and parameters with empty values
  3. Concatenate string: Join as key=value pairs connected by & (do NOT URL-encode values, use raw values)
  4. HMAC-SHA256 sign: Use the key to sign the string with HMAC-SHA256
  5. 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"]
Note

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&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, resulting in:

e8a5c3f2d1b4a6e9c7f0d2b5a8e1c4f7d0b3a6e9c2f5d8b1a4e7c0f3d6b9a2e5

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);
$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:

  1. Recalculate the signature using HMAC-SHA256 algorithm
  2. Compare the calculated result with the sign value
Security Advice

Always verify callback signatures to prevent forged callback attacks.


Common Issues

Signature Error (error_code: 0004)

Common causes:

  1. URL-encoded parameter values - The signing string uses raw values, do NOT URL-encode them (e.g., notify_url should remain https://... as-is, not https%3A%2F%2F...)
  2. Inconsistent parameter values - Ensure the parameter values used for signing match the actual request exactly
  3. Not excluding sign_type - Both sign and sign_type should be excluded from signature calculation
  4. Encoding issues - Ensure UTF-8 encoding is used