概览

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

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

  • merchantId  —  商户 ID(请求必带)
  • apiPublicKey  —  商户机器 RSA 公钥(提交到后台,匹配的私钥只保留在商户服务器)
  • aesKey / aesIv  —  部分敏感字段加密用(如启用)
安全提醒:RSA 私钥永远不要上传、出现在请求体或 URL 里。仅提交机器公钥,私钥泄露后请立即在商户机器轮换密钥并更新公钥。

联调流程

  1. 商户后台获取 merchantId,生成密钥对并提交 apiPublicKey
  2. 在后台 API 对接 页提交机器 RSA 公钥,并配置 支付 URL / 代付 URL(接收异步通知)
  3. (可选)在 IP 白名单 配置允许调用的 IP;不配置则放行所有 IP
  4. 若回调接口启用了入站 IP 白名单,请放行平台回调出口 IP:134.209.144.185
  5. 按本文档签名规则组装请求 → 调用 create 接口下单
  6. 使用返回的 orderNo 或自有 merchantOrderNo 调用 query 查询订单状态
  7. 代收成功、上游明确失败及代付成功或失败时,平台会向您配置的回调 URL 推送异步通知,按回调验签规则验证;代付失败会退回本金和手续费

公共说明

请求方式

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

请求 base URL

商户 API:https://pay.fourpay.online。本文档中的接口路径均相对于该地址。

商户后台:https://admin.fourpay.online;收银台:https://h5.fourpay.online。代收下单后请使用响应中的 payUrl 跳转。

统一响应结构

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

金额格式

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

时间字段

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

RSA 签名规则

所有写接口、查询接口和余额接口都需带 sign 字段。商户先在「API 对接」提交 RSA-2048 以上公钥,私钥只保存在商户机器。

  1. 取出请求体中所有非空参数,剔除 sign;金额按两位小数格式化。
  2. 按 key ASCII 升序拼接为 k1=v1&k2=v2&...&kn=vn,不做 URL 编码,不追加 key。
  3. 使用商户 RSA 私钥执行 PKCS#1 v1.5 + SHA-256,把签名字节用标准 Base64 编码作为 sign。

完整的语言示例、公钥提交接口和安全要求见 Union Pay RSA 对接说明。旧版 MD5 签名不再用于新的商户 API 请求。

创建代收订单

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://h5.fourpay.online/?merchantId=SP20260101000001&orderNo=STABLEPAYIN20260428143022000123"
  }
}

查询代收订单

POST /api/payment/query

请求参数

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

merchantOrderNo 与 orderNo 至少传其一。

响应 data

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

创建代付订单

POST /api/payout/create

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

请求参数

字段类型必填说明
merchantIdstring必填商户 ID
merchantOrderNostring必填商户系统订单号,单商户内唯一
amountnumber必填付款金额,> 0,保留两位小数
beneNamestring条件必填收款人姓名,银行卡必填;UPI 可不传
beneAccountstring必填银行卡账号;UPI 模式填写收款 VPA,如 name@handle
beneIfscstring条件必填银行卡必填;UPI 不传
benePhonestring选填收款人手机号
beneEmailstring选填收款人邮箱
bankNamestring选填收款银行名
transferTypestring选填转账方式,BANK_TRANSFER(缺省)或 UPI
notifyUrlstring选填异步通知地址,覆盖商户后台默认配置
remarkstring选填备注
signstring必填签名
UPI 示例:{"transferType":"UPI","beneAccount":"name@handle",...}。收款姓名可空,不传 IFSC。当前自有钱包 UPI 代付仅支持 MobiKwik Pocket UPI 通道;签名算法、查单和成功回调与银行卡模式相同。

响应 data

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

查询代付订单

POST /api/payout/query

请求参数

同代收查询:传 merchantId + (merchantOrderNo 或 orderNo) + sign。

响应 data

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

查询商户余额

POST /api/balance/query

查询当前商户的实时余额。沿用商户启用状态、RSA 公钥和商户 API IP 白名单校验;无需后台登录 Token 或订单号。

请求参数

字段类型必填说明
merchantIdstring是商户号,使用已登记 RSA 私钥签名
signstring是RSA-SHA256 签名,规则见上方

签名与请求示例

仅 merchantId 参与签名;按 RSA 签名规则拼接 merchantId=YOUR_MERCHANT_ID 后,用商户私钥执行 RSA-SHA256 并 Base64 编码。不要对参数值做 URL 编码。

curl -X POST "$API_BASE_URL/api/balance/query" \
  -H 'Content-Type: application/json' \
  -d '{"merchantId":"YOUR_MERCHANT_ID","sign":"BASE64_RSA_SIGNATURE"}'
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
import base64, requests

merchant_id = "YOUR_MERCHANT_ID"
private_key = serialization.load_pem_private_key(open("merchant-private.pem", "rb").read(), password=None)
sig = private_key.sign(("merchantId=" + merchant_id).encode(), padding.PKCS1v15(), hashes.SHA256())
response = requests.post(f"{api_base_url}/api/balance/query",
                         json={"merchantId": merchant_id, "sign": base64.b64encode(sig).decode()}, timeout=15)
print(response.json())

成功响应

{
  "code": 0,
  "message": "success",
  "data": {
    "merchantId": "YOUR_MERCHANT_ID",
    "currency": "INR",
    "balance": "10000.25",
    "freezeBalance": "500.00",
    "depositBalance": "100.00"
  }
}
字段类型说明
balancestring可用余额,单位 INR;已扣除冻结金额,不能再次减去 freezeBalance
freezeBalancestring冻结余额,单位 INR
depositBalancestring押金余额,单位 INR

金额返回十进制字符串以保留精度。查询不会扣款、冻结、解冻或发起付款。缺少参数返回 400,签名不符返回 401,商户不存在、停用、未配置密钥或 IP 不在白名单返回 403。

回调通知

代收订单达到 SUCCESS、代付订单达到 SUCCESS 或 FAILED 后,平台会向您的回调地址主动推送异步通知(POST application/json)。代付失败会先退回本金和手续费,再发送失败通知;处理中或等待上游确认的代付不会提前通知失败。请在收到通知后验签并检查 status,按订单号幂等处理,不要仅凭通知到达就判定付款成功。

回调地址

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

回调 IP 白名单

平台回调出口 IP:134.209.144.185。若您的回调接口通过服务器防火墙、WAF、CDN 或网关限制来源 IP,请将该 IP 加入入站白名单,否则平台回调可能被拦截并触发重试。

此回调出口 IP 与商户后台的 IP 白名单 配置用途不同:后台 IP 白名单用于限制商户调用平台 API 的来源 IP,不限制平台向商户发送回调。

触发时机

业务触发的终态
代收SUCCESS;上游明确失败的订单为 FAILED
代付SUCCESS / FAILED

通知体(代收示例)

{
  "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。所有字段均以字符串下发,金额为两位小数字符串。

代付 FAILED 通知额外包含 failReason(失败原因,参与签名)。失败退款金额为 amount + processFee;后台“未结算”表示未成功出款,不表示未退款,可按订单号核对 PAYOUT_REFUND 资金流水。

验签

当前异步回调使用兼容协议:排除 sign 和空值,按字段名 ASCII 升序拼接 key=value,字段之间用 & 连接,最后追加 &key=回调密钥,计算 MD5 并转为大写十六进制,与收到的 sign 比较。金额保持收到的两位小数字符串,不做 URL 编码。

回调密钥由运营单独提供;联调前请确认已拿到对应商户的密钥。商户 API 请求使用上方 RSA-SHA256,回调密钥与商户 RSA 密钥用途不同。

应答与重试

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

订单状态

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

业务错误码

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

cURL 示例

以代付下单为例(请把 merchantId、sign、baseURL 替换为您自己的):

curl -X POST https://pay.fourpay.online/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":            "BASE64_RSA_SIGNATURE"
  }'

Python 示例

展示 RSA-SHA256 签名 + 代付下单流程;可直接保存为 demo.py 运行(需要 cryptography)。

import base64, requests, time
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

BASE_URL = "https://pay.fourpay.online"
MERCHANT_ID = "SP20260101000001"
private_key = serialization.load_pem_private_key(open("merchant-private.pem", "rb").read(), password=None)
params = {
    "merchantId": MERCHANT_ID,
    "merchantOrderNo": f"DEMO_{int(time.time())}",
    "amount": "500.00",
    "beneName": "Raj Kumar",
    "beneAccount": "1234567890123456",
    "beneIfsc": "HDFC0001234",
}
canonical = "&".join(f"{k}={params[k]}" for k in sorted(params) if params[k] != "")
sig = private_key.sign(canonical.encode(), padding.PKCS1v15(), hashes.SHA256())
body = dict(params, sign=base64.b64encode(sig).decode())
body["amount"] = 500.00
print(requests.post(f"{BASE_URL}/api/payout/create", json=body, timeout=15).json())