SMPAY OPENAPI
订单接口文档
用于创建支付订单、查询订单状态,以及接收订单终态通知。所有金额均为人民币,保留两位小数并向下取整。
认证与签名
每个商户固定一把 API 密钥。请仅保存在服务端环境变量,不能放入浏览器、小程序或 App 客户端。
商户始终只有一把生效中的 API 密钥。刷新后,新请求立即使用新密钥,旧密钥不能再调用 OpenAPI;请求签名不需要传递密钥版本。
| 请求头 | 必填 | 说明 |
|---|---|---|
Content-Type |
创建订单必填 | 固定为 application/json。 |
X-SMPAY-Merchant-Code |
是 | 商户编号。 |
X-SMPAY-Timestamp |
是 | 10 位 Unix 时间戳(秒)。与平台时间相差不得超过 300 秒。 |
X-SMPAY-Nonce |
是 | 每次请求唯一的 16-128 位随机字符串,可使用字母、数字、下划线和连字符。 |
X-SMPAY-Signature |
是 | 签名结果,64 位小写十六进制 HMAC-SHA-256。 |
X-SMPAY-Idempotency-Key |
条件必填 | 创建订单未传商户订单号时必须传。16-128 位字母、数字、点、下划线或连字符。 |
HTTP_METHOD
PATH
TIMESTAMP
NONCE
SHA256(REQUEST_BODY)
使用当前 API 密钥作为 HMAC-SHA-256 的密钥。创建订单签名的是原始 JSON 字节;查询订单没有请求体,最后一行是空字符串的 SHA-256。查询时的 PATH 必须包含原始查询串(包括参数顺序和 URL 编码)。
POST
创建订单
/api/v1/orders。平台会校验商户状态、金额、地区、通道额度与收款用户可用余额后分配收款通道。
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
merchant_order_no |
string | 否 |
商户订单号,等同 out_trade_no;最长 64
字符。同一商户内唯一。两者同时传时必须相同。
|
out_trade_no |
string | 否 |
merchant_order_no
的兼容别名。不要同时传不同值。
|
payment_method |
string | 是 |
alipay(支付宝)、wechat(微信)或
aggregate_qr(聚合码)。
|
amount |
number/string | 是 |
订单金额,最小 0.01,按两位小数向下取整。
|
province |
string | 是 | 省份,最长 32 字符。与管理后台的地区匹配模式共同决定可用通道。 |
city |
string | 是 | 城市,最长 32 字符。 |
subject |
string | 是 | 订单标题,最长 128 字符。 |
notify_url |
string | 否 | 订单状态通知地址,必须为安全的 HTTPS 公网地址。 |
attach |
string | 否 | 商户自定义回传字段,最长 256 字符;原样返回在查询与回调中。 |
{
"merchant_order_no": "M202609240001",
"payment_method": "alipay",
"amount": "100.00",
"province": "广东省",
"city": "深圳市",
"subject": "会员充值",
"notify_url": "https://merchant.example.com/smpay/notify",
"attach": "user_10086"
}
成功响应
{
"code": 0,
"message": "ok",
"data": {
"platform_order_no": "SM240924001",
"merchant_order_no": "M202609240001",
"status": "processing",
"payment_method": "alipay",
"payment_method_name": "支付宝",
"amount": "100.00",
"currency": "CNY",
"province": "广东省",
"city": "深圳市",
"subject": "会员充值",
"attach": "user_10086",
"payment_info": { "channel_name": "张**的支付宝", "qrcode_url": "/uploads/channels/0123456789abcdef0123456789abcdef.png" },
"created_at": "2026-09-24 10:36:12",
"expires_at": "2026-09-24 10:41:12",
"timeout_seconds": 300
}
}
重复提交相同商户订单号或幂等键会返回原订单,不会重复冻结余额或重复分配通道。未提供商户订单号时,平台会生成
MO... 商户订单号。
GET
查询订单
/api/v1/orders/query。仅可查询当前商户自身的订单。
| 查询参数 | 必填 | 说明 |
|---|---|---|
platform_order_no |
二选一 | 平台订单号,例如 SM240924001。 |
merchant_order_no |
二选一 | 商户订单号。 |
out_trade_no |
否 |
merchant_order_no
的兼容别名。不得与其传递不同值。
|
三个参数最终只能定位一个订单;请求头、签名规则与创建订单相同,查询请求的签名 PATH 必须包含完整查询串,正文为空字符串。
{
"code": 0,
"message": "ok",
"data": {
"platform_order_no": "SM240924001",
"merchant_order_no": "M202609240001",
"status": "completed",
"payment_method": "alipay",
"payment_method_name": "支付宝",
"amount": "100.00",
"currency": "CNY",
"province": "广东省",
"city": "深圳市",
"subject": "会员充值",
"attach": "user_10086",
"qrcode_url": "/uploads/channels/0123456789abcdef0123456789abcdef.png",
"created_at": "2026-09-24 10:36:12",
"expires_at": "2026-09-24 10:41:12",
"completed_at": "2026-09-24 10:38:20",
"cancelled_at": null,
"refunded_at": null,
"refund_amount": null
}
}
回调通知
订单进入已完成、已取消或已退回状态时,平台向订单创建时传入的
notify_url 发送 JSON
回调。未填写回调地址时,请通过查询订单接口获取状态。
按 event_id 去重,并以较大的
event_sequence 覆盖较旧事件。收到任意 2xx
即视为成功。
回调请求头
| 请求头 | 说明 |
|---|---|
Content-Type |
application/json |
X-SMPAY-Signature |
使用固定 API 密钥对原始回调请求体计算的 HMAC-SHA-256 小写摘要。 |
X-SMPAY-Event-Id |
与请求体 event_id 一致。 |
X-SMPAY-Key-Version |
回调签名所用密钥的版本号。刷新 API 密钥后,已入队事件仍使用创建时的版本。 |
回调参数
| 字段 | 类型 | 说明 |
|---|---|---|
event_id |
string | 全局唯一事件标识,用于幂等。 |
event_type |
string |
order.completed、order.cancelled、order.refunded。
|
event_sequence |
integer | 同一订单递增事件序号。 |
occurred_at |
string | 状态发生时间,格式 Y-m-d H:i:s。 |
platform_order_no |
string | 平台订单号。 |
merchant_order_no |
string | 商户订单号。 |
merchant_code |
string | 商户编号。 |
status |
string |
completed、cancelled 或
refunded。
|
amount |
string | 订单金额,两位小数。 |
currency |
string | 固定 CNY。 |
payment_method |
string | 支付方式编码。 |
subject |
string | 订单标题。 |
attach |
string | 创建订单时的回传字段。 |
completed_at |
string/null | 完成时间,仅已完成时有值。 |
cancelled_at |
string/null | 取消时间,仅已取消时有值。 |
refunded_at |
string/null | 退回时间,仅已退回时有值。 |
refund_amount |
string/null | 全额退回金额,仅已退回时有值。 |
{
"event_id": "evt_01J8SMPAY0001",
"event_type": "order.completed",
"event_sequence": 1,
"occurred_at": "2026-09-24 10:38:20",
"platform_order_no": "SM240924001",
"merchant_order_no": "M202609240001",
"merchant_code": "XH202609",
"status": "completed",
"amount": "100.00",
"currency": "CNY",
"payment_method": "alipay",
"subject": "会员充值",
"attach": "user_10086",
"completed_at": "2026-09-24 10:38:20",
"cancelled_at": null,
"refunded_at": null,
"refund_amount": null
}
失败重试计划:首次立即发送;随后 1、5、15、30 分钟,1、3、6、12
小时,最多 9 次。网络异常、超时、408、429 和 5xx 会重试;其他 4xx
不重试。接收方返回 Retry-After 时,平台会采用不早于默认退避的等待时间。回调地址必须是可公开解析的 HTTPS 地址,不能指向内网或保留地址,且不跟随重定向。
密钥刷新后,请保留旧的回调验签密钥至少 24 小时,并按
X-SMPAY-Key-Version 选择密钥验签;新密钥只用于新的 API 请求和新创建的回调事件。
PHP SDK
SDK 会为每次请求生成新的时间戳和随机串;创建订单仅在提供稳定订单号或幂等键时自动重试。
下载 PHP SDK。SDK 强制 HTTPS、校验证书、不跟随重定向,并读取 Retry-After。
$client = new SmpayClient($baseUrl, $merchantCode, $activeApiKey);
// 记录商户后台返回的 api_key_version,并保留旧回调密钥。
$client->setCallbackKey(3, $previousCallbackKey)
->setCallbackKey(4, $activeApiKey);
$isValid = $client->verifyCallbackRequest($rawBody, [
'X-SMPAY-Signature' => $_SERVER['HTTP_X_SMPAY_SIGNATURE'] ?? '',
'X-SMPAY-Event-Id' => $_SERVER['HTTP_X_SMPAY_EVENT_ID'] ?? '',
'X-SMPAY-Key-Version' => $_SERVER['HTTP_X_SMPAY_KEY_VERSION'] ?? '',
]);
错误码与订单状态
所有接口统一返回 code、message 与
data。错误时 data 为
null。
| HTTP | 业务 code | 含义 |
|---|---|---|
| 400 | INVALID_JSON |
JSON 格式不正确。 |
| 401 | UNAUTHORIZED |
商户编号、签名或认证字段不合法。 |
| 401 | TIMESTAMP_EXPIRED |
时间戳超出 300 秒允许窗口。 |
| 403 | IP_NOT_ALLOWED |
管理后台已启用白名单,当前来源 IP 未命中 CIDR 规则。 |
| 409 | REPLAY_DETECTED |
随机串已被使用,请生成新的随机串后重试。 |
| 413 | PAYLOAD_TOO_LARGE |
JSON 请求体超过 16 KiB。 |
| 415 | UNSUPPORTED_MEDIA_TYPE |
创建订单未使用 application/json。 |
| 422 | INVALID_REQUEST |
业务参数、订单状态或通道分配条件不满足。 |
| 422 | IDEMPOTENCY_KEY_REQUIRED |
未传商户订单号时缺少幂等键。 |
| 429 | RATE_LIMITED |
超过限流阈值;读取响应头 Retry-After 后重试。
|
交易中completed
已完成cancelled
已取消refunded
已退回
限流与 IP 白名单
平台会同时控制商户维度和来源 IP 维度的频率,避免影响通道分配和账务处理。
| 规则 | 阈值 | 处理方式 |
|---|---|---|
| OpenAPI 入口(签名校验前) | 同一来源 IP 每分钟 60 次 |
超限返回 HTTP 429、RATE_LIMITED 和
Retry-After。
|
| 创建订单 | 每商户每分钟 120 次 | |
| 查询订单 | 每商户每分钟 300 次 | |
| 开放 API 合计 | 同一来源 IP 每分钟 600 次(签名通过后) |
IP
白名单默认关闭。管理后台保存并启用后,所有商户的 OpenAPI 只允许命中
CIDR 规则的来源 IP 调用;请勿依赖客户端自行传递的
X-Forwarded-For。
同时命中多条规则时,以更严格的阈值为准;同一出口 IP 的生产调用应按 60 次/分钟进行容量规划。