公开 API 文档
版本: 1.1.0 最后更新: 2025-12-24
环境
| 环境 | Base URL | 描述 |
|---|---|---|
| 生产 | https://gateway.facaiqq.com/v1 | 真实交易,实时支付 |
| 沙盒 | https://gateway.facaiqq.com/v1 | 测试交易(仅限沙盒商户类型) |
概述
公开 API 使商户能够将支付处理功能集成到其应用程序中。所有 API 请求都需要 HMAC-SHA256 身份验证。
生产环境与沙盒环境
商户类型决定交易路由:
- 生产商户:通过支付提供商处理真实资金交易。
- 沙盒商户:用于测试的模拟交易。响应在本地模拟,不进行外部处理。
身份验证
所有 API 请求必须包含以下请求头:
| 请求头 | 必填 | 描述 |
|---|---|---|
X-API-Key | 是 | 您的商户 API 密钥(64 个字符) |
X-Signature | 是 | 请求的 HMAC-SHA256 签名 |
X-Timestamp | 是 | Unix 时间戳(自 epoch 以来的秒数) |
Content-Type | 是 | application/json |
签名生成
签名使用您的密钥通过 HMAC-SHA256 计算:
Signature = HMAC-SHA256(SignatureData, SecretKey)
SignatureData = METHOD|PATH|TIMESTAMP|BODY示例(POST 请求):
SignatureData = "POST|/v1/payments/deposit|1702400000|{\"amount\":100.00,\"currency\":\"THB\",\"reference_id\":\"ORDER-001\"}"示例(GET 请求):
SignatureData = "GET|/v1/payments/pay_abc123|1702400000"代码示例
import hmac
import hashlib
import time
import json
import requests
api_key = "your_api_key"
secret_key = "your_secret_key"
base_url = "https://gateway.facaiqq.com"
def create_signature(method, path, timestamp, body=""):
# 重要:path 必须是完整的 HTTP 路径(例如 /v1/payments/deposit)
data = f"{method}|{path}|{timestamp}"
if body:
data += f"|{body}"
return hmac.new(
secret_key.encode(),
data.encode(),
hashlib.sha256
).hexdigest()
def make_request(method, path, body=None):
timestamp = str(int(time.time()))
body_str = json.dumps(body) if body else ""
signature = create_signature(method, path, timestamp, body_str)
headers = {
"X-API-Key": api_key,
"X-Signature": signature,
"X-Timestamp": timestamp,
"Content-Type": "application/json"
}
url = base_url + path
if method == "POST":
return requests.post(url, headers=headers, json=body)
else:
return requests.get(url, headers=headers)
# 创建存款 - 注意:使用包含 /v1 的完整路径
response = make_request("POST", "/v1/payments/deposit", {
"amount": 1000.00,
"currency": "THB",
"reference_id": "ORD888888888",
"bank_code": "004",
"bank_account_no": "1234567890",
"bank_account_name": "test test",
"webhook_url": "https://example.com/path/to/your/webhook"
})
print(response.json())const crypto = require("crypto");
const axios = require("axios");
const apiKey = "your_api_key";
const secretKey = "your_secret_key";
const baseUrl = "https://gateway.facaiqq.com";
function createSignature(method, path, timestamp, body = "") {
// 重要:path 必须是完整的 HTTP 路径(例如 /v1/payments/deposit)
let data = `${method}|${path}|${timestamp}`;
if (body) {
data += `|${body}`;
}
return crypto.createHmac("sha256", secretKey).update(data).digest("hex");
}
async function makeRequest(method, path, body = null) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const bodyStr = body ? JSON.stringify(body) : "";
const signature = createSignature(method, path, timestamp, bodyStr);
const headers = {
"X-API-Key": apiKey,
"X-Signature": signature,
"X-Timestamp": timestamp,
"Content-Type": "application/json",
};
const url = baseUrl + path;
if (method === "POST") {
return axios.post(url, body, { headers });
} else {
return axios.get(url, { headers });
}
}
// 创建存款 - 注意:使用包含 /v1 的完整路径
makeRequest("POST", "/v1/payments/deposit", {
amount: 1000.0,
currency: "THB",
reference_id: "ORD888888888",
bank_code: "004",
bank_account_no: "1234567890",
bank_account_name: "test test",
webhook_url: "https://example.com/path/to/your/webhook",
}).then((res) => console.log(res.data));<?php
$apiKey = 'your_api_key';
$secretKey = 'your_secret_key';
$baseUrl = 'https://gateway.facaiqq.com';
function createSignature($method, $path, $timestamp, $body = '') {
global $secretKey;
// 重要:path 必须是完整的 HTTP 路径(例如 /v1/payments/deposit)
$data = "$method|$path|$timestamp";
if ($body) {
$data .= "|$body";
}
return hash_hmac('sha256', $data, $secretKey);
}
function makeRequest($method, $path, $body = null) {
global $apiKey, $baseUrl;
$timestamp = time();
$bodyStr = $body ? json_encode($body) : '';
$signature = createSignature($method, $path, $timestamp, $bodyStr);
$headers = [
'X-API-Key: ' . $apiKey,
'X-Signature: ' . $signature,
'X-Timestamp: ' . $timestamp,
'Content-Type: application/json'
];
$ch = curl_init($baseUrl . $path);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $bodyStr);
}
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// 创建存款 - 注意:使用包含 /v1 的完整路径
$response = makeRequest('POST', '/v1/payments/deposit', [
'amount' => 1000.00,
'currency' => 'THB',
'reference_id' => 'ORD888888888',
'bank_code' => '004',
'bank_account_no' => '1234567890',
'bank_account_name' => 'test test',
'webhook_url' => 'https://example.com/path/to/your/webhook'
]);
print_r($response);package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"time"
)
const (
apiKey = "your_api_key"
secretKey = "your_secret_key"
baseURL = "https://gateway.facaiqq.com"
)
func createSignature(method, path, timestamp, body string) string {
// 重要:path 必须是完整的 HTTP 路径(例如 /v1/payments/deposit)
data := fmt.Sprintf("%s|%s|%s", method, path, timestamp)
if body != "" {
data += "|" + body
}
h := hmac.New(sha256.New, []byte(secretKey))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
func makeRequest(method, path string, body interface{}) (map[string]interface{}, error) {
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
var bodyStr string
var reqBody io.Reader
if body != nil {
bodyBytes, _ := json.Marshal(body)
bodyStr = string(bodyBytes)
reqBody = bytes.NewBuffer(bodyBytes)
}
signature := createSignature(method, path, timestamp, bodyStr)
req, _ := http.NewRequest(method, baseURL+path, reqBody)
req.Header.Set("X-API-Key", apiKey)
req.Header.Set("X-Signature", signature)
req.Header.Set("X-Timestamp", timestamp)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
return result, nil
}
func main() {
// 创建存款 - 注意:使用包含 /v1 的完整路径
result, _ := makeRequest("POST", "/v1/payments/deposit", map[string]interface{}{
"amount": 1000.00,
"currency": "THB",
"reference_id": "ORD888888888",
"bank_code": "004",
"bank_account_no": "1234567890",
"bank_account_name": "test test",
"webhook_url": "https://example.com/path/to/your/webhook",
})
fmt.Printf("%+v\n", result)
}接口端点
创建存款
创建新的存款支付请求。返回 PromptPay 二维码供客户支付。
POST /v1/payments/deposit
请求体
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
amount | number | 是 | 支付金额(> 0) |
currency | string | 是 | 货币代码(3 个字符,例如 "THB") |
reference_id | string | 是 | 您的唯一订单参考号(1-64 个字符) |
customer_email | string | 否 | 客户邮箱,用于通知 |
customer_phone | string | 否 | 客户电话号码(最多 20 个字符) |
description | string | 否 | 支付描述(最多 255 个字符) |
webhook_url | string | 否 | 支付通知的 Webhook URL |
bank_code | string | 否 | 付款人银行代码(3 位数字),用于参考 |
bank_account_no | string | 否 | 付款人银行账号(最多 20 个字符) |
bank_account_name | string | 否 | 付款人银行账户持有人姓名(最多 100 个字符) |
请求示例
{
"amount": 1000.00,
"currency": "THB",
"reference_id": "ORD888888888",
"bank_code": "004",
"bank_account_no": "1234567890",
"bank_account_name": "test test",
"webhook_url": "https://example.com/path/to/your/webhook"
}响应
响应格式取决于 amount 金额是否达到管理员设定的阈值(以下示例使用 5000 仅供说明 — 实际阈值由管理员配置)。
当 amount 低于阈值(例如 < 5000)时 — 通过 PromptPay 二维码支付
company_account = false,并照常返回 PromptPay 二维码。
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORD888888888",
"type": "deposit",
"status": "pending",
"amount": 1000.00,
"currency": "THB",
"company_account": false,
"qr_code": "00020101021129370016A000000677010111011300668123456785802TH5303764...",
"qr_code_image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
"created_at": "2025-12-24T10:30:00Z",
"expires_at": "2025-12-24T10:45:00Z"
}当 amount 达到阈值(例如 >= 5000)时 — 转账至公司账户
company_account = true,并附带公司账户信息(company_account_bank_code、company_account_bank_name、company_account_name、company_account_no)。此响应中不会返回 qr_code / qr_code_image 字段。
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORD888888888",
"type": "deposit",
"status": "pending",
"amount": 5000.00,
"currency": "THB",
"company_account": true,
"company_account_bank_code": "004",
"company_account_bank_name": "开泰银行",
"company_account_name": "示例公司有限公司",
"company_account_no": "1234567890",
"created_at": "2025-12-24T10:30:00Z",
"expires_at": "2025-12-24T10:45:00Z"
}响应字段
| 字段 | 类型 | 描述 |
|---|---|---|
id | string | 唯一交易 ID(UUID 格式) |
reference_id | string | 您的订单参考号 |
type | string | 交易类型(deposit) |
status | string | 支付状态 |
amount | number | 支付金额 |
currency | string | 货币代码 |
company_account | boolean | 公司账户标志 — 当 amount 达到管理员设定的阈值时为 true(转账至公司账户),低于阈值时为 false(通过二维码支付)。5000 仅为示例阈值。 |
company_account_bank_code | string | 公司账户银行代码(3 位数字)(仅当 company_account = true 时返回) |
company_account_bank_name | string | 公司账户银行名称(仅当 company_account = true 时返回) |
company_account_name | string | 公司账户持有人姓名(仅当 company_account = true 时返回) |
company_account_no | string | 公司账号(仅当 company_account = true 时返回) |
qr_code | string | PromptPay 二维码字符串(仅当 company_account = false 时返回) |
qr_code_image | string | Base64 编码的二维码图片,PNG(仅当 company_account = false 时返回) |
created_at | string | 创建时间戳(ISO 8601) |
expires_at | string | 过期时间戳(ISO 8601,默认 15 分钟) |
创建提款
创建新的提款/付款请求到银行账户。
POST /v1/payments/withdraw
请求体
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
amount | number | 是 | 提款金额(> 0) |
currency | string | 是 | 货币代码(3 个字符) |
reference_id | string | 是 | 您的唯一参考号(1-64 个字符) |
bank_code | string | 是 | 银行代码(3 位数字,见下表) |
account_number | string | 是 | 银行账号(10-20 位数字) |
account_holder_name | string | 是 | 账户持有人姓名(1-100 个字符) |
description | string | 否 | 转账描述(最多 255 个字符) |
webhook_url | string | 否 | 转账通知的 Webhook URL |
银行代码
| 代码 | 泰文名称 | 英文名称 |
|---|---|---|
002 | ธนาคารกรุงเทพ | Bangkok Bank PCL (BBL) |
004 | ธนาคารกสิกรไทย | Kasikornbank PCL (Kbank) |
006 | ธนาคารกรุงไทย | Krungthai Bank PCL (KTB) |
011 | ธนาคารทหารไทยธนชาต | TMBThanachart Bank PCL (TTB) |
014 | ธนาคารไทยพาณิชย์ | Siam Commercial Bank PCL (SCB) |
017 | ธนาคารซิตี้แบงก์ | Citibank N.A. |
020 | ธนาคารสแตนดาร์ดชาร์เตอร์ด (ไทย) | Standard Chartered Bank (Thai) PCL |
022 | ธนาคารซีไอเอ็มบี ไทย | CIMB Thai Bank PCL |
024 | ธนาคารยูโอบี | United Overseas Bank (Thai) PCL |
025 | ธนาคารกรุงศรีอยุธยา | Bank of Ayudhya PCL (Krungsri) |
026 | ธนาคารเมกะสากลพาณิชย์ | Mega International Commercial Bank |
030 | ธนาคารออมสิน | Government Savings Bank (GSB) |
031 | ธนาคารฮ่องกงและเซี่ยงไฮ้ | HSBC Thailand |
033 | ธนาคารอาคารสงเคราะห์ | Government Housing Bank (GHB) |
034 | ธนาคารเพื่อการเกษตรฯ | BAAC |
065 | ธนาคารธนชาต | Thanachart Bank PCL |
066 | ธนาคารอิสลามแห่งประเทศไทย | Islamic Bank of Thailand |
067 | ธนาคารทิสโก้ | TISCO Bank PCL |
069 | ธนาคารเกียรตินาคินภัทร | Kiatnakin Phatra Bank PCL (KKP) |
070 | ธนาคารไอซีบีซี (ไทย) | ICBC (Thai) PCL |
071 | ธนาคารไทยเครดิต | Thai Credit Retail Bank |
073 | ธนาคารแลนด์ แอนด์ เฮ้าส์ | Land and Houses Bank PCL (LH Bank) |
088 | ธนาคารคลิกซ์ | Clicx Bank |
PROMPTPAY | พร้อมเพย์ | PromptPay |
TRUE | ทรูมันนี่ | TrueMoney |
PromptPay 提款
当使用 bank_code: "PROMPTPAY" 时,account_number 字段应包含:
| 账号长度 | 代理类型 | 描述 |
|---|---|---|
| 10 位数字 | MOBILE | 手机号码 |
| 13 位数字 | TXID | 身份证号或税务 ID |
请求示例(使用手机号的 PromptPay):
{
"amount": 5000.00,
"currency": "THB",
"reference_id": "ORD888888890",
"bank_code": "PROMPTPAY",
"account_number": "0812345678",
"account_holder_name": "John Doe"
}请求示例(使用身份证号的 PromptPay):
{
"amount": 5000.00,
"currency": "THB",
"reference_id": "ORD888888891",
"bank_code": "PROMPTPAY",
"account_number": "1234567890123",
"account_holder_name": "John Doe"
}请求示例
{
"amount": 5000.00,
"currency": "THB",
"reference_id": "ORD888888889",
"bank_code": "014",
"account_number": "1234567890",
"account_holder_name": "John Doe",
"webhook_url": "https://example.com/path/to/your/webhook"
}响应
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORD888888889",
"type": "withdraw",
"status": "processing",
"amount": 5000.00,
"currency": "THB",
"created_at": "2025-12-24T11:00:00Z"
}获取支付详情
通过支付 ID 检索支付详情。
GET /v1/payments/{id}
路径参数
| 参数 | 类型 | 描述 |
|---|---|---|
id | string | 支付 ID(UUID 格式) |
响应示例
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORDER-2024-001",
"type": "deposit",
"status": "completed",
"amount": 1500.00,
"currency": "THB",
"qr_code": "00020101021129370016A000000677010111...",
"created_at": "2024-12-12T10:30:00Z",
"expires_at": "2024-12-12T10:45:00Z"
}取消支付
取消待处理的支付。只有状态为 pending 的支付才能被取消。
POST /v1/payments/{id}/cancel
路径参数
| 参数 | 类型 | 描述 |
|---|---|---|
id | string | 要取消的支付 ID |
响应示例
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORDER-2024-001",
"type": "deposit",
"status": "cancelled",
"amount": 1500.00,
"currency": "THB",
"created_at": "2024-12-12T10:30:00Z"
}支付状态
| 状态 | 描述 |
|---|---|
pending | 支付已创建,等待客户操作 |
processing | 支付正在处理中 |
completed | 支付成功完成 |
failed | 支付失败(包括过期的交易) |
cancelled | 支付已取消 |
refunded | 支付已退款 |
提示
当支付过期(客户未在时间限制内支付)时,状态会变为 failed,失败原因为 "Transaction expired"。系统会发送 Webhook 通知您的系统。
Webhooks
当您提供 webhook_url 时,我们将发送 POST 请求以更新支付状态。
Webhook 负载
Webhook 负载直接发送(不包装在事件结构中):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "deposit",
"amount": 1500.00,
"fee": 37.50,
"netAmount": 1462.50,
"bankAccountNo": "1234567890",
"bankAccountName": "SOMCHAI JAIDEE",
"bankCode": "014",
"bankName": "ธนาคารไทยพาณิชย์",
"currency": "THB",
"referenceNo": "ORDER-2024-001",
"status": "success",
"timestamp": "2024-12-12T10:35:00Z",
"paidAt": "2024-12-12T10:34:55.123456789Z"
}Webhook 负载字段
| 字段 | 类型 | 描述 |
|---|---|---|
id | string | 交易 ID |
type | string | 交易类型:deposit 或 withdraw |
amount | number | 原始支付金额 |
fee | number | 收取的处理费 |
netAmount | number | 扣除费用后的金额 |
bankAccountNo | string | 银行账号(用于有付款人信息的存款) |
bankAccountName | string | 银行账户持有人姓名 |
bankCode | string | 银行代码 |
bankName | string | 银行名称(泰文) |
currency | string | 货币代码 |
referenceNo | string | 您的参考 ID |
status | string | success、failed 或 expired |
timestamp | string | Webhook 发送时间戳(ISO 8601) |
paidAt | string | 支付完成时间(如未支付则为空) |
Webhook 状态值
| 状态 | 描述 |
|---|---|
success | 支付成功完成 |
failed | 支付失败或过期 |
expired | 支付过期(旧版,现在使用 failed) |
Webhook 重试策略
- 如果发送失败,Webhook 最多重试 5 次
- 重试间隔:每次尝试之间 1 分钟
- 成功发送是任何 2xx HTTP 响应
- 5 次失败尝试后,Webhook 将被标记为失败
Webhook 验证
Webhook 请求包含用于验证的请求头:
| 请求头 | 描述 |
|---|---|
X-Webhook-Signature | 请求体的 HMAC-SHA256 签名 |
X-Webhook-Timestamp | Webhook 的 Unix 时间戳 |
Content-Type | application/json |
使用与 API 请求相同的 HMAC 方法验证 Webhook:
import hmac
import hashlib
def verify_webhook(body, signature, timestamp, secret_key):
expected = hmac.new(
secret_key.encode(),
f"{timestamp}|{body}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)错误响应
所有错误遵循以下格式:
{
"code": "ERROR_CODE",
"message": "Human-readable error message"
}HTTP 状态码
| 状态码 | 描述 |
|---|---|
200 | 成功 |
201 | 创建成功 |
400 | 请求错误 - 无效参数 |
401 | 未授权 - 无效的 API 密钥或签名 |
403 | 禁止 - 商户未激活或权限不足 |
404 | 未找到 |
409 | 冲突 - 重复的 reference_id |
500 | 内部服务器错误 |
504 | 网关超时 |
错误代码
| 代码 | 描述 |
|---|---|
INVALID_REQUEST | 请求体验证失败 |
MISSING_API_KEY | 缺少 X-API-Key 请求头 |
MISSING_SIGNATURE | 缺少 X-Signature 请求头 |
MISSING_TIMESTAMP | 缺少 X-Timestamp 请求头 |
INVALID_API_KEY | API 密钥无效 |
INVALID_SIGNATURE | 签名验证失败 |
INVALID_TIMESTAMP | 时间戳格式无效 |
TIMESTAMP_EXPIRED | 请求时间戳超出有效窗口 |
MERCHANT_INACTIVE | 商户账户未激活 |
PAYMENT_NOT_FOUND | 未找到支付 |
PAYMENT_ERROR | 一般支付处理错误 |
DUPLICATE_REFERENCE | 参考 ID 已存在 |
INSUFFICIENT_BALANCE | 提款余额不足 |
INVALID_BANK_CODE | 无效的银行代码 |
INVALID_ACCOUNT | 无效的银行账户(沙盒) |
PAYMENT_CANNOT_BE_CANCELLED | 支付无法取消 |
TIMEOUT | 请求超时 |
沙盒测试
沙盒工作原理
沙盒模式由您的商户类型决定,而不是单独的 URL。沙盒和生产商户使用相同的 API 端点:
Base URL: https://gateway.facaiqq.com/v1
- 沙盒商户:交易存储在沙盒数据库中。不处理真实资金。
- 生产商户:交易存储在生产数据库中,并通过支付提供商处理。
请联系支持团队申请用于测试的沙盒凭证。
测试金额
在沙盒模式下,特定金额会触发不同的行为:
| 金额 | 货币 | 行为 | 描述 |
|---|---|---|---|
100.00 | THB | auto_approve | 支付立即完成 |
200.00 | THB | auto_pending | 支付保持待处理(模拟延迟) |
500.00 | THB | auto_approve | 支付立即完成 |
888.88 | THB | timeout | 支付超时 |
999.99 | THB | auto_fail | 支付立即失败 |
1000.00 | THB | auto_approve | 支付立即完成 |
| 其他 | THB | pending | 正常支付流程 |
测试场景
您可以使用 X-Sandbox-Scenario 请求头覆盖行为:
| 场景 | 描述 |
|---|---|
auto_approve | 强制支付立即完成 |
auto_fail | 强制支付失败 |
auto_pending | 强制支付保持待处理 |
timeout | 模拟超时 |
invalid_account | 模拟无效银行账户(仅限提款) |
示例:
curl -X POST https://gateway.facaiqq.com/v1/payments/deposit \
-H "X-Sandbox-Scenario: auto_approve" \
-H "X-API-Key: your_sandbox_api_key" \
-H "X-Signature: ..." \
-H "X-Timestamp: ..." \
-H "Content-Type: application/json" \
-d '{"amount": 500.00, "currency": "THB", "reference_id": "TEST-001"}'响应头
响应包含指示环境的请求头:
| 请求头 | 值 | 描述 |
|---|---|---|
X-Environment | sandbox 或 production | 指示商户类型 |
交易过期
- 存款交易默认过期时间为 15 分钟
- 如果客户在此时间内未支付:
- 状态变为
failed(不是expired) failure_reason设置为 "Transaction expired"- 发送 Webhook,
status: "failed"
- 状态变为
- 过期检查每 1 分钟运行一次
速率限制
| 端点 | 速率限制 |
|---|---|
| 所有端点 | 100 次请求/分钟 |
| 创建存款 | 30 次请求/分钟 |
| 创建提款 | 20 次请求/分钟 |
响应中包含速率限制请求头:
| 请求头 | 描述 |
|---|---|
X-RateLimit-Limit | 每个窗口的最大请求数 |
X-RateLimit-Remaining | 窗口内剩余的请求数 |
X-RateLimit-Reset | 限制重置的 Unix 时间戳 |
支持
如需技术支持: