> ## Documentation Index
> Fetch the complete documentation index at: https://doc.va777.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Signature

> How to generate the sign parameter required on every v1 API call, with examples in several languages.

Every call to our services must include the `sign` parameter. `sign` is generated from the parameters you send plus your API key.

With the [seamless wallet](https://doc.va777.net/en/v1/guides/seamless-wallet.md), we generate `sign` the same way when we call your APIs. Verify it the same way.

## Which parameters to include

* Include only the fields marked **Signed** in each API's parameter list. Unmarked fields and `sign` itself are not included.
* Include a **Signed** field even when it has no value. For example, on the first query `nextToken` is an empty string and is included as `nextToken=`.
* Use **the exact string you send** as the value. For example, if you send `amount` as `10.00`, sign `10.00`, not `10`.
* Array parameters repeat the same key, in the order they are sent, for example `gameIDList=102&gameIDList=101`.

## Steps

1. **Sort by key**: Sort parameter names by character code (ASCII), so uppercase letters come before lowercase letters.
2. **URL-encode into a query string**: Encode both keys and values, join each pair with `=` and the pairs with `&`. Encoding rules:
   * Letters, digits and `-`, `_`, `.`, `~` are not encoded.
   * A space becomes `+`.
   * Every other character is UTF-8 encoded as `%XX` with **uppercase** hex, for example `:` becomes `%3A` and `+` becomes `%2B`.
3. **Append the API key and hash with MD5**: Append your API key directly to the query string with nothing in between. The MD5 of the result, as a **lowercase** 32-character hex string, is `sign`.

A time value such as `2025-12-01T00:00:00.000+08:00` is encoded as `2025-12-01T00%3A00%3A00.000%2B08%3A00`.

## Example

For [Deposit](https://doc.va777.net/en/v1/api-reference/transaction/deposit.md) with the API key `a188va`:

| Step | Result |
| - | - |
| Signed parameters | `transactionId=deposit-20250101000001`, `channelId=1`, `account=player001`, `currency=CNY`, `amount=10.00` |
| Sorted query string | `account=player001&amount=10.00&channelId=1&currency=CNY&transactionId=deposit-20250101000001` |
| Append API key | `account=player001&amount=10.00&channelId=1&currency=CNY&transactionId=deposit-20250101000001a188va` |
| MD5 (`sign`) | `6368a5fd551daad9f202d7cda331bbeb` |

## Code examples

Each example below has been run with the sample data above and returns `6368a5fd551daad9f202d7cda331bbeb`.

```go Go theme={null}
package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"net/url"
)

// GenerateSign Sorts by key and URL-encodes (url.Values.Encode), appends the API key, then hashes with MD5
func GenerateSign(params url.Values, apiKey string) string {
	sum := md5.Sum([]byte(params.Encode() + apiKey))
	return hex.EncodeToString(sum[:])
}

func main() {
	params := url.Values{}
	params.Set("transactionId", "deposit-20250101000001")
	params.Set("channelId", "1")
	params.Set("account", "player001")
	params.Set("currency", "CNY")
	params.Set("amount", "10.00")
	// For array parameters, call Add for each value, e.g. params.Add("gameIDList", "102")
	fmt.Println(GenerateSign(params, "a188va")) // 6368a5fd551daad9f202d7cda331bbeb
}
```

```php PHP theme={null}
<?php
// $params: the parameters marked Signed; $apiKey: the API key we provide
function generateSign($params, $apiKey)
{
    ksort($params, SORT_STRING);
    $pairs = array();
    foreach ($params as $key => $value) {
        // Array parameters repeat the same key, in the order they are sent
        foreach ((array) $value as $v) {
            $pairs[] = urlencode($key) . '=' . urlencode($v);
        }
    }
    // urlencode turns ~ into %7E, but ~ must stay unencoded
    $query = str_replace('%7E', '~', implode('&', $pairs));
    return md5($query . $apiKey);
}

echo generateSign(array(
    "transactionId" => "deposit-20250101000001",
    "channelId" => 1,
    "account" => "player001",
    "currency" => "CNY",
    "amount" => "10.00"
), "a188va"); // 6368a5fd551daad9f202d7cda331bbeb
```

```python Python theme={null}
import hashlib
from urllib.parse import urlencode, quote_plus


def generate_sign(params: dict, api_key: str) -> str:
    # Sorts by key; array parameters repeat the same key (doseq=True), in the order they are sent
    query = urlencode(sorted(params.items()), doseq=True, quote_via=quote_plus)
    return hashlib.md5((query + api_key).encode("utf-8")).hexdigest()


print(generate_sign({
    "transactionId": "deposit-20250101000001",
    "channelId": 1,
    "account": "player001",
    "currency": "CNY",
    "amount": "10.00",
}, "a188va"))  # 6368a5fd551daad9f202d7cda331bbeb
```

```javascript Node.js theme={null}
const crypto = require("crypto");

// Matches our encoding: space becomes +, ~ stays as is, everything else is uppercase percent-encoded
function encode(value) {
  return encodeURIComponent(value)
    .replace(/[!'()*]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase())
    .replace(/%20/g, "+");
}

function generateSign(params, apiKey) {
  const query = Object.keys(params)
    .sort()
    // Array parameters repeat the same key, in the order they are sent
    .flatMap((key) => [].concat(params[key]).map((v) => encode(key) + "=" + encode(String(v))))
    .join("&");
  return crypto.createHash("md5").update(query + apiKey, "utf8").digest("hex");
}

console.log(generateSign({
  transactionId: "deposit-20250101000001",
  channelId: 1,
  account: "player001",
  currency: "CNY",
  amount: "10.00",
}, "a188va")); // 6368a5fd551daad9f202d7cda331bbeb
```

## Troubleshooting

If the response code is `102` (Sign invalid), check the following in order:

1. You included exactly the fields marked **Signed**, no more and no fewer.
2. The values you signed are identical to the values you sent, including decimal places and letter case.
3. Percent-encoding uses uppercase hex, spaces become `+` (not `%20`), and `~` is left unencoded.
4. The MD5 result is lowercase.
5. The API key is correct and has no leading or trailing spaces or line breaks.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.