Docs
Docs
  • 简体中文
  • English
Docs
  • 简体中文
  • English
  • Docs

    • Common Conventions
    • Collection / Payin
    • Payout / Remittance
    • Account Balance
    • Transaction Query
    • Async Notification (Collection / Payout)
    • Signature
    • Account (Acct)
    • Special Instructions

Signature

Algorithm

Summary: sort parameters by ASCII (ascending), exclude empty values, build a querystring, then append &key=YOUR_SECRET_KEY at the end. (Parameter names are lowercase by default.) sign itself is NOT included in the signature.

General steps:

  1. Put all request/response parameters into a set M. Sort the non-empty parameters in M by parameter name ASCII (lexicographical order). Join them into stringA using URL key-value format: key1=value1&key2=value2....
  2. Append &key= + your secret key to stringA to form stringSignTemp, then calculate MD5 of stringSignTemp to get sign.

Important Rules

  • Sort parameter names by ASCII (lexicographical order).
  • Parameters with empty values are not included in the signature.
  • All parameter names are lowercase.
  • When verifying a response or an async callback, the incoming sign is not included in the signature. Generate a signature from the other fields and compare it with the received sign.
  • The API may add new fields. Signature verification must support additional fields.
  • URLs and other special characters must be URL-encoded.
  • Spaces must be escaped as + (different languages may behave differently; pay attention).
  • Do not convert the final signature string to uppercase.
  • When the payout account info is a JSON object (acct), flatten it as acct.key1, acct.key2... (see acct Flattening).

Example (Step by Step)

Example parameters:

{
  "merid": "12345",
  "amt": "1.00",
  "tno": "123123123123",
  "noti_url": "https://www.baidu.com",
  "extra": ""
}
  1. Build the raw signature string
  • Remove empty values
  • URL-encode
  • Sort by parameter name ASCII order
  • Join as key1=value1&key2=value2&key3=value3...

Result:

amt=1.00&merid=12345&noti_url=https%3A%2F%2Fwww.baidu.com&tno=123123123123
  1. Append &key=YOUR_SECRET_KEY
amt=1.00&merid=12345&noti_url=https%3A%2F%2Fwww.baidu.com&tno=123123123123&key=xxxxxxxxx
  1. Calculate MD5
signresult=md5("amt=1.00&merid=12345&noti_url=https%3A%2F%2Fwww.baidu.com&tno=123123123123&key=xxxxxxxxx")

Final request parameters:

{
  "merid": "12345",
  "amt": "100.00",
  "tno": "123123123123",
  "noti_url": "https://www.baidu.com",
  "extra": "",
  "sign": "signresult"
}

acct Flattening

For acct in payout/collection requests, flatten it as follows. Given:

{
  "acct": {
    "code": "PH_GCASH",
    "name": "GCash",
    "number": "09451187823",
    "type": "bankcard",
    "user": "EMHER AAA"
  },
  "amt": "2100.00",
  "cur": "PHP",
  "merid": "99999",
  "notify_url": "http://cb.abcd.com/callback.do",
  "tno": "72245523588"
}

The final joined string is:

acct.code=PH_GCASH&acct.name=GCash&acct.number=09451187823&acct.type=bankcard&acct.user=EMHER+AAA&amt=2100.00&cur=PHP&merid=96182&notify_url=http%3A%2F%2Fcb.abcd.com%2Fcallback.do&tno=72245523588

MD5 calculation is the same:

md5(str+"&key="key)
!! Note:
During signing, both parameter names and values must be URL-encoded following the `application/x-www-form-urlencoded` encoding rules, NOT `RFC 3986`. The code below was converted by GPT, so if your urlencode function behaves differently, pay attention to this requirement.

Reference Code

PHP Example

<?php
function data_encode(array $data)
{
    $flat = [];
    flatten_data('', $data, $flat);

    if (empty($flat)) return '';

    ksort($flat); // asc

    $out = [];
    foreach ($flat as $k => $v) {
        if ($v === '') continue; // strip = 1
        $out[] = urlencode($k) . '=' . urlencode($v); // urlencode = 1
    }

    return implode('&', $out);
}

function flatten_data(string $prefix, array $data, array &$out)
{
    foreach ($data as $k => $v) {
        $key = $prefix ? $prefix . '.' . $k : $k;

        if (is_array($v)) {
            foreach ($v as $kk => $vv) {
                $out[$key . '.' . $kk] = (string)$vv;
            }
        } else {
            $out[$key] = (string)$v;
        }
    }
}


$data = [
    "merid" => "12345",
    "amt" => "100.01",
    "noti_url" => "https://www.baidu.com",
    "acct" => [
        "type" => "bank",
        "card" => "22222222",
    ],
];
$your_api_key="keyekeykeykeyabcd";
echo "data-string".data_encode($data)."<br/>";
echo "sign-string".data_encode($data)."&key=".$your_api_key;

C# Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

class Program
{
    static string DataEncode(Dictionary<string, object> data)
    {
        var flat = new Dictionary<string, string>();
        FlattenData("", data, flat);

        if (flat.Count == 0) return "";

        var keys = flat.Keys.OrderBy(k => k).ToList();

        var outList = new List<string>();
        foreach (var k in keys)
        {
            var v = flat[k];
            if (v == "") continue;
            outList.Add(WebUtility.UrlEncode(k) + "=" + WebUtility.UrlEncode(v));
        }

        return string.Join("&", outList);
    }

    static void FlattenData(string prefix, Dictionary<string, object> data, Dictionary<string, string> flat)
    {
        foreach (var kv in data)
        {
            string key = prefix == "" ? kv.Key : prefix + "." + kv.Key;

            if (kv.Value is Dictionary<string, object> map)
            {
                foreach (var m in map)
                {
                    flat[key + "." + m.Key] = m.Value.ToString();
                }
            }
            else
            {
                flat[key] = kv.Value.ToString();
            }
        }
    }

    static void Main()
    {
        var data = new Dictionary<string, object>
        {
            {"merid", "12345"},
            {"amt", "100.01"},
            {"noti_url", "https://www.baidu.com"},
            { "acct", new Dictionary<string, object>{
                {"type", "bank"},
                {"card", "22222222"},
            }},
        };

        string apiKey = "keyekeykeykeyabcd";

        Console.WriteLine("data-string " + DataEncode(data));
        Console.WriteLine("sign-string " + DataEncode(data) + "&key=" + apiKey);
    }
}
 

Java Example

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class Main {

    static String dataEncode(Map<String, Object> data) {
        Map<String, String> flat = new HashMap<>();
        flattenData("", data, flat);

        if (flat.isEmpty()) return "";

        List<String> keys = new ArrayList<>(flat.keySet());
        Collections.sort(keys);

        List<String> out = new ArrayList<>();
        for (String k : keys) {
            String v = flat.get(k);
            if (v.equals("")) continue;
            out.add(url(k) + "=" + url(v));
        }

        return String.join("&", out);
    }

    static void flattenData(String prefix, Map<String, Object> data, Map<String, String> out) {
        for (Map.Entry<String, Object> e : data.entrySet()) {
            String key = prefix.isEmpty() ? e.getKey() : prefix + "." + e.getKey();
            Object v = e.getValue();

            if (v instanceof Map) {
                Map<?, ?> m = (Map<?, ?>) v;
                for (Map.Entry<?, ?> sub : m.entrySet()) {
                    out.put(key + "." + sub.getKey(), sub.getValue().toString());
                }
            } else {
                out.put(key, v.toString());
            }
        }
    }

    static String url(String s) {
        return URLEncoder.encode(s, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) {
        Map<String, Object> data = new HashMap<>();
        data.put("merid", "12345");
        data.put("amt", "100.01");
        data.put("noti_url", "https://www.baidu.com");

        Map<String, Object> acct = new HashMap<>();
        acct.put("type", "bank");
        acct.put("card", "22222222");
        data.put("acct", acct);

        String apiKey = "keyekeykeykeyabcd";

        System.out.println("data-string " + dataEncode(data));
        System.out.println("sign-string " + dataEncode(data) + "&key=" + apiKey);
    }
}

Go Example

package main

import (
	"fmt"
	"net/url"
	"sort"
	"strings"
)

func dataEncode(data map[string]any) string {
	flat := map[string]string{}
	flattenData("", data, flat)

	if len(flat) == 0 {
		return ""
	}

	keys := make([]string, 0, len(flat))
	for k := range flat {
		keys = append(keys, k)
	}
	sort.Strings(keys) // asc

	var out []string
	for _, k := range keys {
		v := flat[k]
		if v == "" { // strip = 1
			continue
		}
		out = append(out, url.QueryEscape(k)+"="+url.QueryEscape(v)) // urlencode = 1
	}

	return strings.Join(out, "&")
}

func flattenData(prefix string, data map[string]any, out map[string]string) {
	for k, v := range data {
		key := k
		if prefix != "" {
			key = prefix + "." + k
		}

		switch vv := v.(type) {
		case map[string]any:
			for kk, vvv := range vv {
				out[key+"."+kk] = fmt.Sprintf("%v", vvv)
			}
		default:
			out[key] = fmt.Sprintf("%v", v)
		}
	}
}

func main() {
	data := map[string]any{
		"merid":    "12345",
		"amt":      "100.01",
		"noti_url": "https://www.baidu.com",
		"acct": map[string]any{
			"type": "bank",
			"card": "22222222",
		},
	}

	yourAPIKey := "keyekeykeykeyabcd"

	fmt.Println("data-string", dataEncode(data))
	fmt.Println("sign-string", dataEncode(data)+"&key="+yourAPIKey)
}

Python Example

import urllib.parse

def data_encode(data):
    flat = {}
    flatten_data("", data, flat)

    if not flat:
        return ""

    out = []
    for k in sorted(flat.keys()):
        v = flat[k]
        if v == "":
            continue

        # Force Python encoding behavior to match Go's QueryEscape
        key_enc = urllib.parse.quote(k, safe="")
        val_enc = urllib.parse.quote(v, safe="").replace("%20", "+")  # Space -> +

        out.append(key_enc + "=" + val_enc)

    return "&".join(out)

def flatten_data(prefix, data, out):
    for k, v in data.items():
        key = k if prefix == "" else prefix + "." + k

        if isinstance(v, dict):
            for kk, vv in v.items():
                out[key + "." + kk] = str(vv)
        else:
            out[key] = str(v)

data = {
    "merid": "12345",
    "amt": "100.01",
	"ctm":        "2025-11-21 01:37:27",
    "noti_url": "https://api.kj1616.com/api/pay/callback/2o6i9i7rrsHJ9ghFn64u8o",
    "acct": {
        "type": "bank",
        "card": "22222222",
    },
}

api_key = "keyekeykeykeyabcd"

print("data-string", data_encode(data))
print("sign-string", data_encode(data) + "&key=" + api_key)
Prev
Async Notification (Collection / Payout)
Next
Account (Acct)