概览

StablePay 商户开放接口提供 代收(Payment)代付(Payout) 两类业务能力。所有接口为 POST application/json,使用 MD5 签名认证。

接入前请向 StablePay 商务申请商户账号,登录商户后台 → API 对接 页面获取以下凭据:

  • merchantId  —  商户 ID(请求必带)
  • apiKey  —  商户密钥(仅用于本地签名,不要传输
  • aesKey / aesIv  —  部分敏感字段加密用(如启用)
安全提醒:apiKey 永远不要出现在请求体或 URL 里。如不慎泄露,立即在后台重置

联调流程

  1. 商户后台获取 merchantId + apiKey
  2. 在后台 API 对接 页配置 支付 URL / 代付 URL(接收异步通知)
  3. (可选)在 IP 白名单 配置允许调用的 IP;不配置则放行所有 IP
  4. 按本文档签名规则组装请求 → 调用 create 接口下单
  5. 使用返回的 orderNo 或自有 merchantOrderNo 调用 query 查询订单状态
  6. 订单状态变更时,平台会向您配置的回调 URL 推送异步通知(按相同签名规则验签)

公共说明

请求方式

  • 方法:POST
  • Content-Type:application/json
  • 字符集:UTF-8

请求 base URL

由 StablePay 商务为您单独提供(如 https://api.example.com)。本文档中接口路径均为相对路径。

统一响应结构

{
  "code": 0,           // 0=成功,非0=失败
  "message": "success",
  "data": { ... }      // 业务数据,仅 code=0 时有意义
}

金额格式

所有金额字段在 请求体 中以 number 类型传输(如 500.00),在 签名计算 时必须格式化为保留两位小数的字符串(如 "500.00",而不是 "500""500.000")。

时间字段

所有时间字段为 Unix 时间戳(秒级,int64)。前端展示请自行转换。

签名规则

所有写接口(create)和查询接口(query)都需带 sign 字段。算法如下:

  1. 取出请求体中所有非空参数(值为 ""null 或缺省的字段不参与签名),剔除 sign 本身
  2. 金额类字段(如 amount)转成 %.2f 字符串。
  3. 把参数按 key 升序(ASCII)排序,拼接为 k1=v1&k2=v2&...&kn=vn
  4. 末尾追加 &key={apiKey}
  5. 对最终字符串做 MD5(32 位),转大写,作为 sign

示例

请求体(amount=500.00,merchantOrderNo=E2E_OUT_001):

{
  "merchantId":      "SP20260101000001",
  "merchantOrderNo": "E2E_OUT_001",
  "amount":          500.00,
  "beneName":        "Raj Kumar",
  "beneAccount":     "1234567890123456",
  "beneIfsc":        "HDFC0001234",
  "benePhone":       "9876543210",
  "notifyUrl":       "https://your.site/notify",
  "sign":            "<由本算法计算>"
}

排序拼接(注意 amount 用 500.00):

amount=500.00&beneAccount=1234567890123456&beneIfsc=HDFC0001234&beneName=Raj Kumar&benePhone=9876543210&merchantId=SP20260101000001&merchantOrderNo=E2E_OUT_001&notifyUrl=https://your.site/notify&key=<您的 apiKey>

对上述字符串 MD5 → 转大写 → 即为 sign

注意:拼接时 =& 之间不留空格;URL 字符(如 : /做编码;中文/空格保持原样。

创建代收订单

POST /api/payment/create

商户向用户收款时调用。下单成功后平台返回平台订单号;用户付款 / 平台对账完成后通过异步通知或主动查询接口获取最终状态。

请求参数

字段类型必填说明
merchantIdstring必填商户 ID
merchantOrderNostring必填商户系统订单号,单商户内唯一
amountnumber必填下单金额,> 0,保留两位小数
customerNamestring选填付款人姓名
customerEmailstring选填付款人邮箱
customerPhonestring选填付款人手机号
notifyUrlstring选填异步通知地址,覆盖商户后台默认配置
returnUrlstring选填付款完成后浏览器跳回地址
remarkstring选填备注,原样回显
signstring必填签名,详见签名规则

响应 data

字段类型说明
orderNostring平台订单号(以 STABLEPAYIN 开头)
merchantOrderNostring回显商户订单号
txRefstring平台交易参考号(6 位大写字母/数字)。用户 UPI 转账时必须原样填入「备注」,平台按它回扫收款银行短信 Payer Remark 匹配订单
amountstring实付金额,保留两位小数
payableAmountstring应付金额,保留两位小数(当前与 amount 一致)
statusstring初始状态固定 PENDING
expiryDateint64订单过期时间(Unix 秒),默认 30 分钟
remarksstringUPI 转账备注,值等于 txRef,用户转账时须原样填写
beneNamestring收款人姓名
beneAccountstring收款人银行账号
beneIfscstring收款人银行 IFSC
bankstring收款银行名称
upiVpastring收款 UPI VPA(收银台/UPI 拉起用)
payUrlstring平台收银台地址,可直接跳转给终端用户扫码 / 拉起 UPI 应用付款

说明:若商户自建收银页,可用 beneName/beneAccount/beneIfsc/bank/upiVpa 展示收款信息,并提示用户把 txRef 填入转账备注;若直接使用平台收银台,跳转 payUrl 即可。

返回示例

{
  "code": 0,
  "message": "success",
  "data": {
    "orderNo": "STABLEPAYIN20260428143022000123",
    "merchantOrderNo": "E2E_IN_001",
    "txRef": "K7M9P2",
    "amount": "500.00",
    "payableAmount": "500.00",
    "status": "PENDING",
    "expiryDate": 1714203600,
    "remarks": "K7M9P2",
    "beneName": "Raj Kumar",
    "beneAccount": "1234567890123456",
    "beneIfsc": "HDFC0001234",
    "bank": "HDFC Bank",
    "upiVpa": "raj@okhdfc",
    "payUrl": "https://pay.stablepay.example/?merchantId=SP20260101000001&orderNo=STABLEPAYIN20260428143022000123"
  }
}

查询代收订单

POST /api/payment/query

请求参数

字段类型必填说明
merchantIdstring必填商户 ID
merchantOrderNostring二选一商户订单号
orderNostring二选一平台订单号
signstring必填签名

merchantOrderNoorderNo 至少传其一。

响应 data

字段类型说明
orderNostring平台订单号
merchantOrderNostring商户订单号
amountstring下单金额
paidAmountstring实际到账金额
statusstring订单状态,见订单状态
utrstring银行端流水号(成功后才有)
createTimeint64创建时间(Unix 秒)
updateTimeint64最近更新时间(Unix 秒)

创建代付订单

POST /api/payout/create

商户向第三方账户付款时调用。下单瞬间扣减商户可用余额(金额 + 手续费),订单失败后退款回账。

请求参数

字段类型必填说明
merchantIdstring必填商户 ID
merchantOrderNostring必填商户系统订单号,单商户内唯一
amountnumber必填付款金额,> 0,保留两位小数
beneNamestring必填收款人姓名
beneAccountstring必填收款人账号
beneIfscstring必填收款行 IFSC 代码
benePhonestring选填收款人手机号
beneEmailstring选填收款人邮箱
bankNamestring选填收款银行名
transferTypestring选填转账方式,缺省 BANK_TRANSFER
notifyUrlstring选填异步通知地址,覆盖商户后台默认配置
remarkstring选填备注
signstring必填签名

响应 data

字段类型说明
orderNostring平台订单号(以 STABLEPAYOUT 开头)
merchantOrderNostring回显商户订单号
amountstring金额
processFeestring手续费
statusstring初始状态固定 PENDING
余额校验:下单金额 + 手续费必须 ≤ 商户可用余额,否则返回 余额不足

查询代付订单

POST /api/payout/query

请求参数

代收查询:传 merchantId + (merchantOrderNoorderNo) + sign

响应 data

字段类型说明
orderNostring平台订单号
merchantOrderNostring商户订单号
amountstring付款金额
processFeestring手续费
statusstring订单状态,见订单状态
utrstring银行端流水号
createTimeint64创建时间
updateTimeint64更新时间

回调通知

订单到达终态后,平台会向您的回调地址主动推送一次异步通知(POST application/json)。请在收到后验签并以订单号入账,不要仅凭通知到达就放行。

回调地址

  • 优先使用下单请求里传的 notifyUrl
  • 未传则回退商户后台配置的默认地址(代收 = paymentUrl,代付 = payoutUrl);
  • 两者都为空则不推送(订单会停留在后台「回调失败」列表,可人工处理)。

触发时机

业务触发的终态
代收SUCCESS
代付SUCCESSFAILED

通知体(代收示例)

{
  "merchantId":      "SP20260101000001",
  "orderNo":         "STABLEPAYIN20260101000001",
  "merchantOrderNo": "E2E_IN_001",
  "amount":          "500.00",
  "paidAmount":      "500.00",
  "status":          "SUCCESS",
  "utr":             "1234567890",
  "sign":            "<平台按签名规则计算>"
}

代付通知体字段为 merchantId / orderNo / merchantOrderNo / amount / processFee / status / utr / sign。所有字段均以字符串下发,金额为两位小数字符串。

验签

与请求签名规则完全一致:取通知体里所有非空字段(剔除 sign)按 key 升序拼成 k1=v1&k2=v2&...&key=<您的 apiKey>,MD5 转大写,与 sign 比对一致才可信任。

应答与重试

成功应答:HTTP 200 且响应体包含 success(或纯文本 ok)。任何其它情况(非 2xx、连接拒绝、超时、响应体未表示成功)均视为失败。
  • 每笔订单平台总共最多推送 3 次:首次推送失败后,每隔 60 秒再推一次,累计 3 次仍未收到成功应答即停止。
  • 停止后订单进入后台「回调失败」列表,运营可手动「重新发送」(会重置次数并立即再推一轮,同样最多 3 次)。
  • 请保证回调接口幂等:同一订单可能被推送多次。

订单状态

status说明
PENDING已创建,等待处理
SUCCESS成功(代收=已到账,代付=已打款)
FAILED失败(代付会自动解冻并退回余额)
EXPIRED已过期(仅代收)

业务错误码

code含义
0成功
400参数错误(缺字段、字段类型不对等)
401签名验证失败
403商户不存在 / IP 不在白名单 / 功能未开通
500服务端错误(含余额不足、订单号重复等业务异常)

cURL 示例

以代付下单为例(请把 merchantIdsignbaseURL 替换为您自己的):

curl -X POST https://api.example.com/api/payout/create \
  -H "Content-Type: application/json" \
  -d '{
    "merchantId":      "SP20260101000001",
    "merchantOrderNo": "E2E_OUT_001",
    "amount":          500.00,
    "beneName":        "Raj Kumar",
    "beneAccount":     "1234567890123456",
    "beneIfsc":        "HDFC0001234",
    "benePhone":       "9876543210",
    "notifyUrl":       "https://your.site/notify",
    "sign":            "ABCDEF0123456789ABCDEF0123456789"
  }'

Python 示例

展示完整签名 + 下单流程;可直接保存为 demo.py 运行。

import hashlib, requests, time

BASE_URL = "https://api.example.com"
MERCHANT_ID  = "SP20260101000001"
API_KEY      = "<在商户后台 API 对接页复制>"
AMOUNT       = 500.00

def compute_sign(params: dict, api_key: str) -> str:
    filtered = {k: str(v) for k, v in params.items() if k != "sign" and str(v) != ""}
    raw = "&".join(f"{k}={filtered[k]}" for k in sorted(filtered)) + f"&key={api_key}"
    return hashlib.md5(raw.encode()).hexdigest().upper()

def create_payout():
    sign_params = {
        "merchantId":      MERCHANT_ID,
        "merchantOrderNo": f"DEMO_{int(time.time())}",
        "amount":          f"{AMOUNT:.2f}",   # 签名时用字符串
        "beneName":        "Raj Kumar",
        "beneAccount":     "1234567890123456",
        "beneIfsc":        "HDFC0001234",
        "benePhone":       "9876543210",
        "notifyUrl":       "https://your.site/notify",
    }
    body = dict(sign_params)
    body["sign"]   = compute_sign(sign_params, API_KEY)
    body["amount"] = AMOUNT                   # 请求体里 amount 是 float
    return requests.post(f"{BASE_URL}/api/payout/create", json=body, timeout=15).json()

print(create_payout())