NAV
java php python go javascript

XAgent 单一钱包 API 文档 V3.0

前言

对接流程

  1. 提供相应信息,联系我方在UAT环境给您开户
  2. 提供您的服务器 IP 用以添加服务器 IP 白名单。
  3. 若您的接口也设置有白名单, 我方会提供服务器 IP 用以添加访问您的接口的白名单。
  4. 开户完成,我方提供publicKey、businessAccount、site等其他参数,和游戏后台地址
  5. 根据您的游戏需求,我方提供相对应的游戏编码表,对接用
  6. 根据文档,您需要将我方需要的接口开发完毕,提供出来
  7. 根据游戏编码表,将游戏相关数据(名称、编码、封面素材等)在您的平台创建出来
  8. 通过接口文档开始对接接口
  9. 接口对接完毕,联调测试无误
  10. 正式环境准备接入,我方商务介入,与您洽谈商务事项
  11. 正式环境开户完毕,切换接口地址和参数
  12. 您提供IP白名单,游戏后台配置白名单
  13. 正式环境预运营与验收
  14. 接入完毕,正式上线

对接说明

  1. 我方所有接口参数入参 , 大小写敏感 (不会忽略大小写) , 请注意传值

  2. 对于您提供的所有接口的参数入参 , 也需要大小写敏感 (不忽略大小写)

  3. 在您的WEB / H5 / APP 平台,配置好游戏入口后,用户点击游戏入口,调用我方提供的用户注册/登录接口, 传入account、游戏编码、gameType等参数

  4. 成功后,取到接口返回值,其中url字段,就是携带了该用户登录态token的form表单 或 url 链接

  5. 通过访问、内嵌url字段,即可跳转进入游戏本体内部

  6. 您需要取用户的唯一ID作为account,按照一定的规则,拼接好,传入接口

  7. 我方会在游戏内不同的场景,调用您提供的接口进行 下单、结算、更新结算等操作

  8. 您可在我方提供的游戏控制台(后台),创建不同的代理线路,每条线路可配置独立的RTP配置,每条线路的用户也是分割独立的。

  9. 我方在给您开户时,会帮您创建好一条默认的代理线路供您使用,后续若需要更多,可自行去后台添加

  10. 您还可以在后台创建您的子代理,将不同的代理线路分配给您的子代理管理,并附带 子代理后台,可查看分配给该子代理的相关游戏数据、用户订单记录、报表、调整RTP参数等。

白名单限制

我方的接口域名 , 以及给您开设的游戏后台 , 均有设置IP白名单限制 , 若未进行配置 , 则无法调用接口 / 访问后台 , 所以在对接开始之前 , 需要您提供如下IP信息给我方进行预配置:

  1. 您各环境的服务器的IP地址

  2. 您需要访问游戏后台的设备IP地址

同时若您的接口也设置有白名单限制, 在调用您提供的接口之前 , 我方也会提供各环境服务器IP给您预配置 , 确保对接流程顺畅

我方提供的接口

域名

UAT环境:https://xagent-open-api-uat-hh.ncfc.cc

正式环境:联系商务人员获取

调用前准备

Headers:

参数名称 参数值 是否必须 示例 备注
Content-Type application/json application/json
businessAccount 我方提供的总代唯一标识 ballGame
site XAgent商户后台中的代理线路标识 default 若无另行设定,带入default即可

参数加解密示例:

// 加密示例
/**
 * @param jsonString json字串,格式示例:{"field_1":"value_1","field_2":"value_2"}
 * @param publicKey  公钥,格式示例:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADC....
 * @return 加密后的请求参数,包含加密后的数据字段
 * @throws Exception 加密过程中可能抛出的异常
 */
public static Map<String, String> encryptRequestBody(String jsonString, String publicKey) throws Exception {
    // 将json字符串转换为字节数组
    byte[] data = jsonString.getBytes(StandardCharsets.UTF_8);

    // 通过Base64解码获取公钥字节数组
    byte[] keyBytes = Base64.getDecoder().decode(publicKey);

    // 使用公钥字节数组创建X509EncodedKeySpec对象
    X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);

    // 使用KeyFactory生成公钥对象
    KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // 加密算法
    Key publicK = keyFactory.generatePublic(x509KeySpec);

    // 初始化Cipher对象,用于RSA加密
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, publicK);

    int inputLen = data.length;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int offSet = 0;
    byte[] cache;

    // 对数据进行分段加密,每次加密块的大小不超过MAX_ENCRYPT_BLOCK
    int MAX_ENCRYPT_BLOCK = 117;   // RSA加密的最大块大小,PKCS1填充方式下为117字节
    while (inputLen - offSet > 0) {
        int len = Math.min(MAX_ENCRYPT_BLOCK, inputLen - offSet);
        cache = cipher.doFinal(data, offSet, len);
        out.write(cache);
        offSet += len;
    }

    // 获取加密后的字节数组
    byte[] encryptedData = out.toByteArray();
    out.close();

    // 将加密后的字节数组进行Base64编码,转换为字符串
    String base64String = Base64.getEncoder().encodeToString(encryptedData);

    // 创建一个Map,将Base64编码后的加密数据作为参数放入Map中
    Map<String, String> encryptParam = new HashMap<>();
    encryptParam.put("data", base64String);

    // 返回包含加密数据的Map
    return encryptParam;
}

public static void main(String[] args) throws Exception {
  String account = "用户名";
  String password = "password";

  // 手動拼接 JSON
  String jsonString = "{"
  + "\"account\":\"" + account + "\","
  + "\"password\":\"" + password + "\""
  + "}";

  System.out.println(jsonString);

  String base64PublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...'; // 替換為你的 base64 公鑰內容

  OkHttpClient client = new OkHttpClient().newBuilder()
  .build();

  MediaType mediaType = MediaType.parse("application/json");
  RequestBody body = RequestBody.create(mediaType, encryptRequestBody( jsonString , base64PublicKey ));



  Request request = new Request.Builder()
  .url("your-api-endpoint")
  .method("POST", body )
  .addHeader("businessAccount", "我方提供的总代唯一标识")
  .addHeader("site", "XAgent 商户后台中的代理线路标识")
  .addHeader("Content-Type", "application/json")
  .build();
  Response response = client.newCall(request).execute();
}

// 解密示例
public class RSAPublicDecrypt {
  public static void main(String[]args)throws Exception {
    String pubKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
      "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCioAIV3Npz1K8H4L+r4E2DCptp\n" +
      "MJHeD09qzAyh244+dkWRqod7u0G+JMQp2EJ50MEf/zmNsprtRET5HAaM2zG6TAIL\n" +
      "7hQLuUj/cDXLLWS+MvFIhjruR2xj//F7EQNJBPxE/CVcCzZQB7WDlH+aMYeCP+/y\n" +
      "DNINBEMF9KTM+0b6qwIDAQAB\n" +
      "-----END PUBLIC KEY-----";

    String encryptedBase64 = "RKgVABPFCq5jaOaR/nWzSkB1CDjtPP7Cs7KKlIs/LYePsTQM12NpaR+ph3TlMkOphSxcO4pchvOI+gwF/XuaYs9LLW/r3ikmgoGF4AzZdKMYx50fAECMM1SZeVBu10KqZx7dF0hDdhBNr+2gLJwletW9bkcBCRRBJG1NrtZUXPGBBV/u+hL2uTSKrzUT46JZkLz2XllZMGtcwYn9ioBF0R1usob7mvVHU74Nktwh+6n2M+lUwJFXD0/GWrJy8stv/QZtUxjrmRkKHK9xGmtzx2oLi1Is/5bsPb5VJaTTfi9u2qWgvwndhDMe2OuGSliaileMW3xwXB3wFCtaC5UoYg==";

    // 轉換公鑰 PEM -> byte[]
    String cleanPem = pubKeyPem.replaceAll("-----\\w+ PUBLIC KEY-----", "").replaceAll("\\s", "");
    byte[]keyBytes = Base64.getDecoder().decode(cleanPem);

    // 建立公鑰物件
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes));

    // 設定 Cipher 模式為 RSA 公鑰解密,PKCS1 Padding
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE, publicKey);

    // 解密分段處理
    byte[]encryptedBytes = Base64.getDecoder().decode(encryptedBase64);
    int chunkSize = 128; // 1024-bit RSA
    StringBuilder decrypted = new StringBuilder();

    for (int i = 0; i < encryptedBytes.length; i += chunkSize) {
      byte[]chunk = new byte[chunkSize];
      System.arraycopy(encryptedBytes, i, chunk, 0, chunkSize);
      byte[]decryptedChunk = cipher.doFinal(chunk);
      decrypted.append(new String(decryptedChunk, "UTF-8"));
    }
    System.out.println(decrypted.toString().trim());
  }
}
// 加密示例:
<?php
// 假設 json 資料與 base64 公鑰字串
$jsonString = json_encode([
    'account'=> '用户名',
    'password'=> 'password'
]);

$base64PublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...'; // 替換為你的 base64 公鑰內容

// 1. 將 base64 公鑰解碼並封裝成 PEM 格式
$pubKeyFormatted = "-----BEGIN PUBLIC KEY-----\n" .
    chunk_split($base64PublicKey, 64, "\n") .
    "-----END PUBLIC KEY-----";

// 2. 載入公鑰
$publicKey = openssl_pkey_get_public($pubKeyFormatted);
if (!$publicKey) {
    die("公鑰格式錯誤");
}

// 3. 將 JSON 字串轉為位元組(UTF-8)
$data = $jsonString;

// 4. RSA 1024-bit 公鑰 -> 明文限制 117 bytes (PKCS1 padding)
$chunkSize = 117;
$offset = 0;
$encrypted = '';

while ($offset < strlen($data)) {
    $chunk = substr($data, $offset, $chunkSize);
    $encryptedChunk = '';
    if (!openssl_public_encrypt($chunk, $encryptedChunk, $publicKey, OPENSSL_PKCS1_PADDING)) {
        die("加密失敗");
    }
    $encrypted .= $encryptedChunk;
    $offset += $chunkSize;
}

// 5. 最終結果:Base64 編碼後輸出
$encryptedBase64 = base64_encode($encrypted);
echo "加密結果(Base64):\n" . $encryptedBase64;
// HTTP POST 请求函数
function httpPost($encryptedBase64) {
    $url = 'your-api-endpoint'; // 替换为您的API接口地址

    $options = array(
        'http' => array(
            'header' => "businessAccount: 我方提供的总代唯一标识\r\n" .
            "site: XAgent 商户后台中的代理线路标识\r\n" .
            "Content-Type: application/json\r\n",
            'method'  => 'POST',
            'content' => json_encode([
                'data'=> $encryptedBase64
            ])
        )
    );

    $context  = stream_context_create($options);
    $result = file_get_contents($url, false, $context);

    return $result;
}
?>

// 解密示例:
<?php
$pubKeyFormatted = <<<EOD
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCioAIV3Npz1K8H4L+r4E2DCptp
MJHeD09qzAyh244+dkWRqod7u0G+JMQp2EJ50MEf/zmNsprtRET5HAaM2zG6TAIL
7hQLuUj/cDXLLWS+MvFIhjruR2xj//F7EQNJBPxE/CVcCzZQB7WDlH+aMYeCP+/y
DNINBEMF9KTM+0b6qwIDAQAB
-----END PUBLIC KEY-----
EOD;

// 取得公鑰資源
$publicKey = openssl_pkey_get_public($pubKeyFormatted);

if (!$publicKey) {
    die("❌ 無法讀取公鑰格式,請檢查 PEM 是否正確\n");
}

// 加密內容(base64 編碼的 RSA "加密" 結果)
$encryptedBase64 = "RKgVABPFCq5jaOaR/nWzSkB1CDjtPP7Cs7KKlIs/LYePsTQM12NpaR+ph3TlMkOphSxcO4pchvOI+gwF/XuaYs9LLW/r3ikmgoGF4AzZdKMYx50fAECMM1SZeVBu10KqZx7dF0hDdhBNr+2gLJwletW9bkcBCRRBJG1NrtZUXPGBBV/u+hL2uTSKrzUT46JZkLz2XllZMGtcwYn9ioBF0R1usob7mvVHU74Nktwh+6n2M+lUwJFXD0/GWrJy8stv/QZtUxjrmRkKHK9xGmtzx2oLi1Is/5bsPb5VJaTTfi9u2qWgvwndhDMe2OuGSliaileMW3xwXB3wFCtaC5UoYg==";

// 將 base64 還原為二進位資料
$encrypted = base64_decode($encryptedBase64);
$chunkSize = 128; // 對應 1024-bit RSA
$decryptedAll = '';

for ($i = 0; $i < strlen($encrypted); $i += $chunkSize) {
    $chunk = substr($encrypted, $i, $chunkSize);
    $partialDecrypted = '';

    $success = openssl_public_decrypt($chunk, $partialDecrypted, $publicKey, OPENSSL_PKCS1_PADDING);

    if (!$success) {
        echo "❌ 解密失敗於區塊 $i\n";
        exit;
    }

    $decryptedAll .= $partialDecrypted;
}

echo "✅ 解密成功:\n";
echo $decryptedAll . "\n";
?>
// 加密示例:
import base64
from json import dumps
import requests
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
def rsa_encrypt_chunked(data: bytes, public_key_pem: str) -> bytes:
    """
    使用 RSA 公钥 (PKCS#1) 分段加密数据。
    :param data: 需要加密的原始数据 (bytes)
    :param public_key_pem: 公钥字符串 (PEM 格式)
    :return: 加密后的原始 bytes (未进行 Base64 编码)
    """
    key = RSA.importKey(public_key_pem)
    cipher_rsa = PKCS1_v1_5.new(key)
    def get_rsa_chunk_size(public_key_pem: str) -> int:
        """
        计算 RSA 加密时单次最大加密数据的大小。
        :param public_key_pem: 公钥字符串 (PEM 格式)
        :return: 可加密的最大字节数
        """
        key = RSA.importKey(public_key_pem)
        key_bits = key.size_in_bits()  # 获取密钥的位数
        key_bytes = key_bits // 8  # 转换为字节
        return key_bytes - 11  # PKCS#1 v1.5 需要扣除 11 字节填充
    offset = 0
    encrypted_data = b""
    chunk_size = get_rsa_chunk_size(public_key_pem)  # 计算分块大小
    while offset < len(data):
        chunk = data[offset: offset + chunk_size]  # 获取当前分块数据
        encrypted_chunk = cipher_rsa.encrypt(chunk)  # 使用 RSA 加密
        encrypted_data += encrypted_chunk  # 拼接加密后的数据
        offset += chunk_size  # 移动偏移量
    return encrypted_data
if __name__ == "__main__":
    # 业务账号唯一标识
    business_account = "我方提供的总代唯一标识"
    # 代理线路标识
    site = "XAgent 商户后台中的代理线路标识"
    # RSA 公钥
    public_key_str = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END PUBLIC KEY-----"""
    # 需要加密的参数
    payload = {"account": "用户名", "password": "password"}
    data_bytes = dumps(payload).encode("utf-8")  # 将字典转换为 JSON 并编码为字节
    # 分段加密数据
    encrypted_bytes = rsa_encrypt_chunked(data_bytes, public_key_str)
    # 对加密数据进行 Base64 编码
    encrypted_b64 = base64.b64encode(encrypted_bytes).decode("utf-8")
    # 发送请求的参数
    encrypt_param = {"data": encrypted_b64}
    # 请求头
    headers = {
        "Content-Type": "application/json",
        "businessAccount": business_account,
        "site": site
    }
    # 接口地址
    url = "https://xxxx.xxx.xxxx.xxxx/v3/xxxxx/xxxx/xxxx"
    # 发送 POST 请求
    response = requests.post(url, json=encrypt_param, headers=headers)
    print("Response:", response.text)

// 解密示例:
import base64
from base64 import b64decode
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

pub_key_pem = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCioAIV3Npz1K8H4L+r4E2DCptp
MJHeD09qzAyh244+dkWRqod7u0G+JMQp2EJ50MEf/zmNsprtRET5HAaM2zG6TAIL
7hQLuUj/cDXLLWS+MvFIhjruR2xj//F7EQNJBPxE/CVcCzZQB7WDlH+aMYeCP+/y
DNINBEMF9KTM+0b6qwIDAQAB
-----END PUBLIC KEY-----"""

encrypted_b64 = "RKgVABPFCq5jaOaR/nWzSkB1CDjtPP7Cs7KKlIs/LYePsTQM12NpaR+ph3TlMkOphSxcO4pchvOI+gwF/XuaYs9LLW/r3ikmgoGF4AzZdKMYx50fAECMM1SZeVBu10KqZx7dF0hDdhBNr+2gLJwletW9bkcBCRRBJG1NrtZUXPGBBV/u+hL2uTSKrzUT46JZkLz2XllZMGtcwYn9ioBF0R1usob7mvVHU74Nktwh+6n2M+lUwJFXD0/GWrJy8stv/QZtUxjrmRkKHK9xGmtzx2oLi1Is/5bsPb5VJaTTfi9u2qWgvwndhDMe2OuGSliaileMW3xwXB3wFCtaC5UoYg=="

key = RSA.import_key(pub_key_pem)
cipher = PKCS1_v1_5.new(key)

encrypted_bytes = b64decode(encrypted_b64)
chunk_size = 128
decrypted = b''

for i in range(0, len(encrypted_bytes), chunk_size):
    chunk = encrypted_bytes[i:i+chunk_size]
    decrypted += cipher.decrypt(chunk, None)

print("✅ 解密結果:")
print(decrypted.decode("utf-8").strip())
// 加密示例:
package main

import (
    "bytes"
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/base64"
    "encoding/json"
    "encoding/pem"
    "fmt"
    "net/http"
)

func main() {
    // 参数
    param := map[string]string{
        "account":  "用户名",
        "password": "password",
    }

    // JSON 编码
    jsonData, err := json.Marshal(param)
    if err != nil {
        fmt.Println("JSON Marshal error:", err)
        return
    }

    // RSA 加密
    publicKey := `-----BEGIN PUBLIC KEY-----
your-public-key-here
-----END PUBLIC KEY-----`

    encodedData, err := rsaEncrypt(jsonData, publicKey)
    if err != nil {
        fmt.Println("RSA Encryption error:", err)
        return
    }

    // Base64 编码
    base64String := base64.StdEncoding.EncodeToString(encodedData)
    fmt.Println("encoded:", base64String)

    // 构建请求参数
    encryptParam := map[string]string{
        "data": base64String,
    }

    // 调用接口
    req, err := http.NewRequest("POST", "your-api-endpoint", encryptParam)
    if err != nil {
        fmt.Println("HTTP Post error:", err)
    }

    // 设置请求头
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("site", "XAgent 商户后台中的代理线路标识")
    req.Header.Set(businessAccount", "我方提供的总代唯一标识")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("HTTP Post get response error:", err)
    }

    fmt.Println("Response:", resp)
}

// 解密示例:
package main

import (
    "crypto/rsa"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "fmt"
    "math/big"
)

func main() {
    publicKeyPEM := `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCioAIV3Npz1K8H4L+r4E2DCptp
MJHeD09qzAyh244+dkWRqod7u0G+JMQp2EJ50MEf/zmNsprtRET5HAaM2zG6TAIL
7hQLuUj/cDXLLWS+MvFIhjruR2xj//F7EQNJBPxE/CVcCzZQB7WDlH+aMYeCP+/y
DNINBEMF9KTM+0b6qwIDAQAB
-----END PUBLIC KEY-----`

    encryptedBase64 := "RKgVABPFCq5jaOaR/nWzSkB1CDjtPP7Cs7KKlIs/LYePsTQM12NpaR+ph3TlMkOphSxcO4pchvOI+gwF/XuaYs9LLW/r3ikmgoGF4AzZdKMYx50fAECMM1SZeVBu10KqZx7dF0hDdhBNr+2gLJwletW9bkcBCRRBJG1NrtZUXPGBBV/u+hL2uTSKrzUT46JZkLz2XllZMGtcwYn9ioBF0R1usob7mvVHU74Nktwh+6n2M+lUwJFXD0/GWrJy8stv/QZtUxjrmRkKHK9xGmtzx2oLi1Is/5bsPb5VJaTTfi9u2qWgvwndhDMe2OuGSliaileMW3xwXB3wFCtaC5UoYg=="

    // Decode public key
    block, _ := pem.Decode([]byte(publicKeyPEM))
    if block == nil {
        panic(" 無法解析 PEM 公鑰")
    }
    pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
    if err != nil {
        panic(" 公鑰格式錯誤: " + err.Error())
    }
    pubKey := pubInterface.(*rsa.PublicKey)

    // Base64 decode
    encryptedBytes, err := base64.StdEncoding.DecodeString(encryptedBase64)
    if err != nil {
        panic(" base64 解析錯誤")
    }

    keySize := pubKey.Size() // 128 bytes for 1024-bit RSA
    var decrypted []byte

    for i := 0; i < len(encryptedBytes); i += keySize {
        chunk := encryptedBytes[i : i+keySize]

        // 解密公式:明文 = (密文 ^ e) mod n
        c := new(big.Int).SetBytes(chunk)
        m := new(big.Int).Exp(c, big.NewInt(int64(pubKey.E)), pubKey.N)
        plain := m.Bytes()

        // 若解出來不足 key size 長度,補 0
        if len(plain) < keySize {
            padding := make([]byte, keySize-len(plain))
            plain = append(padding, plain...)
        }

        // 手動去除 PKCS#1 v1.5 padding
        if plain[0] == 0x00 && plain[1] == 0x01 {
            // 找到 0x00 作為分隔符號
            idx := 2
            for idx < len(plain) && plain[idx] == 0xFF {
                idx++
            }
            if idx < len(plain) && plain[idx] == 0x00 {
                decrypted = append(decrypted, plain[idx+1:]...)
            } else {
                panic(" Padding 格式錯誤")
            }
        } else {
            panic(" 非預期 padding 起始位元")
        }
    }

    fmt.Println(" 解密成功")
    fmt.Println(string(decrypted))
}
// 加密示例:
const crypto = require('crypto');
const axios = require('axios');

// 参数
const param = {
    account: '用户名',
    password: 'password'
};

// JSON 编码
const jsonData = JSON.stringify(param);

// RSA 加密
const publicKey = `-----BEGIN PUBLIC KEY-----
your-public-key-here
-----END PUBLIC KEY-----`;
const buffer = Buffer.from(jsonData, 'utf8');
const encryptedData = crypto.publicEncrypt(publicKey, buffer);

// Base64 编码
const base64String = encryptedData.toString('base64');
console.log("encoded: " + base64String);

// 构建请求参数
const encryptParam = {
    data: base64String
};

// 调用接口
var endpoint = 'your-api-endpoint';
let config = {
  method: 'post',
  maxBodyLength: Infinity,
  url: endpoint,
  headers: {
    'businessAccount': '我方提供的总代唯一标识',
    'site': 'XAgent 商户后台中的代理线路标识',
    'Content-Type': 'application/json'
  },
  data : encryptParam
};

axios.request(config)
  .then((response) => {
      console.log(JSON.stringify(response.data));
  }

// 解密示例:
const crypto = require('crypto');

// ▶ 公鑰 PEM 格式
const publicKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCioAIV3Npz1K8H4L+r4E2DCptp
MJHeD09qzAyh244+dkWRqod7u0G+JMQp2EJ50MEf/zmNsprtRET5HAaM2zG6TAIL
7hQLuUj/cDXLLWS+MvFIhjruR2xj//F7EQNJBPxE/CVcCzZQB7WDlH+aMYeCP+/y
DNINBEMF9KTM+0b6qwIDAQAB
-----END PUBLIC KEY-----`;

// ▶ 加密後的 Base64 字串
const base64EncodedData = `RKgVABPFCq5jaOaR/nWzSkB1CDjtPP7Cs7KKlIs/LYePsTQM12NpaR+ph3TlMkOphSxcO4pchvOI+gwF/XuaYs9LLW/r3ikmgoGF4AzZdKMYx50fAECMM1SZeVBu10KqZx7dF0hDdhBNr+2gLJwletW9bkcBCRRBJG1NrtZUXPGBBV/u+hL2uTSKrzUT46JZkLz2XllZMGtcwYn9ioBF0R1usob7mvVHU74Nktwh+6n2M+lUwJFXD0/GWrJy8stv/QZtUxjrmRkKHK9xGmtzx2oLi1Is/5bsPb5VJaTTfi9u2qWgvwndhDMe2OuGSliaileMW3xwXB3wFCtaC5UoYg==`;

// ▶ 將 base64 字串轉為 Buffer(二進位資料)
const encryptedBuffer = Buffer.from(base64EncodedData, 'base64');

// ▶ RSA 金鑰長度為 1024 bits(每次解密 128 bytes)
const chunkSize = 128;
let decryptedResult = '';

for (let i = 0; i < encryptedBuffer.length; i += chunkSize) {
  const chunk = encryptedBuffer.slice(i, i + chunkSize);

  try {
    const decrypted = crypto.publicDecrypt(
      {
        key: publicKey,
        padding: crypto.constants.RSA_PKCS1_PADDING
      },
      chunk
    );
    decryptedResult += decrypted.toString('utf8');
  } catch (err) {
    console.error(`❌ 解密失敗於區塊 ${i}:`, err.message);
    process.exit(1);
  }
}

console.log('✅ 解密成功:');
console.log(decryptedResult);
  1. API请求方式为:application/json.
  2. 需要将我方提供的2个固定值,添加在请求头中,header key为 site和businessAccount
  3. 您需要将请求体使用公钥 (publicKey) 进行RSA加密,然后把加密后的byte数组转为Base64字符串,最后组装为 {"data":"加密后的Base64字符串"} 格式的json提交给接口
  4. http返回对象中 code为返回码 200=正常,数据在data中.当code不为200时,message为错误信息.
  5. 请求示例
    • 例 XAgent服务器域名为 https://xagent-open-api-uat.ncfc.cc
    • 某个接口的地址为 /v3/single/act
    • 商户端公钥为 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADC...
    • 业务参数json数据 {"field_1":"value_1","field_2":"value_2"}
    • 先对业务参数RSA加密得到密文 Os977UOvhnqN+0xY4YBg95vlBpaKmgWAHnTa...
    • 设置请求头 businessAccount site content-type
    • 对密文base64编码后,构建统一的请求参数实体 {"data":"Os977UOvhnqN+0xY4YBg95vlBpaKmgWAHnTa..."}
    • 使用post提交方式请求 https://xagent-open-api-uat.ncfc.cc/v3/single/act
    • 得到响应数据 {"code":200,"message":"success","data":{"field_a":"value_a","field_b":"value_b"}}
    • 判断code是否为200, 若为200, 则强烈建议商户端对响应数据进行验签, 不是200则不会有签名值, 签名值从响应头 signature 中获取得到
    • 将Body中的数据拼接上公钥得到 {"code":200,"message":"success","data":{"field_a":"value_a","field_b":"value_b"}}MIGfMA0GCSqGSIb3DQEBAQUAA4GNADC...
    • 将拼接好的字符串通过MD5算法得到信息摘要, 再与响应头中的签名值进行比较, 若一致则响应数据是安全的
    • 之后再根据业务参数进行后续的业务逻辑判断

用户注册 / 登录(游戏入口)

接口路径

POST /v3/single/user/login

接口描述

于会员登入游戏时调用此接口, 请求此接口 注册/登录用户, 并同时获取带用户token的URL入口, account必须唯一。

场馆

如需对接 XG 场馆,需要实现此接口。

请求参数

Body:

参数 数据类型 必填 说明
account string Y 玩家帐号 (只支持(2~13)位小写字符或数字) ^[a-z0-9]{2,8}$
password string Y 帐号密码 (不超过20字符)
gameType string Y 游戏类别代码 (参考附录)
isMobile bool Y 是否为手机端
gameCode string Y 游戏编码, 具体游戏的编码
lang string Y 语言类型 (参考附录)
coin string Y 货币类型 (参考附录)
line string N 线路, 部分场馆支持, 如支持的则用这个值填充三方的“线路/站点”体系的必填参数, 不传则为默认值
lobby string N 大厅地址, 部分场馆支持, 比如从场馆游戏页面回跳到商户的页面, 如支持的则用这个值填充三方的“大厅地址/玩家IP”体系的必填参数, 不传则为空
userIP string N 玩家IP, 如支持的则用这个值填充三方的“大厅地址/玩家IP”体系的必填参数, 不传则为空
# input sample
curl --location 'https://xagent-open-api-uat-hh.ncfc.cc/v3/single/user/login' \
--header 'businessAccount: testaccount' \
--header 'site: default' \
--header 'Content-Type: application/json' \
--data '{
"data":"afafCuR6SHzSHFMjOEm44FdKkDPL9KMpeJEVfeolaUFI6RDHRlpL+BDRYGnniSHcEeKycmaKblh0w5fH8xVMTrssep1VmlSuIDd6VZJX+LQgWpjs7ExpAK4aD9O1NqssfcYbgtVUAzuAanbk3AStWtp8MFi7X7jkzm5WeeuCkX8tgXt3CEBfczlegzKiLZoYhdobLXbBaMo0auj8ZxWiQobnP4vL2Q/19sd0kIzkaYgryYpDa4e9PbR2QMwnowXOgfJ5hYh//+YAybgGq1NzHfc4N2AWK3QGFk2H8rHqVMgnyBeTBSE+hWqyXauDfl4yq43rGU+MqekEB0Dz2utY3A=="
}'
// input sample
{
  "account": "68111111",
  "password": "123456",
  "gameType": "xg_electronic",
  "isMobile": true,
  "gameCode": "123",
  "lang": "zh",
  "coin": "cny",
  "line": "default",
  "lobby": "https://h111.xg.io/zh-CN/",
  "userIP": "39.123.252.45"
}

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须 200 错误码,调用成功为200,失败为其他值,见错误码定义
message string 非必须 错误码不为200时,此字段展示错误信息
data object 非必须 具体数据字段,见data数据定义 *备注:* 具体数据内容
// response
{
  "code": 200,
  "data": {
    "url": "https://xxxxxxxx",
    "urlType": 1
  },
  "message": "success"
}

data 数据:

参数 数据类型 必填 说明
urlType int Y url类型 [1:直接跳转的url 2:自动跳转的from表单]
url string Y 进入游戏地址

获取历史注单

接口路径

POST /v3/single/bet/betting

接口描述

查询游戏历史注单的接口 (一次调用最多返回5000条数据)

场馆

如需对接 XG 场馆,需要实现此接口。

请求参数

Body:

参数 数据类型 必填 说明
gameType string Y 游戏类别代码 (参考附录)
startTimestampMs string Y 开始时间戳
endTimestampMs string Y 结束时间戳, 开始与结束时间不能超过12个小时
pageIndex string Y 页码
# input sample
curl --location 'https://xagent-open-api-uat-hh.ncfc.cc/v3/single/bet/betting' \
--header 'businessAccount: testaccount' \
--header 'site: default' \
--header 'Content-Type: application/json' \
--data '{
"data":"SNQQ39/jXIZkpmq16SK4ujLLVNnrbjM4KZ/LV0xJrcwbzyQRN1SuPvOMP5D8jtP5LGrs4cGjzSbD6MHThlUsPSEdplPa9yxnSzKXKGcoGtXgpqNryEY10iCQEJaFN0Cw43+u83jBUle6hPryf5Rj5fn9jLilumaTQYQeRLj1u20="
}'
// input sample
{
  "gameType": "xg_electronic",
  "startTimestampMs": "1735660800000",
  "endTimestampMs": "1735704000000",
  "pageIndex": "1"
}

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须 错误码,调用成功为200,失败为其他值,见错误码定义
message string 非必须 错误码不为200时,此字段展示错误信息
data String 非必须 具体数据字段,见下方data数据定义

data数据:

参数 数据类型 必填 说明
pageIndex string Y 页码
pageTotal string Y 总页数
bets BetItemModel[] Y 历史注单集合
// response
{
  "code": 200,
  "message": "success",
  "data": {
    "pageIndex": "1",
    "pageTotal": "1",
    "bets": [
      {
        "betNoVenue": "BET202501010001",
        "account": "testuser001",
        "coin": "usd",
        "gameType": "pg_electronic",
        "gameCode": "demo_001",
        "betAmount": 50.00,
        "validAmount": 50.00,
        "netWinningAmount": -20.00,
        "betTimestampMs": 1735661200000,
        "settlementTimestampMs": 1735664800000,
        "isTrial": false,
        "betState": 1,
        "settlementState": 4,
        "extend": {
          "roundId": "R20250101001",
          "tableId": "T01"
        }
      }
    ]
  }
}

BetItemModel:

参数 数据类型 必填 说明
betNoVenue string Y 场馆注单号
account string Y 玩家账号
coin string Y 币种 (参考附录)
gameType string Y 游戏类别 (参考附录)
gameCode string Y 游戏编码
betAmount decimal Y 投注金额
validAmount decimal Y 有效投注
netWinningAmount decimal Y 净输赢
betTimestampMs long Y 下注时间戳
settlementTimestampMs long Y 结算时间戳
isTrial bool Y 是否试玩注单
betState int Y 注单状态 (参考附录)
settlementState int Y 结算状态 (参考附录)
extend object Y 注单扩展信息, 不同类别的注单的扩展信息有所差异 (参考附录)
site string Y XAgent商户后台中的代理线路标识

获取游戏列表

接口路径

POST /v3/single/game/list-gamecode

接口描述

获取游戏列表的接口

场馆

如需对接 XG 场馆,需要实现此接口。

请求参数

Body:

参数 数据类型 必填 说明
gameType string Y 游戏类别代码 (参考附录)
# input sample
curl --location 'https://xagent-open-api-uat-hh.ncfc.cc/v3/single/game/list-gamecode' \
--header 'businessAccount: testaccount' \
--header 'site: default' \
--header 'Content-Type: application/json' \
--data '{
"data":"G4HEngPlnZ85x449qYvOsqeV2KjlgXTAjmWF2tUjRFSuOiVMvtFnxvEIwqEz7a1QCUrqaBPKWCOnAUs+OAkv61vApOhI9gZ9pU1o5fy7V+g0Q9Y4WYlD48chxr5KwFI5E9peTJaUaje+n50YBOgFhzKhfN3muQeJbEMIdYWB42A="
}'
// input sample
{
  "gameType": "pg_electronic"
}

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须 错误码,调用成功为200,失败为其他值,见错误码定义
message string 必须 错误码不为200时,此字段展示错误信息
data GameCodeList[] 必须 游戏集合 (参考附录)
// response
{
  "code": 200,
  "message": "success",
  "data": [
    {
      "gameType": "pg_electronic",
      "gameCode": "demo_001",
      "gameNameEnus": "Demo Slot 1",
      "gameNamePtbr": "Demo Slot 1 PT",
      "gameNameZhcn": "示範老虎機 1",
      "isEnable": true,
      "logoUrl": "https://demo.example.com/logo/demo_001.png",
      "remark": "熱門遊戲"
    }
  ]
}

获取游戏类别列表

接口路径

POST /v3/single/game/list-gametype

接口描述

获取游戏类别列表的接口

场馆:

如需对接 XG 场馆,需要实现此接口。

请求参数

Body:

参数 数据类型 必填 说明
coin string Y 货币类型 (参考附录)
# input sample
curl --location 'https://xagent-open-api-uat-hh.ncfc.cc/v3/single/game/list-gametype' \
--header 'businessAccount: testaccount' \
--header 'site: default' \
--header 'Content-Type: application/json' \
--data '{
"data":"JWBtLQ47oLSCHOYr+UaXwMZEdkw/s3moTstrFBFAwzukvW0lh57aeVXEqjLjMXxHOnObn/QmnMlUUygBSgCSlRY9J5T6HevQ2F5TmM4ESlRN1v1WeF++RKLSkuk45aOl2XywB9DcpVV8O9aorsByuhZ4MSUgo8fhePty3EnFN3w="
}'
// input sample
{
  "coin": "usd"
}

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须 错误码,调用成功为200,失败为其他值,见错误码定义
message string 必须 错误码不为200时,此字段展示错误信息
data GameTypeList[] 必须 游戏类别集合 (数据结构参考附录)
// response
{
  "code": 200,
  "message": "success",
  "data": [
    {
      "gameType": "pg_electronic",
      "name": "PocketGames 電子",
      "isEnable": true
    }
  ]
}

PG游戏馆会员踢线接口

接口路径

POST /v3/single/game/pgKickOut

接口描述

PG游戏专用,踢会员下线接口,调用后会员会被强制下线处理

请求参数

Body:

参数 数据类型 必填 说明
account string Y 玩家帐号
gameType string Y 游戏类别代码 (参考附录)
# input sample
curl --location 'https://xagent-open-api-uat-hh.ncfc.cc/v3/single/game/pgKickOut' \
--header 'businessAccount: testaccount' \
--header 'site: default' \
--header 'Content-Type: application/json' \
--data '{
"data":"jAmvMbJ4ZgPt6GDnp9x0jAirH2qr3RiVLPXqm5xX+t2LzuY6lx9FttA7pA/iRksJXpvRuDfJouKSo3VtcjKkeV6qg39Ft1b8+yVdnZNjwC+peir7AMUbcx/61Unx6zTqt1K0n4RX89NGd7Y/kt9gYjShZQLtceqc0NR1FypJe34="
}'
// input sample
{
  "account": "testuser001",
  "gameType": "pg_electronic"
}

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须 错误码,调用成功为200,失败为其他值,见错误码定义
message string 必须 错误码不为200时,此字段展示错误信息
// response
{
  "code": 200,
  "message": "success"
}

PG游戏馆查看订单详情

接口路径

POST /v3/single/game/pgSnapshot

接口描述

PG游戏专用,可查看某个会员某个PG游戏订单的详细游戏截图详情,调用接口后以URL的形式返回,直接访问即可查看

请求参数

Body:

参数 数据类型 必填 说明
account string Y 玩家帐号
gameType string Y 游戏类别代码 (参考附录)
gameCode string Y 游戏代码 (参考附录)
orderNo string Y 订单编号
# input sample
curl --location 'https://xagent-open-api-uat-hh.ncfc.cc/v3/single/game/pgSnapshot' \
--header 'businessAccount: testaccount' \
--header 'site: default' \
--header 'Content-Type: application/json' \
--data '{
"data":"Z2R6/LFNXDvne7A2hXDx8WF6rTXZmihVIu1ZtoI9KIykueP5DFRX4VWeP0CZUXVxTLTlrx6Vr8xGk7O/G8RtEhqjDDx4Zka3wN0t19rIRsJyB3xMdBxBqHW8M6qk9Ou7IcFt+S0/PnkceEZjPdEDRWBs5bNQwSZ6x+mjKNSnA88="
}'
// input sample
{
  "account": "testuser001",
  "gameType": "pg_electronic",
  "gameCode": "demo_001",
  "orderNo": "ORDER20250101001"
}

返回数据

名称 类型 是否必须 默认值 备注 其他信息
code number 必须 错误码,调用成功为200,失败为其他值,见错误码定义
message string 必须 错误码不为200时,此字段展示错误信息
data string 必须 游戏订单详情页面的URL,直接访问即可查看
// response
{
  "code": 200,
  "message": "success",
  "data": "https://demo.example.com/snapshot/ORDER20250101001"
}

您需要提供的接口及要求说明

接入准备与说明

请求参数示例

统一的请求数据结构:

{ "data":"具体某个接口的参数为json字符串并进行RSA加密" }

我方在请求前, 会根据不同接口要求的参数, 组装参数, 然后将参数对象进行json序列化, 然后赋值给data字段。

data字段加密规则:根据RSA公钥进行解密即可

返回参数示例

您需要包装的响应参数为Json格式的数据,响应Json数据模板如下:

统一的响应数据结构:

{ "code": 200, "message":"错误消息", "data":"具体某个接口的参数为json 无需加密直接返回" }

需要注意: 此处您还需要组装包装一个参数到responseHeader里 具体规则如下:

responseHeader

Key:signature

value:您包装的所有返回结果的JSON (data不须再字符串化 见示例) + publicKey 然后MD5加密

例如:

所有返回结果JSON = {"code":200,"message":"success","data":{"account":"abc123","balance":100}}

则签名(signature)的源串为:

"{"code":200,"message":"success","data":{"account":"abc123","balance":100}}aaaa12345678"

得到响应后应,我方会先判断code是否为200成功, 然后再获取data字段数据

整体流程:

a) 我方会使用privateKey将数据加密好,放置在data,传入到您的接口中来

b) 您在接口中,接收到我方传过来的data参数,要先进行base64解码 , 然后根据我方提供的publicKey,使用RSA解密工具,对data进行解密

c) 我方传入的具体参数包装在data中,直接拿data的数据即可使用

d) 您在包装您的接口返回值时,data字段无需加密,直接返回原始值即可

e) 返回值组装好后, 根据包装好的返回值, signature字段 , 放置在responseHeader里返回响应

参数解密示例请参照上方加解密示例说明,根据各语言切换

  1. 本API版本接入前,我方会提供给您一份 唯一标识 和 publicKey 和 site(默认)
  2. 此处说明,为我方调用您的接口,需要您准备的事项
  3. 请求方式:POST
  4. 请求参数:格式为json, content-type=application/json
  5. 下方接口及说明中仅展示data字段所需要的数据

总流程示意

img.png

下注結算相关流程示意

正常下注 -> 結算

img.png

取消投注

img.png

取消已結算投注

img.png

活动派彩 / 人工加扣款及撤销调帐

每次調整金額都會生成新的帳單號,但只有取消帳單 /cancelbill 才會對應到原來的 billNoVenues

adjustbill 主要用在 活动派彩 / 人工加扣款 这种「不依附于下注」的情况。

例如:

活动派彩:办活动送玩家奖金(不一定有下注)。

adjustbill → 加钱给玩家,生成 billNoVenues。

人工扣款:发现玩家异常,人工调整余额。

adjustbill → 扣钱(负数金额),生成 billNoVenues。

撤销调帐:如果这笔派彩/调整发错了,要撤销。

cancelbill → 依据原本的 billNoVenues 撤销。

img.png

1. **获取玩家信息接口(/getaccountinfo)

参数 数据类型 必填 说明
account string Y 玩家帐号
coin string Y 货币类型 (参考附录)
// input sample
{
  "account": "xxxxxxxx",
  "coin": "cny"
}
字段 数据类型 必填 说明
account string Y 玩家帐号
accountName string N 玩家名称
coin string Y 货币类型 (参考附录)
balance decimal Y 玩家实时余额
// response
{
  "account": "xxxxxxxx",
  "accountName": "xxxxxxxx",
  "balance": 100,
  "coin": "cny"
}

2. **下注申请(/applycreatebet)

img.png

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
bets ApplyCreateBetItemV3Model[] Y 注单信息集合, 数据结构参考附录
// input sample
{
  "gameType": "pg_electronic",
  "account": "xxxxxxxx",
  "coin": "cny",
  "bets": [
    {
      "betAmount": 0,
      "betNoVenue": "1982808557020184581:1982808557020184582",
      "betTimestampMs": 1759232322918,
      "gameCode": "135",
      "isTrial": false
    }
  ]
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "gameType": "pg_electronic",
  "account": "xxxxxxxx",
  "coin": "idr",
  "balance": 100,
  "results": {
    "betType": 1,
    "betNoVenue": "1982808557020184581:1982808557020184582",
    "betNoMerchant": "10041162403",
    "account": "xxxxxxxx",
    "coin": "cny",
    "betState": 1,
    "settlementState": 1,
    "operateState": 1,
    "resultDesc": "Success"
  }
}

3. 获取注单信息(/getbet)

参数 数据类型 必填 说明
gameType string Y 游戏类别
betNoVenues string[] Y 注单号(场馆端的)集合
// input sample
{
  "betNoVenues": [
    "Nextspin_10177_56543398_202509200014361341271739"
  ],
  "gameType": "nextsp_electronic"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
betMap map Y 注单信息字典, Key为注单号, Value为对应的注单信息; 若注单信息不存在, 返回空 Map {} 即可
// response
{
  "betMap": {},
  "gameType": "nextsp_electronic"
}

4. 更新初始注单(/updatebet)

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
bets ApplyCreateBetItemV3Model[] Y 注单信息集合, 数据结构参考附录
// input sample
{
  "account": "xxxxxxxx",
  "bets": [
    {
      "betAmount": 100,
      "betNoVenue": "1557823605557955143",
      "extend": {
        "details": [
          {
            "betState": "已确认",
            "desc": "Under 2.5",
            "detailID": "1557823605557955399",
            "league": "England Premier League",
            "match": "2025-09-29 19:00:00",
            "odds": 1.9,
            "proj": "1007_Over/Under",
            "teamA": "Everton FC ",
            "teamB": "West Ham United"
          },
          {
            "betState": "已确认",
            "desc": "Under 3",
            "detailID": "1557823605557955655",
            "league": "Portugal Primeira Liga",
            "match": "2025-09-29 19:00:00",
            "odds": 1.89,
            "proj": "1007_Over/Under",
            "teamA": "FC Arouca ",
            "teamB": "FC Porto"
          },
          {
            "betState": "已结算",
            "desc": "Besiktas JK -1/1.5",
            "detailID": "1557823605557955911",
            "league": "Turkey Super Lig",
            "match": "2025-09-29 17:00:00",
            "odds": 1.89,
            "proj": "1000_Handicap",
            "teamA": "Besiktas JK ",
            "teamB": "Kocaelispor"
          }
        ],
        "isParlay": true,
        "odds": 1.9,
        "oddsType": "Europe",
        "playType": "1",
        "sportBetType": "Parlay"
      },
      "isTrial": false
    }
  ],
  "coin": "cny",
  "gameType": "fb_sport"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "fb_sport",
  "results": [
    {
      "account": "xxxxxxxx",
      "betNoMerchant": "1972696774055727104",
      "betNoVenue": "1557823605557955143",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

5. 取消初始注单(/cancelbet)

img.png

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
betNoVenues string[] Y 注单号(场馆端的)集合
// input sample
{
  "account": "xxxxxxxx",
  "betNoVenues": [
    "1759223994983"
  ],
  "coin": "cny",
  "gameType": "by_sport"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录

关于注单与账单的关系, 请查看附录中的介绍

// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "by_sport",
  "results": [
    {
      "account": "xxxxxxxx",
      "betNoMerchant": "1759223994983",
      "betNoVenue": "",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

6. 回滚已取消注单(/backcancelbet)

img.png

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
betNoVenues string[] Y 注单号(场馆端的)集合
// input sample
{
  "account": "xxxxxxxx",
  "betNoVenues": [
    "1759223994983"
  ],
  "coin": "cny",
  "gameType": "by_sport"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "by_sport",
  "results": [
    {
      "account": "xxxxxxxx",
      "betNoMerchant": "1972954549079937024",
      "betNoVenue": "1759223994983",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

7. **结算初始注单(/settlementbet)

img.png

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
bets SettlementBetItemV3Model[] Y 注单信息集合, 数据结构参考附录
// input sample
{
  "account": "xxxxxxxx",
  "bets": [
    {
      "betAmount": 0,
      "betNoVenue": "JiLiGaming_10177_56367276_586706132379648082",
      "betTimestampMs": 0,
      "extend": {},
      "isCreateAndSettlement": false,
      "isTrial": false,
      "prizeAmount": 0,
      "settlementAmountType": 1,
      "settlementTimestampMs": 1759233707753,
      "validAmount": 1.3
    }
  ],
  "coin": "cny",
  "gameType": "jili_electronic"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "gameType": "jili_electronic",
  "account": "xxxxxxxx",
  "coin": "cny",
  "balance": 36.46,
  "results": [
    {
      "betType": 1,
      "betNoVenue": "JiLiGaming_10177_56367276_586706132379648082",
      "betNoMerchant": "129963JiLiGaming_10177_56367276_586706132379648082",
      "account": "xxxxxxxx",
      "coin": "cny",
      "betState": 1,
      "settlementState": 1,
      "operateState": 1,
      "resultDesc": ""
    }
  ]
}

8. 重新结算注单(/resettlementbet)

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
bets SettlementBetItemV3Model[] Y 注单信息集合, 数据结构参考附录
// input sample
{
  "account": "xxxxxxxx",
  "bets": [
    {
      "betNoVenue": "G00012c1fe85c",
      "extend": {},
      "prizeAmount": 0,
      "settlementAmountType": 1,
      "settlementTimestampMs": 1759041579521
    }
  ],
  "coin": "cny",
  "gameType": "art_electronic"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "art_electronic",
  "results": [
    {
      "account": "xxxxxxxx",
      "betNoVenue": "G00012c1fe85c",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

9. 回滚已结算注单(/backsettlementbet)

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
betNoVenues string[] Y 注单号(场馆端的)集合
// input sample
{
  "account": "xxxxxxxx",
  "betNoVenues": [
    "1759224019772"
  ],
  "coin": "cny",
  "gameType": "by_sport"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 0,
  "coin": "cny",
  "gameType": "by_sport",
  "results": [
    {
      "account": "xxxxxxxx",
      "betNoMerchant": "1972954652744851456",
      "betNoVenue": "1759224019772",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

10. 取消已结算注单(/cancelsettlementbet)

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
betNoVenues string[] Y 注单号(场馆端的)集合
// input sample
{
  "account": "xxxxxxxx",
  "betNoVenues": [
    "1759224019772"
  ],
  "coin": "cny",
  "gameType": "by_sport"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBetItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "by_sport",
  "results": [
    {
      "account": "xxxxxxxx",
      "betNoMerchant": "1972954652744851456",
      "betNoVenue": "1759224019772",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

11. **余额调整(/adjustbill)

img.png

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
bills SingleBillItemV3Model[] Y 调整信息集合, 数据结构参考附录
// input sample
{
  "account": "xxxxxxxx",
  "bills": [
    {
      "account": "xxxxxxxx",
      "amount": 100,
      "billNoVenue": "72011641",
      "coin": "cny",
      "reason": "SETTLED-WagerId-2882367827-ActionId-122887905",
      "timestampMs": 1759234109445
    }
  ],
  "coin": "cny",
  "gameType": "ap_sport"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBillItemV3Result[] Y 注单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "ap_sport",
  "results": [
    {
      "account": "xxxxxxxx",
      "billNoMerchant": "1972996972647260160",
      "billNoVenue": "72011641",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

12. 取消账单(/cancelbill)

参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
billNoVenues string[] Y 账单号集合(场馆端)
// input sample
{
  "account": "xxxxxxxx",
  "billNoVenues": [
    "214791496971-900-948719984-2209517006"
  ],
  "coin": "cny",
  "gameType": "ly_brand"
}
参数 数据类型 必填 说明
gameType string Y 游戏类别
account string Y 玩家帐号
coin string Y 货币类型(参考附录)
balance decimal Y 处理成功后的玩家实时余额
results SingleBillItemV3Result[] Y 账单处理结果集合, 数据结构参考附录
// response
{
  "account": "xxxxxxxx",
  "balance": 100,
  "coin": "cny",
  "gameType": "ly_brand",
  "results": [
    {
      "account": "xxxxxxxx",
      "billNoMerchant": "1972703317955915776",
      "billNoVenue": "214791496971-900-948719984-2209517006",
      "coin": "cny",
      "operateState": 1
    }
  ]
}

附录

ApplyCreateBetItemV3Model 单一钱包下注注单模型

参数 数据类型 说明
betNoVenue string 注单号 - 第三方游戏馆的
gameCode string 游戏编码
betAmount decimal 下注金额,2位小数
betTimestampMs long 下注时间戳(毫秒级)
isTrial bool 是否为试玩的注单
extend object 棋牌注单的详细数据,betType=1时有数据

SettlementBetItemV3Model 单一钱包结算注单模型

参数 数据类型 说明
betNoVenue string 注单号 - 第三方游戏馆的
settlementAmountType decimal 结算时, 以哪个金额来计算[1:PrizeAmount 2:NetWinningAmount]
netWinningAmount decimal 净输赢金额, 4位小数 此处注意:每个游戏馆支持的取整逻辑不一样, 此处为XAgent接口返回的小数位数
prizeAmount decimal 中奖金额/派奖金额(含下注金额),4位小数 此处注意:每个游戏馆支持的取整逻辑不一样, 此处为XAgent接口返回的小数位数
settlementTimestampMs long 结算时间戳(毫秒级)
extend object 棋牌注单的详细数据,betType=1时有数据
isCreateAndSettlement bool 是否为下注并结算,只有结算接口可能出现true,一般电子游艺类型的注单,不会先下注再结算,而是直接通知结果的
gameCode string 游戏编码, isCreateAndSettlement=true才有值
betAmount decimal 下注金额,2位小数, isCreateAndSettlement=true才有值
betTimestampMs long 下注时间戳(毫秒级), isCreateAndSettlement=true才有值
isTrial bool 是否为试玩的注单, isCreateAndSettlement=true才有值
validAmount decimal 有效投注金额(部分场馆支持)

GetBetItemV3Model 单一钱包获取注单模型

参数 数据类型 说明
betNoVenue string 注单号 - 第三方游戏馆的
betNoMerchant string 注单号 - 商户端的
account string 玩家帐号
coin string 货币类型 (参考附录)
gameCode string 游戏编码
betState int 注单状态 (参考附录)
betAmount decimal 下注金额,2位小数
betTimestampMs long 下注时间戳(毫秒级)
isTrial bool 是否为试玩的注单
extend object 棋牌注单的详细数据,betType=1时有数据
settlementState int 结算状态[1:未结算/未开奖/等待开奖 3:赢/中奖 4:输/未中奖 5:和/平局], 这个字段对于部分游戏仅供参考, 所以请根据下注和结算的金额计算出的净输赢最终确定输赢结果
settlementTimestampMs long 结算时间戳(毫秒级)
netWinningAmount decimal 净输赢金额, 2位小数

关于注单信息中出现的金额特别说明

  • BetAmount 下注金额 只能是正数(大于0)
  • PrizeAmount 派彩金额 含下注金额, 任何情况都不会为负数
  • NetWinningAmount 净输赢 减去下注金额的最终输赢, 正数负数都有可能, 若净输赢为负数, 其绝对值必定小于等于下注金额
  • 下注10块, 没中奖: BetAmount=10; PrizeAmount=0; NetWinningAmount=-10;
  • 下注10块, 中了8块: BetAmount=10; PrizeAmount=8; NetWinningAmount=-2;
  • 下注10块, 中了10块: BetAmount=10; PrizeAmount=10; NetWinningAmount=0;
  • 下注10块, 中了14块: BetAmount=10; PrizeAmount=14; NetWinningAmount=4;

    这三个金额必定满足恒等式 BetAmount = PrizeAmount - NetWinningAmount;

  • 结算时候因为不同游戏馆接口的差异, 不一定每次都是三个字段都有值, 但是一定会按满足这个公式定义的情况, 提供到商户, 以供商户进行结算, 例如SettlementAmountType指明使用PrizeAmount进行结算, 则使用PrizeAmount进行结算即可, 不用关心NetWinningAmount的值.

    对于商户结算, 用 PrizeAmount 和 NetWinningAmount 这两个数据即可完成结算。

  • 对于先下注后结算的注单, 也就是下注时候已经扣过下注金额的, 此时结算的就只是根据中奖情况返还余额而已。

    1. 按 PrizeAmount 计算则为: 返还金额 = PrizeAmount, 如果大于0则增加余额, 否则必定为0
    2. 按 NetWinningAmount 计算则为: 返还金额 = BetAmount + NetWinningAmount, 如果大于0则增加余额, 否则必定为0
  • 对于同时下注并结算的, 此时BetAmount有值, PrizeAmount和NetWinningAmount也必定会有一个或者两个都有值。

    这个就看商户那边是如何记账, 如果一笔扣一笔加的, 则与先下注后结算的没啥区别。

    • 如果按一笔记录的(可能是增加可能是减少的)
    • 下注金额肯定没得差别, 就是最终加减款有点差别,
    1. 按 PrizeAmount 计算则为: 最终加减款 = PrizeAmount - BetAmount, 如果大于0则增加余额, 小于0就是减少, 也有为0的情况
    2. 按 NetWinningAmount 计算则为: 终加减款 = NetWinningAmount, 如果大于0则增加余额, 小于0就是减少, 也有为0的情况

一定要牢记, 这三个金额必定满足恒等式 BetAmount = PrizeAmount - NetWinningAmount 。必须是满足这个等式的才是正确的。

SingleBetItemV3Result 单一钱包注单处理结果模型

参数 数据类型 必填 说明
betType int Y 注单类别 [0:棋牌 1:电子游艺 2:真人视讯 3:彩票 4:竞技体育 5:股票]
betNoVenue string Y 注单号 - 第三方游戏馆的
betNoMerchant string Y 注单号 - 商户的
account string Y 玩家帐号
coin string Y 货币类型 (参考附录)
betState int Y 注单状态 (参考附录)
settlementState int Y 结算状态[1:未结算/未开奖/等待开奖 3:赢/中奖 4:输/未中奖 5:和/平局]
operateState int Y 操作的结果, 参考附录注单处理结果
resultDesc string Y 错误描述

BrandBetItemExtend 棋牌注单的详细数据

参数 数据类型 必填 说明
roundNo string Y 游戏局号

ElectronicBetItemExtend 电子游艺注单的详细数据

参数 数据类型 必填 说明
暂无字段, 预留类型

LiveBetItemExtend 真人注单的详细数据

参数 数据类型 必填 说明
tableNo string Y 桌子编号

LotteryBetItemExtend 彩票注单的详细数据

参数 数据类型 必填 说明
roundNo string Y 期号

SportBetItemExtend 体育注单的详细数据

参数 数据类型 必填 说明
playType string Y 体育比赛类型, 例如: 足球/篮球等
sportBetType string Y 体育注单类型[0:普通投注/普通注单/非滚球 1:混合投注/过关注单/混合非滚球 2:普通滚球/普通走地 3:混合滚球/混合走地 9:其它]
oddsType int Y 赔率类型 [1:欧洲盘 2:香港盘 3:马来盘 4:印尼盘 ]
odds decimal Y 赔率
matchDate string Y 比赛时间
isParlay bool Y 是否为混合过关,否则为普通投注
details SportBetItemDetail[] Y 体育注单比赛信息集合

StockBetItemExtend 股票注单的详细数据

参数 数据类型 必填 说明
gameType string Y 游戏类别
stockCode string Y 股票编码
stockName string Y 股票名称
transDirection string Y 交易方向
transPrice decimal Y 成交价格
transNumber decimal Y 成交数量
transFee decimal Y 手续费
leverage decimal Y 杠杆

SportBetItemDetail 体育注单具体的比赛信息实体

参数 数据类型 必填 说明
league string Y 联赛名称
teamA string Y 主队名称
teamB string Y 客队名称
proj string Y 赌注项目, 若为混合过关,则是每场比赛的, 比如让球/独赢/大小/总入球等等
desc string Y 比赛内容, 若为混合过关,则是每场比赛的, 比如主队让球1个/总入球4个/客队独赢等等, 混合过关的时候,这个才有效
score string Y 最终比分(格式必须为 A:B(主队得分:客队得分) 或为空), 若为混合过关,则是每场比赛的
gq string Y 滚球(走地)下注时的比分,仅限走地, 若为混合过关,则是每场比赛的
betState string Y 注单状态, 若为混合过关,则是每场比赛的, 注单状态的文字
odds decimal Y 赔率, 若为混合过关,则是每场比赛的
match string Y 比赛时间(yyyy-MM-dd HH:mm:ss), 若为混合过关,则是每场比赛的

SingleBillItemV3Model 单一钱包账单模型

参数 数据类型 必填 说明
billNoVenue string Y 账单号, 第三方游戏馆的
account string Y 会员账号
coin string Y 币种
amount decimal Y 调整金额, 正数为增加, 负数为减少
timestampMs long Y 调整时间戳(毫秒级)
reason string Y 调整原因
remark string Y 备注信息

SingleBillItemV3Result 单一钱包账单处理结果模型

参数 数据类型 必填 说明
billNoVenue string Y 账单号, 第三方游戏馆的
billNoMerchant string Y 账单号, 下游商户的
account string Y 会员账号
coin string Y 币种
operateState int Y 操作的结果, 参考BillOperateStates账单处理结果

GameType

此处只展示部分GameType,仅作参考,详细请移步查看XAgent Game List 文档, 后续增加了会再文档中体现, 或者通过商户后台查看最新数据

编码 名称 说明
xg_electronic XG电子
ag_electronic AsiaGaming电子
ag_live AsiaGaming真人
ax_electronic Aviatrix电子
bng_electronic BNG电子
cr_sport 皇冠体育
dg_live DreamGaming真人
evo_live Evolution真人
fb_sport FB体育
fg_brand FunGaming棋牌
imty_sport InplayMatrix体育
jdb_electronic 夺宝电子
ky_brand 开元棋牌
mg_electronic MicroGaming电子
nlc_electronic NoLimitCity电子
ob_live DB(原OB)真人
obty_sport 熊猫体育
pg_electronic PocketGames电子
png_electronic PlayNGo电子
pp_electronic PragmaticPlay电子
pp_live PragmaticPlay真人
rt_electronic Red Tiger电子
sc_lottery SHI CAI彩票
sexy_live Sexy真人
shb_sport 沙巴体育

Coin 币种

后续增加了会再文档中体现, 或者通过商户后台查看最新数据

编码 名称 说明
aud 澳大利亚元
bdt 孟加拉塔卡
brl 巴西雷亚尔
cny 人民币
eur 欧元
hkd 港元
idr 印尼盾
inr 印度卢比
irr 伊朗里亚尔
jpy 日元
khr 柬埔寨瑞尔
krw 韩元
mmk 缅元
myr 马来西亚令吉
ngn 尼日利亚奈拉
npr 尼泊尔卢比
php 菲律宾比索
pkr 巴基斯坦卢比
pyg 巴拉圭爪拉尼
rub 俄罗斯卢布
sgd 新加坡元
thb 泰铢
twd 新台币
usd 美元
vnd 越南盾
usdt USDT

Lang 语言

后续增加了会再文档中体现, 或者通过商户后台查看最新数据

编码 名称 说明
en 英文
hi 印地语
id 印尼语
ja 日语
ko 韩文
pt 葡萄牙文
ta 泰米尔语
th 泰语
vi 越南语
zh 中文
zh_hant 中文-繁体
es 西班牙语

BetOperateStates 注单处理结果

编码 名称 说明
0 未知, 未赋值情况下的默认值或者不确定是否成功的任意情况, 也是失败
1 成功
2 失败
3 余额不足
4 玩家不存在
20 注单不存在
21 注单重复
22 注单已结算
23 注单已取消
24 注单已回滚

BillOperateStates 账单处理结果

编码 名称 说明
0 未知, 未赋值情况下的默认值或者不确定是否成功的任意情况, 也是失败
1 成功
2 失败
3 余额不足
4 玩家不存在
20 账单不存在
21 账单重复
23 账单已取消

VenueResultCodes 常见错误码

编码 名称 说明
200 成功, 除此之外全部都为失败
400 参数错误
9*** 内部服务错误
9999 包含任何失败的情况
3000 XAgent-系统维护
3001 XAgent-IP白名单限制
3002 商户未开通这个游戏馆或者币种
5000 游戏馆系统维护
5001 游戏馆IP白名单限制
5002 游戏馆不可访问
5003 游戏馆系统繁忙
5004 不可直接进入大厅
5005 订单处理中
5006 余额不足
7000 商户端系统维护
7001 商户端IP白名单限制
7003 商户端系统繁忙
7004 商户端账号不存在

BetStates 注单状态

编码 名称 转账模式 单一钱包 说明
0 未知状态 可用 可用
1 注单已被接受,有效 可用 可用
2 拒绝/投注失败 可用 不可用
3 撤销/注销/第三方强制撤单/退款 可用 不可用
4 注单已被取消 可用 可用
5 回收/出售 可用 不可用

GameTypeList 游戏类别实体

参数 数据类型 必填 说明
gameType string Y 游戏类别
name string Y 对应的中文名
isEnable bool Y 是否启用 true / false

GameCodeList 游戏列表实体

参数 数据类型 必填 说明
gameType string Y 游戏类别
gameCode string Y 游戏编码, 具体游戏的编码
gameNameEnus string Y 游戏英文名称
gameNamePtbr string Y 游戏葡萄牙语名称
gameNameZhcn string Y 游戏中文名称
isEnable bool Y 是否启用 true / false
logoUrl string Y 游戏logo
remark string Y 备注