文档
商户 Trade API 接入
本文档说明商户如何接入 Brazosd Trade API,用 Agent 作为前端入口、商户 SaaS 作为后端履约系统。
1. 启用商户功能
使用普通 Brazosd Bearer token 调用:
POST /api/v1/trade/merchant
Authorization: Bearer <access_token>启用后,当前 org 会得到一个 merchant profile。平台默认抽佣比例为 1500 ppm,即 0.15%。Brazosd 会同时创建商户回调签名用的 Ed25519 key。商户可用:
GET /api/v1/trade/merchant/callback-keys
Authorization: Bearer <access_token>获取用于校验 Brazosd 回调签名的公钥列表。
2. 生成 Trade API 密钥
商户后端持有 Ed25519 私钥,Brazosd 只保存公钥。私钥只能放在商户服务端,不要放进浏览器、Agent prompt、日志或用户可见配置。
from base64 import b64encode
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization
private_key = ed25519.Ed25519PrivateKey.generate()
public_key = private_key.public_key()
private_key_b64 = b64encode(private_key.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption(),
)).decode()
public_key_b64 = b64encode(public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)).decode()
print("PRIVATE_KEY_B64=", private_key_b64)
print("PUBLIC_KEY_B64=", public_key_b64)注册公钥:
POST /api/v1/trade/merchant/public-keys
Authorization: Bearer <access_token>
Content-Type: application/json
{
"name": "prod-2026-05",
"publicKeyB64": "<PUBLIC_KEY_B64>"
}保存返回的 id,创建订单时放入 x-brazosd-tradeapi-pubkey-id。
3. 配置回调 URL
PUT /api/v1/trade/merchant/callback-url
Authorization: Bearer <access_token>
Content-Type: application/json
{
"url": "https://merchant.example.com/brazosd/order-callback"
}回调 URL 必须是 https://,host 必须是域名,不能是 IP 或 localhost。
4. 创建产品
Product 只存价格无关的身份和展示信息。价格在创建订单时快照到订单行里。
POST /api/v1/trade/products
Authorization: Bearer <access_token>
Content-Type: application/json
{
"name": "CRM Agent Seat",
"description": "Monthly CRM agent seat"
}description 最多 200 个字符。
5. 签名并创建订单
签名 canonical string:
METHOD
REQUEST_URI
TIMESTAMP
BODY例如 POST /api/v1/tradeapi/orders 时,REQUEST_URI 是 /api/v1/tradeapi/orders。TIMESTAMP 是 Unix seconds,Brazosd 会校验 5 分钟时间窗口。签名是 raw Ed25519 signature 的 base64。
import json
import time
import requests
from base64 import b64decode, b64encode
from cryptography.hazmat.primitives.asymmetric import ed25519
base_url = "https://brazosd.example.com"
path = "/api/v1/tradeapi/orders"
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
b64decode("<PRIVATE_KEY_B64>")
)
body = {
"agentId": "<agent_uuid>",
"buyerUserId": 123,
"outTradeNo": "merchant-order-10001",
"description": "CRM Agent monthly usage",
"expiresInSeconds": 1800,
"items": [
{
"productId": "<product_uuid>",
"amountMicroyuans": 199000000,
"note": "May subscription"
}
]
}
body_bytes = json.dumps(body, separators=(",", ":"), ensure_ascii=False).encode()
timestamp = str(int(time.time()))
canonical = b"POST\n" + path.encode() + b"\n" + timestamp.encode() + b"\n" + body_bytes
signature = b64encode(private_key.sign(canonical)).decode()
resp = requests.post(
base_url + path,
data=body_bytes,
headers={
"content-type": "application/json",
"x-brazosd-tradeapi-pubkey-id": "<registered_public_key_id>",
"x-brazosd-tradeapi-timestamp": timestamp,
"x-brazosd-tradeapi-signature": signature,
},
timeout=10,
)
resp.raise_for_status()
print(resp.json())outTradeNo 在同一个 merchant 下幂等。相同 outTradeNo 和相同请求体会返回同一个订单;相同 outTradeNo 但请求体不同会返回冲突。
6. 用户授权和白名单
订单创建后,Brazosd 会向 buyerUserId 创建一个授权表单。用户确认后才会扣款。用户勾选“不再询问此类订单”时,本订单的所有 productId 会加入该 org + agent 的允许列表;后续相同 product 可自动同意支付。
Gift credits 只能用于平台自身消费,不能用于 merchant 订单。Trade API 订单只会扣 buyer org 的 recharge credits。
7. 处理回调
订单支付成功或用户取消时,Brazosd 会 POST JSON 到商户 callback URL。Brazosd 对回调做 Ed25519 签名:
Headers:
X-Brazosd-Callback-Key-Id: <callback_key_uuid>
X-Brazosd-Callback-Timestamp: <unix_seconds>
X-Brazosd-Callback-Signature: <base64_signature>回调签名 canonical string 同样是:
METHOD
REQUEST_URI
TIMESTAMP
BODY校验示例:
from base64 import b64decode
from cryptography.hazmat.primitives.asymmetric import ed25519
def verify_brazosd_callback(method, request_uri, body_bytes, timestamp, signature_b64, public_key_b64):
public_key = ed25519.Ed25519PublicKey.from_public_bytes(b64decode(public_key_b64))
canonical = (
method.upper().encode()
+ b"\n"
+ request_uri.encode()
+ b"\n"
+ timestamp.encode()
+ b"\n"
+ body_bytes
)
public_key.verify(b64decode(signature_b64), canonical)商户服务应按 eventId 或 order.id + order.status 做幂等处理。
回调失败时,Brazosd 每 30 分钟重试一次任务;每次任务会连续尝试最多 3 次,间隔 5 秒。Brazosd 会保留每个 callback URL 最近一次失败信息,即使之后成功也不会删除失败记录,只会更新最近成功时间。