Skip to content

Signature Algorithm

Algorithm Description

The fields participating in the signature are:

  • apiPath: URI (required)
  • body: Request body (required), JSON string
  • x-api-key: Same as appId (required)
  • x-api-timestamp: Current timestamp in milliseconds (required)
  • Other parameters in the URI

Follow these steps for implementation:

  1. Put all the above parameter names and values into contentMap, sort the contentMap keys in ascending order
  2. JSON serialize the contentMap
  3. Use the SHA256 algorithm to encrypt the JSON string + secret key
  4. Finally, apply BASE64 encoding to the encryption result to get the signature string

For example:

shell

curl -H 'x-api-key: key' \
     -H 'x-api-timestamp: 1744636844000' \
     -H 'x-api-signature: 0xb7Kxxxx=' \
      'https://{HOST}/path/to/pay?param1=test1&param2=test2' \
      -d '{"data":"test"}'

curl -H 'x-api-key: key' \
     -H 'x-api-timestamp: 1744636844000' \
     -H 'x-api-signature: 0xb7Kxxxx=' \
      'https://{HOST}/path/to/pay?param1=test1&param2=test2' \
      -d '{"data":"test"}'

The JSON serialized contentMap would be:

json
{
  "apiPath": "/path/to/pay",
  "body": {
    "data": "test"
  },
  "param1": "test1",
  "param2": "test2",
  "x-api-key": "key",
  "x-api-timestamp": "17200001"
}
{
  "apiPath": "/path/to/pay",
  "body": {
    "data": "test"
  },
  "param1": "test1",
  "param2": "test2",
  "x-api-key": "key",
  "x-api-timestamp": "17200001"
}

Finally, encrypt this JSON string.

Code Implementation

Backend Implementation Examples

go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "net/url"
    "sort"
)

func GenSign(appId, apiSecret, pathUrl, payload string, tm int64) string {
	// Parse URL
	path := ""
	contentMap := make(map[string]string)
	if u, err := url.Parse(pathUrl); err == nil {
		path = u.Path
		for k, v := range u.Query() {
			if len(v) > 0 {
				contentMap[k] = v[0]
			}
		}
	}

	// Build the map to be signed
	contentMap["x-api-key"] = appId
	contentMap["x-api-timestamp"] = fmt.Sprintf("%d", tm) // milliseconds
	contentMap["apiPath"] = path
	contentMap["body"] = payload

	// Get all keys and sort
	keys := make([]string, 0, len(contentMap))
	for k := range contentMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// Create ordered map
	orderedMap := make(map[string]string)
	for _, k := range keys {
		orderedMap[k] = contentMap[k]
	}

	// JSON serialization
	content, _ := json.Marshal(orderedMap)

	// Calculate HMAC
	mac := hmac.New(sha256.New, []byte(apiSecret))
	mac.Write(content)
	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

// Usage example
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", `{"data":"test"}`, time.Now().UnixMilli())

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "net/url"
    "sort"
)

func GenSign(appId, apiSecret, pathUrl, payload string, tm int64) string {
	// Parse URL
	path := ""
	contentMap := make(map[string]string)
	if u, err := url.Parse(pathUrl); err == nil {
		path = u.Path
		for k, v := range u.Query() {
			if len(v) > 0 {
				contentMap[k] = v[0]
			}
		}
	}

	// Build the map to be signed
	contentMap["x-api-key"] = appId
	contentMap["x-api-timestamp"] = fmt.Sprintf("%d", tm) // milliseconds
	contentMap["apiPath"] = path
	contentMap["body"] = payload

	// Get all keys and sort
	keys := make([]string, 0, len(contentMap))
	for k := range contentMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// Create ordered map
	orderedMap := make(map[string]string)
	for _, k := range keys {
		orderedMap[k] = contentMap[k]
	}

	// JSON serialization
	content, _ := json.Marshal(orderedMap)

	// Calculate HMAC
	mac := hmac.New(sha256.New, []byte(apiSecret))
	mac.Write(content)
	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

// Usage example
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", `{"data":"test"}`, time.Now().UnixMilli())
php
<?php

function GenSign(
    string $appId,
    string $apiSecret,
    string $pathUrl,
    string $payload,
    int    $tm): string {
    $contentMap = [];
    $parts      = parse_url($pathUrl);

    $path = $parts['path'] ?? '';
    if (!empty($parts['query'])) {
        parse_str($parts['query'], $queryArr);
        foreach ($queryArr as $k => $v) {
            $contentMap[$k] = (string)$v;
        }
    }

    $contentMap['x-api-key']      = $appId;
    $contentMap['x-api-timestamp'] = (string)$tm;
    $contentMap['apiPath']        = $path;
    $contentMap['body']           = $payload;

    ksort($contentMap, SORT_STRING);

    $json = json_encode(
        $contentMap,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    $raw  = hash_hmac('sha256', $json, $apiSecret, true);
    return base64_encode($raw);
}


// Usage example
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", '{"data":"test"}', floor(microtime(true) * 1000))
<?php

function GenSign(
    string $appId,
    string $apiSecret,
    string $pathUrl,
    string $payload,
    int    $tm): string {
    $contentMap = [];
    $parts      = parse_url($pathUrl);

    $path = $parts['path'] ?? '';
    if (!empty($parts['query'])) {
        parse_str($parts['query'], $queryArr);
        foreach ($queryArr as $k => $v) {
            $contentMap[$k] = (string)$v;
        }
    }

    $contentMap['x-api-key']      = $appId;
    $contentMap['x-api-timestamp'] = (string)$tm;
    $contentMap['apiPath']        = $path;
    $contentMap['body']           = $payload;

    ksort($contentMap, SORT_STRING);

    $json = json_encode(
        $contentMap,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    $raw  = hash_hmac('sha256', $json, $apiSecret, true);
    return base64_encode($raw);
}


// Usage example
GenSign("A123456", "ABC123", "/path/to/pay?param1=test1&param2=test2", '{"data":"test"}', floor(microtime(true) * 1000))

Postman Pre-request Script

To automatically generate signatures in Postman, paste the following script into the Pre-request Script tab:

javascript
const appId = 'YOUR_APP_ID';
const apiSecret = 'YOUR_API_SECRET';

const ts = Date.now();

pm.request.headers.upsert({ key: 'x-api-key', value: appId });
pm.request.headers.upsert({ key: 'x-api-timestamp', value: String(ts) });

const path = pm.request.url.getPath();
const rawBody =
  pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';
const bodyForSign = rawBody.replace(/\r?\n/g, '');

const ordered = {
  apiPath: path,
  body: bodyForSign,
  'x-api-key': appId,
  'x-api-timestamp': String(ts),
};

const content = JSON.stringify(ordered);

console.log('Signature content:', ordered);

const signature = CryptoJS.HmacSHA256(content, apiSecret).toString(
  CryptoJS.enc.Base64
);

pm.request.headers.upsert({ key: 'x-api-signature', value: signature });

console.log('Generated signature:', signature);
const appId = 'YOUR_APP_ID';
const apiSecret = 'YOUR_API_SECRET';

const ts = Date.now();

pm.request.headers.upsert({ key: 'x-api-key', value: appId });
pm.request.headers.upsert({ key: 'x-api-timestamp', value: String(ts) });

const path = pm.request.url.getPath();
const rawBody =
  pm.request.body && pm.request.body.raw ? pm.request.body.raw : '';
const bodyForSign = rawBody.replace(/\r?\n/g, '');

const ordered = {
  apiPath: path,
  body: bodyForSign,
  'x-api-key': appId,
  'x-api-timestamp': String(ts),
};

const content = JSON.stringify(ordered);

console.log('Signature content:', ordered);

const signature = CryptoJS.HmacSHA256(content, apiSecret).toString(
  CryptoJS.enc.Base64
);

pm.request.headers.upsert({ key: 'x-api-signature', value: signature });

console.log('Generated signature:', signature);