---
# System prepended metadata

title: Untitled

---

api

* 必要物品
 1. signkey
 2. encryptKey
 3. encryptIV
 4. APIURL
 

---

 
* 加密範例(php版)(以取得遊戲連結為範例)
```php
$signKey    = '{signkey}';
$encryptKey = base64_decode('{encryptKey}');
$encryptIV  = base64_decode('{encryptIV}');
// 代理識別碼
$agentCode  = 1234;
// GAME API 請求網址
$apiurl     = 'http://{APIURL}/api/game';

$requestData = [
    'ns'       => 'player',
    'work'     => 'forwardRecreation',
    'version'  => '0.0.1',
    'data' => [
        'playerAccount' => 'test5566player',
        'homeUrl'       => 'www.nba.com',
        'gameRuleUrl'   => 'www.nba.com'
    ]
];
$content = json_encode($requestData);

// 開始加密
$dataLen = strlen($content);
$remainder = $dataLen % 16;
if ($remainder != 0) {
    $content .= str_repeat(chr(0), 16 - $remainder);
}
$rawData = openssl_encrypt($content, 'AES-256-CBC', $encryptKey, OPENSSL_ZERO_PADDING, $encryptIV);

// 製作簽名資料
$sign = openssl_digest($rawData . $signKey, 'sha512');

// 送出請求 header 需包含 Agent-Code 及 Sign
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL                 => $apiurl,
    CURLOPT_HEADER              => false,
    CURLOPT_BINARYTRANSFER      => true,
    CURLOPT_RETURNTRANSFER      => true,
    CURLOPT_HTTPHEADER          =>  [
        'Agent-Code: ' . $agentCode,
        'Sign: '. $sign,
    ],
    CURLOPT_POST                => true,
    CURLOPT_POSTFIELDS          => $rawData
]);
$rawData = curl_exec($ch);
curl_close($ch);

```
* 解密範例(php版) - api回應的資料作解密
```php

$signKey    = '{signkey}';
$encryptKey = base64_decode('{encryptKey}');
$encryptIV  = base64_decode('{encryptIV}');

// 從 response header 中取得簽名檔
$originSign = '我是簽名檔';  //API回傳header中的Sign
$rawData = '我是加密後的資料';//API回傳的已加密資料

// 驗證簽名檔是否正確
$sign = openssl_digest($rawData . $signKey, 'sha512');
if ($sign != $originSign) {
    throw new Exceptin('簽名檔驗證錯誤');
}

// 執行解密
$decryptData = trim(openssl_decrypt($rawData, 'AES-256-CBC', 
       $encryptKey, OPENSSL_ZERO_PADDING, $encryptIV));
$responseData = json_decode($decryptData, true);
```
* 解密後結果
```
================== 最後結果 ==================

最後 $responseData 的值應如下
[
    'errorCode' => 0,
    'message'   => 'OK',
    'url'       => 'http://play.url?authToken=xxxxxxxxxxxxxxxxxxxx'
]
```




---

* 更多
歡迎提供其他版本的範例 : java / C# / golang / node-js 版



---


* 錯誤代碼

| errorCode| message | description | need you |
| -------- | --------| -------- | -------- |
| 0        | OK      | nothing wrong     | nothing |
| 1001     |         | 簽名驗證失敗 | 確認Sign簽名是否正確 |
| 1002     |         | server解密失敗 | 確認加密金鑰是否正確加密 |
| 1004     |         | agent key disabled | 請聯絡custom support|
| 2001     |         | ns field wrong format | 檢查ns欄位是否合法 |
| 2002     |         | work field | 檢查欄位的值內容是否合法 |
| 2003     |         | version field | 檢查欄位的值內容是否為0.0.1 |
| 2004     |         | playerAccount field | 檢查欄位的值內容是否合法 &長度是否合法 |
| 2005     |         | url format | 檢查欄位的值內容是否合法 |
| 2006     |         | nickname format | 檢查欄位的值內容是否合法 |
| 2007     |         | email format | 檢查欄位的值內容是否合法(RFC2822) |
| 2008     |         | billnumber format | 檢查欄位的值內容是否合法 , prefix is agentCode |
| 2009     |         | amount format | 檢查欄位的值內容是否合法, deposit only allow integer / withdraw allow integer & double |
| 2010     |         | time format | 檢查欄位的值內容是否合法, Y-m-d H:i:s |
| 3001     |         | not exist account | create first and then use it |
| 3002     |         | account already exist | duplicate create |
| 3003     |         | logout player failed | |
| 3004     |         | trial account over limit | |
| 4001     |         | transfer type not valid ||
| 4002 | | transfer balance should not equal zero. | check transfer balance |
| 4003 | | lookup transfer record failed | check again or contact customer support | 
| 4004 | | player balance insufficient | check player balance |
| 4005 | | transfer failed | try again or contact customer support |
| 5001 | | start page | vaild : integer and >0 |
| 5002 | | items each page | vaild: 100~2000 |
| 6001 | | starttime not over or equal endtime | check starttime |
| 6002 | | date only allow today - 30 days  | check date|
| 6003 | | time format range limit 24 hours | valid time format & range |
| 8001 | | unknow service | check ns / work / version valid & work |
| 8004 | | token expired | create new token |
| 8009 | |game url not set | set game url |
| 9999 | | others error | contact customer support |


---

* FAQ
1. 若 Header中沒有傳入 名為Agent-Code, Sign 任一項的Header，則 Http 狀態碼回傳 401，錯誤訊息會放在 Header Message 裡

2. 利用 Header Agent-Code 取得系統中 Agent-Code 的相關資訊，若無法取得，則 Http 狀態碼回傳 401，錯誤訊息會放在 Header Message 裡簽名無效狀況

3. 確認1,2點皆無問題後，進行簽名驗證，若驗證失敗則回傳加密後的資料 errorCode=1001 => 解密失敗

4. 確認1,2,3點皆無問題後，進行資料解密，若失敗回傳加密後的資料 errorCode=1002

5. 請確認傳入的參數和值沒有多餘的空白




---

* JAVA版
```
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher
import groovy.json.JsonBuilder

JsonBuilder builder = new JsonBuilder()
builder(
	{
		"ns" "player"
		"work" "createPlayer"
		"version" "0.0.1"
		"data" {
			"account" "abc1234567890"
			"nickname" "haabc1234567890" 
			"email" "abc@test.com"
		}
	}
)
def plainText = builder.toString()
def encryptKey = encryptKey.decodeBase64()
def ivSpec = encryptIV.decodeBase64()
def cipher = Cipher.getInstance("AES/CBC/NoPadding")

int blockSize = cipher.getBlockSize()
byte[] dataBytes = plainText.getBytes()
int length = dataBytes.length

if (length % blockSize != 0) {
	length = length + (blockSize - (length % blockSize));
}

byte[] newPlainText = new byte[length]
System.arraycopy(dataBytes, 0, newPlainText, 0, dataBytes.length)

SecretKeySpec key = new SecretKeySpec(encryptKey, "AES")
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivSpec))
def encryptedStr = cipher.doFinal(newPlainText).encodeBase64().toString()
```