收单 Open API 接口文档(一期)
面向收单商户。能力为 Payment Processing 中转:Checkout Session(创建 / 查询 / 列表 / 更新)与 Payment Intent(查询 / 列表);支付完成后通过独立收单 Webhook 通知商户。
- 文档版本
- 2026-08-11
- 服务前缀
/open/v1/acquiring/**
- 凭证类型
- 必须使用
credential_type = ACQUIRING 的 AccessKey
- Base URL
- 由交付环境提供,形如
https://{host}(下文路径相对该 Host)
- 责任边界
- 一期为 API / Webhook 技术中转与商户隔离;收单主体、清算、拒付、结算以合同及渠道协议为准
重要:本接口仅接受 ACQUIRING 凭证。使用其他类型凭证调用本路径返回 40314。
1. 接入前准备
- 联系运营开通商户
acquiring_enabled(未开通时创建 / PATCH Session 返回 40315)。
- 获取 ACQUIRING 类型 Open API 凭证:
AccessKeyId + SecretKey + MerchantCode(由运营创建)。
- 向运营配置收单 Webhook:HTTPS 回调 URL + 订阅事件(建议订阅
checkout_session.completed、payment_intent.succeeded、payment_intent.failed),妥善保存 Webhook Secret(仅创建/轮换时展示一次)。
- 实现:请求签名(§2)、幂等 Key(创建 Session)、Webhook 验签(§11)、前端用
@slashfi/checkout-js 嵌入托管收银台(§5.6,不可直接浏览器打开 url)。
2. 鉴权与请求签名(商户 → 平台)
除健康检查外,所有请求须带签名头。
| HTTP 头 | 必填 | 说明 |
X-Access-Key-Id | 是 | ACQUIRING AccessKey ID |
X-Merchant-Code | 是 | 商户编码,须与 Key 所属商户一致 |
X-Vcc-Timestamp | 是 | Unix 时间戳,单位秒(UTC) |
X-Vcc-Signature | 是 | HMAC-SHA256,小写十六进制 |
Idempotency-Key | 写操作必填 | POST 创建 Session 必填;建议 UUID |
Content-Type | 有 body 时 | application/json |
X-Trace-Id | 否 | 调用方追踪 ID |
2.2 Canonical Request
用 \n(ASCII 0x0A)连接 4 行,再做 HMAC:
- HTTP 方法(大写),如
POST
- Servlet 路径,如
/open/v1/acquiring/checkout-sessions(不含域名与 query)
- 规范化查询串:无参数时为空字符串
- 请求体原始字节的 SHA-256(小写 hex);无 body 时对空字节做哈希
Signature = hex_lower( HMAC_SHA256( UTF8(SecretKey), UTF8(CanonicalRequest) ) )
2.3 凭证范围
| 项 | 说明 |
| 允许的凭证 | credential_type = ACQUIRING |
| 可调用路径 | /open/v1/acquiring/**,以及允许的 GET /open/v1/health |
| 错误 | 凭证类型不匹配 → HTTP 403,业务码 40314(OPEN_API_CREDENTIAL_SCOPE_DENIED) |
3. 统一响应
成功(HTTP 200)
{
"code": "0",
"message": "OK",
"data": { },
"traceId": "..."
}
业务字段均在 data 内。
失败(HTTP 4xx / 5xx)
{
"code": "40315",
"message": "商户未开通收单能力",
"data": null,
"traceId": "..."
}
请将 traceId 一并提供给支持排查。
4. 接口一览
| 方法 | 路径 | 说明 |
POST | /open/v1/acquiring/checkout-sessions | 创建 Checkout Session |
GET | /open/v1/acquiring/checkout-sessions | 列出本商户 Session(本地快照,非实时) |
GET | /open/v1/acquiring/checkout-sessions/{id} | 查询 Session(非终态会回源渠道刷新) |
PATCH | /open/v1/acquiring/checkout-sessions/{id} | 更新 open Session 的 amount / expiresAt |
GET | /open/v1/acquiring/payment-intents | 列出本商户已映射 Payment Intent |
GET | /open/v1/acquiring/payment-intents/{id} | 查询 Payment Intent(归属校验 + 非终态回源) |
GET | /open/v1/health | 健康检查(可不签名) |
不含:退款写接口、对账下载、收银台自托管、Portal 自助配置。Session/Intent 响应可含 paymentIntentId(未知时为 null,属合法中间态)。
5. 创建 Checkout Session
POST /open/v1/acquiring/checkout-sessions
5.1 额外请求头
| 头 | 必填 | 说明 |
Idempotency-Key | 是 | 商户侧幂等键。相同 Key + 相同 body → 返回首次结果;相同 Key + 不同 body → 40904 |
平台会改写发往渠道的幂等键,不会把商户原始 Key 透传给渠道。
5.2 请求体
| 字段 | 类型 | 必填 | 说明 |
amount | integer | 是 | 金额,单位美分;范围 1 … 99999999 |
currency | string | 是 | 仅支持 usd(大小写不敏感) |
clientOrderId | string | 否 | 商户订单号,最长 64 |
customMetadata | object | 否 | 自定义元数据;值仅允许标量;总大小(含平台注入)≤ 2KB;键名禁止使用平台保留前缀(大小写不敏感) |
config | object | 否 | 透传给渠道的收银台配置对象 |
未知顶层字段 → 40001。平台会在 metadata 中注入只读的商户标识、商户编码等保留字段(若有 clientOrderId 再注入订单映射字段);商户勿写入或依赖覆写这些保留键。
5.3 请求示例
POST /open/v1/acquiring/checkout-sessions HTTP/1.1
Host: {host}
Content-Type: application/json
X-Access-Key-Id: ak_acq_xxx
X-Merchant-Code: M2026...
X-Vcc-Timestamp: 1723276800
X-Vcc-Signature: ...
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"customMetadata": {
"sku": "PLAN_PRO",
"uid": "u_10086"
}
}
5.4 成功响应 data
| 字段 | 类型 | 说明 |
id | string | Checkout Session ID(后续查询使用) |
url | string | 托管收银台 URL(须用 SDK 嵌入,见 §5.6;勿直接在浏览器地址栏打开) |
status | string | 如 open / complete / expired |
amount | integer | 美分 |
currency | string | 如 usd |
clientOrderId | string|null | 商户订单号 |
merchantCode | string | 商户编码 |
createdAt | string | ISO-8601 创建时间 |
expiresAt | string|null | 过期时间 ISO-8601 |
paymentIntentId | string|null | 关联 Payment Intent;创建当下常为 null |
{
"code": "0",
"message": "OK",
"data": {
"id": "acq_checkout_xxx",
"url": "https://app.slash.com/checkout/acq_checkout_xxx",
"status": "open",
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"merchantCode": "M2026...",
"createdAt": "2026-08-10T10:00:00.000Z",
"expiresAt": "2026-08-11T10:00:00.000Z",
"paymentIntentId": null
},
"traceId": "..."
}
5.5 推荐业务流程
- 服务端
POST 创建 Session,落库 id + clientOrderId。
- 将
url 交给你们自己的前端页面,用 @slashfi/checkout-js 嵌入(见 §5.6)。
- 以 Webhook
completed 为准更新订单;可用 GET 查询做补偿。
- 勿依赖用户关闭页面时机作为支付成功依据。
5.6 打开托管收银台(必须 SDK 嵌入)
必读:渠道 Hosted Checkout 要求在商户页面内用官方 SDK 挂载 data.url。
在浏览器新标签直接打开 https://app.slash.com/checkout/... 常会报 couldn't load——这不是 Session 创建失败,而是错误的打开方式。
不要:
- 在浏览器地址栏 / 新标签直接打开托管
url
- 用普通
<a href> 整页跳转到该 URL 当作收银台
- 把前端
onComplete 当作发货依据(履约仍以 Webhook / GET Session 为准)
正确做法:
- 后端创建 Session,把
data.url(或 data.id,由后端再查)安全下发给前端。
- 前端安装并加载
@slashfi/checkout-js(建议钉版本,例如 0.0.4)。
- 调用
loadSlashCheckout() → createEmbeddedCheckoutPage({ url }) → mount('#容器')。
- 监听 SDK 的
onComplete / onError 仅作 UI 提示;订单履约以收单 Webhook checkout_session.completed 或 GET /checkout-sessions/{id} 的 status=complete 为准。
<div id="checkout"></div>
<script type="module">
import { loadSlashCheckout } from '@slashfi/checkout-js';
const slashCheckout = await loadSlashCheckout();
const checkout = await slashCheckout.createEmbeddedCheckoutPage({
// 来自 POST /open/v1/acquiring/checkout-sessions 的 data.url
url: checkoutSessionUrl,
onReady: () => { /* 收银台已渲染 */ },
onProcessing: () => { /* 支付处理中(UI) */ },
onComplete: () => { /* 仅 UI;勿在此发货 */ },
onError: ({ code, message }) => { console.error(code, message); },
});
checkout.mount('#checkout');
// 换单前先 checkout.destroy()
</script>
约束与排障
| 项 | 说明 |
| 同源 / HTTPS | 商户嵌入页应为 HTTPS(本地可用 http://localhost) |
| 单实例 | 同一页面同时只能有一个 Embedded Checkout;重新创建前先 destroy() |
| MIME | 若自托管 SDK 的 .mjs,静态服务器须返回 Content-Type: text/javascript(否则浏览器报 Failed to fetch dynamically imported module) |
load_failed | SDK 约 30s 内未收到托管页协议消息;检查账号是否开通 Hosted Checkout、网络是否拦截 app.slash.com、Session 是否仍为 open |
| Intent | Payment Intent 通常在买家支付后才出现;勿用 Session id 去查 Intent |
6. 查询 Checkout Session
GET /open/v1/acquiring/checkout-sessions/{id}
6.1 路径与规则
| 项 | 说明 |
{id} | 创建时返回的 Session id |
| 归属 | 仅本商户可见;他商户 ID → 40401 |
| 关停后 | acquiring_enabled=0 时仍允许 GET(禁止新建 / PATCH) |
| 非终态 | 平台会回源渠道刷新状态后再返回 |
| 终态 | 直接返回本地快照(complete / expired / canceled 等) |
6.2 请求示例
GET /open/v1/acquiring/checkout-sessions/cs_xxx HTTP/1.1
Host: {host}
X-Access-Key-Id: ak_acq_xxx
X-Merchant-Code: M2026...
X-Vcc-Timestamp: 1723276800
X-Vcc-Signature: ...
6.3 成功响应 data
| 字段 | 类型 | 说明 |
id | string | Checkout Session ID |
url | string | 托管收银台 URL |
status | string | open / complete / expired 等 |
amount | integer | 美分 |
currency | string | 如 usd |
clientOrderId | string|null | 商户订单号 |
merchantCode | string | 商户编码 |
createdAt | string | ISO-8601 |
expiresAt | string|null | 过期时间 ISO-8601 |
paymentIntentId | string|null | 关联 Intent;未知则为 null |
customMetadata | object | 含商户字段及平台注入的只读保留字段 |
config | object|null | 收银台配置快照(若有) |
freshness | string | 可选;回源失败时为 CACHED |
nonRealtime | boolean | 可选;与 freshness 同时出现 |
{
"code": "0",
"message": "OK",
"data": {
"id": "cs_xxx",
"url": "https://checkout.example/cs_xxx",
"status": "complete",
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"merchantCode": "M2026...",
"createdAt": "2026-08-10T10:00:00.000Z",
"expiresAt": "2026-08-11T10:00:00.000Z",
"paymentIntentId": "pi_xxx",
"customMetadata": {
"sku": "PLAN_PRO",
"uid": "u_10086"
},
"config": null
},
"traceId": "..."
}
履约权威:Session status=complete(或 Webhook checkout_session.completed)。
7. 列出 Checkout Session
GET /open/v1/acquiring/checkout-sessions
仅返回本商户本地映射快照,不会透传渠道账号级 list。list 不批量回源;要对单笔实时状态请用 §6 GET by id。
7.1 Query 参数
| 参数 | 类型 | 必填 | 说明 |
cursor | string | 否 | 上一页返回的 nextCursor;须为正整数字符串,非法 → 40001 |
limit | integer | 否 | 默认 20,最大 100 |
status | string | 否 | open / complete / expired |
clientOrderId | string | 否 | 精确匹配商户订单号 |
排序:按本地主键 id DESC(近似 newest first)。
7.2 请求示例
GET /open/v1/acquiring/checkout-sessions?limit=20&status=open HTTP/1.1
Host: {host}
X-Access-Key-Id: ak_acq_xxx
X-Merchant-Code: M2026...
X-Vcc-Timestamp: 1723276800
X-Vcc-Signature: ...
7.3 成功响应 data
| 字段 | 类型 | 说明 |
items | array | Session 列表;元素字段同创建响应,但不含 customMetadata / config |
nextCursor | string|null | 下一页游标;无更多数据时为 null |
count | integer | 本页条数 |
{
"code": "0",
"message": "OK",
"data": {
"items": [
{
"id": "cs_xxx",
"url": "https://checkout.example/cs_xxx",
"status": "open",
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"merchantCode": "M2026...",
"createdAt": "2026-08-10T10:00:00.000Z",
"expiresAt": "2026-08-11T10:00:00.000Z",
"paymentIntentId": null
}
],
"nextCursor": "42",
"count": 1
},
"traceId": "..."
}
8. 更新 Checkout Session
PATCH /open/v1/acquiring/checkout-sessions/{id}
仅本地归属本商户且本地状态为 open 时可更新;经渠道 PATCH 成功后回写本地 amount / expiresAt。不会因 PATCH 单独出站 Webhook。
8.1 路径与门禁
| 项 | 说明 |
{id} | Session ID |
| 归属 | 非本商户 → 40401 |
| 开通 | acquiring_enabled=0 → 40315 |
| 本地非 open | → 40916 OPEN_API_ACQUIRING_SESSION_NOT_PATCHABLE |
| 渠道冲突(支付中/终态等 409) | → 40916 |
| 渠道业务拒绝(400 等) | → 40060 |
8.2 请求体
| 字段 | 类型 | 必填 | 说明 |
amount | integer | 条件 | 美分;范围同创建 1…99999999 |
expiresAt | string | 条件 | ISO-8601;须距今 ≥ 30 分钟且 ≤ 7 天 |
amount 与 expiresAt 至少填一个。未知顶层字段 → 40001。
8.3 请求示例
PATCH /open/v1/acquiring/checkout-sessions/cs_xxx HTTP/1.1
Host: {host}
Content-Type: application/json
X-Access-Key-Id: ak_acq_xxx
X-Merchant-Code: M2026...
X-Vcc-Timestamp: 1723276800
X-Vcc-Signature: ...
{
"amount": 2999,
"expiresAt": "2026-08-12T12:00:00.000Z"
}
8.4 成功响应 data
字段同 §6 查询(含 customMetadata / config 若有)。
{
"code": "0",
"message": "OK",
"data": {
"id": "cs_xxx",
"url": "https://checkout.example/cs_xxx",
"status": "open",
"amount": 2999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"merchantCode": "M2026...",
"createdAt": "2026-08-10T10:00:00.000Z",
"expiresAt": "2026-08-12T12:00:00.000Z",
"paymentIntentId": null,
"customMetadata": {
"sku": "PLAN_PRO",
"uid": "u_10086"
},
"config": null
},
"traceId": "..."
}
9. 列出 Payment Intent
GET /open/v1/acquiring/payment-intents
仅本商户已映射 Intent(本地表)。不会透传渠道账号级 list。关停后仍允许查询。
9.1 Query 参数
| 参数 | 类型 | 必填 | 说明 |
cursor | string | 否 | 上一页 nextCursor;非法 → 40001 |
limit | integer | 否 | 默认 20,最大 100 |
status | string | 否 | pending / processing / succeeded / failed / canceled |
checkoutSessionId | string | 否 | 精确匹配关联 Session ID |
9.2 请求示例
GET /open/v1/acquiring/payment-intents?status=succeeded&limit=20 HTTP/1.1
Host: {host}
X-Access-Key-Id: ak_acq_xxx
X-Merchant-Code: M2026...
X-Vcc-Timestamp: 1723276800
X-Vcc-Signature: ...
9.3 成功响应 data
| 字段 | 类型 | 说明 |
items | array | Intent 对象列表(字段见 §10.3) |
nextCursor | string|null | 下一页游标 |
count | integer | 本页条数 |
{
"code": "0",
"message": "OK",
"data": {
"items": [
{
"id": "pi_xxx",
"status": "succeeded",
"amount": 1999,
"currency": "usd",
"checkoutSessionId": "cs_xxx",
"clientOrderId": "ORD-20260810-001",
"createdAt": "2026-08-10T10:01:00.000Z",
"updatedAt": "2026-08-10T10:05:00.000Z"
}
],
"nextCursor": null,
"count": 1
},
"traceId": "..."
}
10. 查询 Payment Intent
GET /open/v1/acquiring/payment-intents/{id}
对账权威:Intent status=succeeded(或 Webhook payment_intent.succeeded)。履约仍以 Session complete 为主路径。
10.1 路径与规则
| 项 | 说明 |
{id} | Payment Intent ID |
| 归属 | 本地映射属本商户,或渠道响应含本商户 checkoutSessionId → 允许;否则 40401 |
| 无 Session 关联(如开票来源 Intent) | 40401(本产品线不服务) |
| 非终态 | 回源渠道刷新并 upsert |
| 终态 | 可直接返回本地(succeeded / failed / canceled) |
| 回源失败且本地有行 | 200 + 本地快照 + freshness: CACHED |
10.2 请求示例
GET /open/v1/acquiring/payment-intents/pi_xxx HTTP/1.1
Host: {host}
X-Access-Key-Id: ak_acq_xxx
X-Merchant-Code: M2026...
X-Vcc-Timestamp: 1723276800
X-Vcc-Signature: ...
10.3 成功响应 data
| 字段 | 类型 | 说明 |
id | string | Payment Intent ID |
status | string | pending / processing / succeeded / failed / canceled |
amount | integer | 美分 |
currency | string | 如 usd |
checkoutSessionId | string | 关联 Checkout Session |
clientOrderId | string|null | 来自关联 Session |
createdAt | string | ISO-8601 |
updatedAt | string | ISO-8601 |
freshness / nonRealtime | — | 可选;回源失败降级时出现 |
{
"code": "0",
"message": "OK",
"data": {
"id": "pi_xxx",
"status": "succeeded",
"amount": 1999,
"currency": "usd",
"checkoutSessionId": "cs_xxx",
"clientOrderId": "ORD-20260810-001",
"createdAt": "2026-08-10T10:01:00.000Z",
"updatedAt": "2026-08-10T10:05:00.000Z"
},
"traceId": "..."
}
如何拿到 Intent id:Session 响应 / completed Webhook 的 paymentIntentId(可能暂时为 null)、本接口 list、或 payment_intent.* 出站事件。创建 Session 当下 Intent 可能尚未生成,null 为合法中间态。
11. 收单 Webhook(平台 → 商户)
独立配置:收单回调使用独立 Webhook Secret(与 API SecretKey 不同)。一期由运营为商户配置回调 URL 与 Secret。
11.1 HTTP 约定
| 项 | 说明 |
| 方法 | POST |
| Content-Type | application/json; charset=utf-8 |
| 成功 | 返回 HTTP 2xx(建议 200);非 2xx 可能重试 |
| 超时 | 连接/读超时约数秒级(以部署为准) |
11.2 签名头(与 Open API 入站算法不同)
| 头 | 说明 |
X-Vcc-Signature | HMAC-SHA256,小写 hex |
X-Vcc-Timestamp | Unix 时间戳,单位毫秒 |
X-Vcc-Nonce | 32 字符 hex |
X-Vcc-Event-Id | 与 body eventId 一致,用于幂等 |
payload = timestamp + nonce + body_raw_utf8
expected = hex_lower( HMAC_SHA256( UTF8(WebhookSecret), UTF8(payload) ) )
body 必须用原始请求体,勿重新 JSON 序列化。
- Webhook Secret ≠ API SecretKey。
- 建议校验时间戳偏差,并对
eventId / nonce 去重。
11.3 事件类型
| eventType | 说明 |
merchant.webhook.acquiring.checkout_session.completed | Session → complete(履约权威);data 可含 paymentIntentId(可为 null) |
merchant.webhook.acquiring.payment_intent.succeeded | Intent → succeeded(对账通过) |
merchant.webhook.acquiring.payment_intent.failed | Intent → failed(非退款确认) |
11.4 载荷结构(Session completed)
{
"eventType": "merchant.webhook.acquiring.checkout_session.completed",
"eventId": "mwh_evt_acq_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"occurredAt": "2026-08-10T10:05:00.000Z",
"merchantCode": "M2026...",
"data": {
"checkoutSessionId": "cs_xxx",
"status": "complete",
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"paymentIntentId": "pi_xxx",
"customMetadata": {
"sku": "PLAN_PRO",
"uid": "u_10086"
},
"slashEventId": "可选,渠道侧事件 ID",
"slashEventTimestamp": "可选"
}
}
| data 字段 | 说明 |
checkoutSessionId | 与 Open API Session id 相同 |
status | 完成态语义(complete) |
amount / currency | 与创建一致 |
clientOrderId | 若创建时传入 |
paymentIntentId | 已知则 string;未知则为 null |
customMetadata | 创建时写入并含平台注入字段 |
slashEventId / slashEventTimestamp | 可选,渠道侧追踪 |
11.5 载荷结构(Payment Intent 终态)
{
"eventType": "merchant.webhook.acquiring.payment_intent.succeeded",
"eventId": "mwh_evt_acq_pi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"occurredAt": "2026-08-10T10:05:01.000Z",
"merchantCode": "M2026...",
"data": {
"paymentIntentId": "pi_xxx",
"checkoutSessionId": "cs_xxx",
"status": "succeeded",
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-20260810-001",
"updatedAt": "2026-08-10T10:05:00.000Z"
}
}
| data 字段 | 说明 |
paymentIntentId | 必有 |
checkoutSessionId | 关联 Session |
status | succeeded 或 failed |
amount / currency | 金额币种 |
clientOrderId | 可空 |
updatedAt | ISO-8601 |
同一资源同一终态的 eventId 稳定派生;商户须按 eventId 幂等。Session completed 与 Intent succeeded 可能先后到达,请按 clientOrderId / Session id 幂等履约。
12. 错误码(收单相关)
| HTTP | code | 含义 |
| 400 | 40001 / VALIDATION_ERROR | 参数非法(金额、币种、expiresAt、cursor、未知字段等) |
| 400 | 40003 / OPEN_API_IDEMPOTENCY_REQUIRED | 缺少 Idempotency-Key |
| 400 | 40060 / ACQUIRING_CHANNEL_REJECTED | 渠道拒绝(含部分 PATCH 业务拒绝) |
| 401 | OPEN_API_SIGNATURE_INVALID 等 | 签名 / 凭证 / 时钟问题 |
| 403 | 40314 / OPEN_API_CREDENTIAL_SCOPE_DENIED | Key 类型与路径不匹配 |
| 403 | 40315 / OPEN_API_ACQUIRING_DISABLED | 商户未开通收单(POST/PATCH) |
| 404 | 40401 / NOT_FOUND | Session/Intent 不存在或不属于本商户 |
| 409 | 40904 / OPEN_API_IDEMPOTENCY_CONFLICT | 同幂等键不同请求体 |
| 409 | 40916 / OPEN_API_ACQUIRING_SESSION_NOT_PATCHABLE | Session 当前不可 PATCH |
| 502 | 50260 / ACQUIRING_CHANNEL_UNAVAILABLE | 渠道不可用 |
13. 限流与约束
| 项 | 说明 |
| 限流 | 创建约 30 rpm / 查询约 120 rpm 每商户(可配置) |
| 币种 | 仅 usd |
| 金额 | 整数美分 1–99999999 |
| list | 默认 20 / 最大 100;仅本商户本地快照 |
| 幂等 | 创建必带;建议每个业务订单使用稳定且唯一的 Key |
| 安全 | Secret / Webhook Secret 仅服务端持有,禁止下发到浏览器或 App |
14. 签名示例(Python)
14.1 调用 Open API(创建 Session)
import hashlib, hmac, json, time, uuid, urllib.request
BASE = "https://{host}"
AK = "YOUR_ACQUIRING_ACCESS_KEY"
SK = "YOUR_API_SECRET"
MERCHANT = "M..."
body_obj = {
"amount": 1999,
"currency": "usd",
"clientOrderId": "ORD-001",
"customMetadata": {"sku": "PLAN_PRO"},
}
body = json.dumps(body_obj, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
method = "POST"
path = "/open/v1/acquiring/checkout-sessions"
query = ""
body_hash = hashlib.sha256(body).hexdigest()
canonical = f"{method}\n{path}\n{query}\n{body_hash}"
ts = str(int(time.time()))
sig = hmac.new(SK.encode(), canonical.encode(), hashlib.sha256).hexdigest()
req = urllib.request.Request(
BASE + path,
data=body,
method=method,
headers={
"Content-Type": "application/json",
"X-Access-Key-Id": AK,
"X-Merchant-Code": MERCHANT,
"X-Vcc-Timestamp": ts,
"X-Vcc-Signature": sig,
"Idempotency-Key": str(uuid.uuid4()),
},
)
print(urllib.request.urlopen(req).read().decode())
14.2 验签收单 Webhook
import hashlib, hmac
def verify_acquiring_webhook(secret: str, timestamp: str, nonce: str, body: bytes, signature: str) -> bool:
payload = (timestamp + nonce).encode("utf-8") + body
expected = hmac.new(secret.encode("utf-8"), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature.lower())
15. 联调检查清单
- 使用 ACQUIRING Key,签名通过
- 未开通时创建返回
40315;开通后可创建并拿到 url
- 前端用
@slashfi/checkout-js 嵌入 url,能看到收银台;直接打开 url 出现 couldn't load(属预期)
- 同
Idempotency-Key 重放返回同一 id
- 改 body 同 Key →
40904
- 非
ACQUIRING 凭证调用本路径 → 40314
- list Session / Intent 仅见本商户数据;他商户 id →
40401
- PATCH open Session 成功;终态 / 关停分别得到
40916 / 40315
- Webhook 验签通过;按
eventId 幂等更新订单
GET Session 完成后可查到 complete;对账可用 Intent succeeded
16. 变更记录
| 日期 | 变更 |
| 2026-08-11 | 收单文档独立成册,去除其他产品交叉引用与品牌混用 |
| 2026-08-11 | 新增 §5.6:托管收银台必须用 @slashfi/checkout-js 嵌入;禁止浏览器直开 url |
| 2026-08-11 | 补齐 List / PATCH Session、Payment Intent get/list 完整字段表与请求响应示例;Webhook 三事件;错误码 40916 |
| 2026-08-11 | 发布独立 HTML 公网文档(本页) |
| 2026-08-10 | 初版:收单 Checkout Session 创建/查询、独立 Webhook、错误码与示例 |