很多系統已經支持易支付(epay)協議,比如 Xboard、V2Board、SSPanel 和獨角數卡。KukoPay 提供一個兼容這個協議的網關:在這些系統的易支付設置裏填上 KukoPay 的地址和密鑰,就能直接收款,不需要安裝插件,也不需要寫代碼。
通過易支付創建的訂單和 API 直連 創建的訂單完全相同,用同一個收銀台,走同一套結算、退款和拒付流程,都在控制台的訂單列表裏。易支付只是另一種下單方式。
新開發的系統建議直接對接 API。易支付協議使用 MD5 簽名,而且只能表達「支付成功」這一個結果。兼容接口是為已有系統準備的。
開通
在控制台開通
打開控制台「開發者 → 易支付接入」,點擊「開通易支付」,會得到:
| 項目 | 説明 |
|---|---|
| 網關地址 | https://api.kukopay.com/epay,對接系統會自己在後面拼 /submit.php 等路徑 |
商戶 ID(pid) | 純數字 |
商戶密鑰(key) | 32 位,用於簽名 |
| 收款幣種 | 易支付的 money 只有數字,沒有幣種,所以按這裏選的幣種收款 |
| 訂單有效期 | 買家必須在這段時間內付款,默認 30 分鐘,見下文 訂單有效期 |
pid 和密鑰屬於應用:每個應用在沙箱和正式環境各有一套,環境和應用都由 pid 決定。要接多套系統,就為每套系統建一個應用、各開通一次,它們的訂單和通知互不影響,資金在同一個賬戶裏。開通前先在後台左上角切換到對應的應用。先在 沙箱控制台 開通並聯調,入駐審核 通過後再開通正式環境。
填到對接系統裏
在對接系統的易支付設置裏,填上網關地址、商戶 ID 和商戶密鑰。以 Xboard 自帶的「EPay」支付方式為例:
| Xboard 字段 | 填什麼 |
|---|---|
| 支付網關地址 | https://api.kukopay.com/epay |
| 商戶ID | 控制台顯示的 pid |
| 通信密鑰 | 控制台顯示的商戶密鑰 |
| 支付類型 | 可以留空,見下文 支付類型 |
在沙箱跑通一筆
用沙箱的 pid 下一個訂單,在收銀台用測試卡 4242 4242 4242 4242 付款。確認對接系統收到支付通知、訂單變成已支付。更多測試卡見 沙箱測試。
接口
| 地址 | 方式 | 用途 |
|---|---|---|
/epay/submit.php | GET 或表單 POST | 頁面跳轉支付:買家瀏覽器帶着簽名參數訪問,被重定向到收銀台 |
/epay/mapi.php | POST | 服務端下單:返回收銀台地址 payurl,由你把買家帶過去 |
/epay/api.php(act=order) | POST(推薦)或 GET | 查詢單筆訂單 |
以上地址都在 https://api.kukopay.com 下。POST 請求可以用表單(application/x-www-form-urlencoded)或 JSON。
下單參數
submit.php 和 mapi.php 的參數相同:
| 參數 | 必填 | 説明 |
|---|---|---|
pid | 是 | 商戶 ID |
out_trade_no | 是 | 你的訂單號,最長 128 個字符 |
money | 是 | 金額,最多兩位小數,例如 30、30.5、19.99 |
name | 否 | 商品名稱,最長 255 個字符 |
notify_url | 是 | 支付成功後接收通知的地址,必須是公網可訪問的 http(s) 地址 |
return_url | 否 | 支付完成後買家返回的頁面 |
type | 否 | 支付類型,見下文 |
param | 否 | 附加參數,最長 512 個字符,通知和查詢時原樣返回 |
sign | 是 | 簽名 |
sign_type | 否 | 固定為 MD5 |
mapi.php 可以額外傳 clientip、device,KukoPay 接受但不使用,收銀台會自己適配買家的設備。
mapi.php 下單成功時返回:
{
"code": 1,
"msg": "success",
"trade_no": "TRD_SB_9F3A…",
"payurl": "https://checkout.kukopay.com/checkout?trade_no=TRD_SB_9F3A…&token=…",
"request_id": "req_…"
}失敗時 HTTP 狀態碼仍然是 200,code 為 -1,msg 説明原因。submit.php 失敗時,買家會看到一個錯誤頁。兩種失敗都會帶上 request_id,拿它到控制台「開發者 → 調用日誌」可以看到完整的請求。
同一個 out_trade_no 重複下單,得到的還是同一筆訂單,買家多點幾次支付不會產生重複訂單。如果重複請求改了金額、商品名稱、notify_url 或 return_url,會被拒絕。type 和 param 變了不算不同的訂單,仍然按第一次的記錄。
查詢訂單
curl -X POST https://api.kukopay.com/epay/api.php \
-d act=order -d pid=10001 -d key="$EPAY_KEY" -d out_trade_no=XB2026…用 trade_no(KukoPay 訂單號)或 out_trade_no(你的訂單號)查詢,兩個都傳時按 trade_no 查。
這個接口按協議直接用 key 作為憑證,請只從服務端調用,並且用 POST 把參數放在請求體裏。協議也允許 GET 查詢參數,但 URL 會被反向代理、CDN 和瀏覽器歷史原樣記錄,密鑰就跟着留在了那些日誌裏。
返回:
{
"code": 1,
"msg": "查詢訂單號成功!",
"trade_no": "TRD_SB_9F3A…",
"out_trade_no": "XB2026…",
"type": "alipay",
"pid": 10001,
"addtime": "2026-09-28 18:00:00",
"endtime": "2026-09-28 18:01:12",
"name": "Pro plan",
"money": "30.00",
"currency": "CNY",
"status": 1,
"param": "",
"request_id": "req_…"
}status 為 1 表示已支付(包括之後部分退款的訂單),2 表示已全額退款,0 表示未支付、已失敗或已過期。時間是北京時間。
簽名
簽名方法和彩虹易支付的 SDK 一致:
- 去掉
sign、sign_type和值為空的參數; - 按參數名的 ASCII 順序排序;
- 拼成
a=1&b=2,值不做 URL 編碼; - 在末尾直接接上商戶密鑰,求 MD5,取 32 位小寫。
function epay_sign(array $params, string $key): string {
ksort($params);
$pairs = [];
foreach ($params as $k => $v) {
if ($k === 'sign' || $k === 'sign_type' || $v === '' || $v === null) continue;
$pairs[] = $k . '=' . $v;
}
return md5(implode('&', $pairs) . $key);
}import hashlib
def epay_sign(params: dict, key: str) -> str:
pairs = [f"{k}={v}" for k, v in sorted(params.items())
if k not in ("sign", "sign_type") and v not in ("", None)]
return hashlib.md5(("&".join(pairs) + key).encode()).hexdigest()import crypto from "node:crypto"
function epaySign(params, key) {
const text = Object.keys(params)
.filter((k) => k !== "sign" && k !== "sign_type" && params[k] !== "" && params[k] != null)
.sort()
.map((k) => `${k}=${params[k]}`)
.join("&")
return crypto.createHash("md5").update(text + key).digest("hex")
}有些系統用 PHP 的 http_build_query 拼簽名串,會把空值也拼進去。KukoPay 兩種寫法都能驗簽通過。
支付通知
訂單支付成功後,KukoPay 用 GET 請求你的 notify_url,參數放在查詢字符串裏:
| 參數 | 説明 |
|---|---|
pid | 商戶 ID |
trade_no | KukoPay 訂單號 |
out_trade_no | 你的訂單號 |
type | 買家實際使用的支付方式,如 alipay、wxpay、card;未知時為下單時傳的 type |
name | 商品名稱 |
money | 金額,固定兩位小數 |
trade_status | 固定為 TRADE_SUCCESS |
param | 下單時傳的附加參數(為空時不帶) |
sign / sign_type | 簽名,算法同上 |
處理完成後,響應內容必須是 success 這幾個字母,HTTP 狀態碼為 2xx。返回別的內容、非 2xx 或超過 10 秒沒有響應,都算失敗。失敗後會依次間隔 30 秒、2 分鐘、10 分鐘、30 分鐘、2 小時重試,最多共發送 6 次。
只有支付成功才會發通知。 失敗、過期都不會通知,因為很多易支付客戶端(包括 Xboard 自帶的)只校驗簽名、不看 trade_status,發了反而會被當成支付成功。需要知道訂單失敗或過期,請用查詢接口。
- 先驗簽,再處理。 簽名用的是發送時刻的商戶密鑰。
- 按
out_trade_no去重。 同一筆訂單的通知可能重複收到,已處理過的直接返回success。 - 核對金額。 用
money和你訂單裏的金額比對。 notify_url可以是 http,但仍建議用 https。通知有簽名,別人無法偽造,但 http 下內容是明文傳輸的。- 控制台「開發者 → Webhook」的投遞記錄裏能看到每次通知的請求、你的返回內容和失敗原因。修復接收端後可以手動重發。
支付後返回
如果下單時傳了 return_url,買家付款後會先經過 KukoPay,再跳回 return_url,並帶上和支付通知相同的簽名參數,你的返回頁可以用同樣的方法驗簽。訂單未支付時直接跳回,不帶參數。
返回頁只用來展示結果。發貨、開通服務請以支付通知或查詢接口為準,因為買家可能根本不會回到你的頁面。
訂單有效期
易支付協議沒有表示「訂單何時失效」的參數,所以在控制台為每個環境設置一個訂單有效期。這個時間從下單算起,過了之後收銀台關閉,買家無法再付款。
請把它設為不超過對接系統自己的訂單超時時間。 大多數系統會自動取消一段時間內未付款的訂單:Xboard 和 V2Board 是 2 小時,獨角數卡默認 5 分鐘。如果我們的收銀台開得比對方久,買家可能在對方已經取消訂單之後付款。這時通知會照常發出,對方系統可能直接回復 success 卻不發貨,只能人工退款。
- 可選 5 分鐘到 24 小時,默認 30 分鐘。
- 設短了最多是讓買家重新下單;設長了才可能收到已取消訂單的錢。拿不準時寧可設短。
- 修改只對之後創建的訂單生效。同一個
out_trade_no重複下單,沿用第一次的有效期。
支付類型
type 不會限制收銀台顯示哪些支付方式:收銀台顯示你賬戶已開通的所有支付方式,由買家選擇。所以 type 可以留空,也可以隨便填(alipay、wxpay、qqpay 都會被接受)。通知和查詢裏的 type 是買家實際使用的支付方式。
和 KukoPay 其它功能的關係
- 訂單:易支付訂單出現在控制台訂單列表裏,也能用 訂單查詢接口 查到。
- Webhook:易支付訂單的支付成功只發易支付格式的通知,不會再推到你配置的 Webhook 端點。該訂單之後的退款、拒付等事件照常推到 Webhook 端點,也都能在 事件接口 查到。
- 訂單號:
out_trade_no和 API 下單共用一個命名空間。同一個訂單號不能既用 API 下單、又用易支付下單。 - 退款、拒付、提現:在控制台操作,或使用對應的 API。
- IP 白名單:商戶 ID 所屬應用的 IP 白名單(「開發者 → API 密鑰」頁面)對
mapi.php和api.php生效。submit.php是買家瀏覽器發起的請求,不受白名單限制。 - 重置密鑰:舊密鑰立即失效,沒有過渡期,因為協議一條消息只能帶一個簽名。重置後請馬上在對接系統裏更新。
常見問題
下單提示「簽名校驗失敗」
最常見的原因是密鑰填錯,或者用了另一個環境的 pid 或密鑰。其次是簽名串拼法不對:值做了 URL 編碼、沒有排序,或者把 sign_type 也拼進去了。
付款成功了,對接系統沒收到通知
到「開發者 → Webhook」查看投遞記錄:
- 對方返回了內容但不是
success:接收端報錯了,或者驗簽失敗。看返回內容裏的錯誤信息。 - 連接失敗或超時:檢查對接系統的防火牆、CDN、WAF 是否攔截了 KukoPay 的請求。
- 沒有投遞記錄:確認訂單確實是用易支付下的,並且已經支付成功。
提示「商戶尚未通過資質審核」
你在用正式環境的 pid,但賬戶還沒通過審核。審核通過前請使用沙箱的 pid。