写在前面
- 文中将${seq}定义为参数名为seq的参数,相关的表中有参数描述
- 文中将${timeout*}定义为可选的参数timeout
- 本文当中所有涉及到时间的参数都使用精确到毫秒的时间戳,例1535595007983表示Thu Aug 30 2018 10:10:07 GMT+0800 (中国标准时间)
- 本文中涉及到分页的接口使用skip、limit参数,表示跳过skip行取至多limit行,返回头X-Total-Count参数表示查询总行数
- 所有接口使用HTTP状态码200表示成功,其他状态码表示失败,如有特例将在具体接口中说明
接口描述
http://pre.sovell.com/sovellpay/v2
http://pass.sovell.com/sovellpay/v2
接口中所有token参数都是通过OAuth2授权取得
R1.查询交易单
- 本接口调用频率同一订单不超过1次/秒
- 系统内只有一个订单,退款与支付同属一个订单
GET http://${domain}/sovellpay/v2/trade/${seq}
?timeout=${timeout*}
Authorization: Bearer ${token}
{
"id": ${id},
"status": ${status},
"status_msg": ${status_msg*},
"create_time": ${create_time},
"title": ${title},
"account_id": ${account_id*},
"notify_uri": ${notify_uri},
"seq": ${seq},
"trade_id": ${trade_id*},
"detail": [
{"tags": [${tags}...], "amount": ${amount}}
]
}
| 参数名 |
类型 |
说明 |
| id |
string |
服务器订单号 |
| seq |
string(36) |
客户端订单号 |
| title(128) |
string |
订单描述 |
| status |
|
订单状态 |
|
accepted |
订单处理中,结果未知,需要继续查询订单状态 |
|
normal |
已支付,就是支付成功,此时钱已经入服务商账户 |
|
refunding |
退款中,结果未知,需要继续查询订单状态 |
|
refund |
部分退款,此时钱部分回到用户账户 |
|
closed |
关闭,此时钱已经回到用户账户 |
| status_msg |
|
订单信息,如果有需要输出的时候,例如错误的时候 |
| create_time |
int |
支付时间戳(毫秒) |
| notify_uri |
string |
扫码支付时二维码信息 |
| account_id |
支付通道 |
见支持的支付通道定义
|
| trade_id |
string |
业务订单号,如果存在 |
| detail |
list |
明细,如果存在则输出 |
| └ tags |
list |
明细标签组合,这是一个集合,取值不仅限于所列出值,见支持的标签
|
| └ card |
string |
如果是卡支付的话则输出为卡号,非卡支付不输出 |
| └ amount |
int |
实付金额 |
| └ balance |
int |
交易后余额,wallet时给出该值 |
R2.事件总线
GET http://${domain}/sovellpay/v2/trade/event
?after=${index*}
&timeout=${timeout*}
&limit=${limit*}
Authorization: Bearer ${token}
| 参数名 |
类型 |
说明 |
| limit |
int |
返回最大条数,默认100 |
| index |
int |
初始是为0,之后的调用使用每次结果中的index |
| 其他 |
|
见其他请求参数说明
|
- 返回
PAYMENT数组,返回参数定义同R1.查询交易单
[{
"id": ${id},
"status": ${status},
"create_time": ${create_timestamp},
"title": ${title},
"goods": ${goods*},
"account_id": ${account_id*},
"notify_uri": ${notify_uri},
"seq": ${seq},
"index": ${index},
"trade_id": ${trade_id*}
}...]
R3.查询可用账户
GET http://${domain}/sovellpay/v2/accounts
Authorization: Bearer ${token}
[{
"name": ${name},
"account_id": ${account_id},
"id": ${mch_account},
"user_account": ${user_account},
"status": ${status},
"balance": ${balance*}
...
}...]
| 参数名 |
类型 |
说明 |
| name |
string |
账户名 |
| mch_account |
string |
商户账户 |
| user_account |
string |
用户账户 |
| status |
|
账户状态 |
|
normal |
正常 |
|
disabled |
禁用 |
| account_id |
支付通道 |
见支持的支付通道定义
|
| 参数名 |
类型 |
说明 |
| balance |
int |
余额(分) |
| property |
|
属性,可选 |
| └ name |
|
姓名,可选 |
| └ corp_id |
|
企业id,可选 |
| └ depart |
|
部门路径 |
| └ no |
|
工号 |
| what |
|
一定为wallet |
| 参数名 |
类型 |
说明 |
| balance |
int |
余额(分),表示剩余透支余额 |
| what |
|
一定为credit |
一般不需要体现在界面上
| 参数名 |
类型 |
说明 |
| balance |
int |
优惠余额 |
| what |
|
一定为helpbuy |
| 参数名 |
类型 |
说明 |
| balance |
int |
券余额(分),即券面额 |
| what |
|
一定为coupon |
|参数名|类型|说明|
|:---|:---|:---|:---|
|code | |结果状态码|
||204|查询成功,但找不到|
||其他|见其他返回参数说明|
C1.单用户统一交易接口
- 当用于刷卡支付时,即商户扫用户的码,调用时传入
pay_code即可
- 当用于扫码支付时,即用户扫商户的码,传入
app参数,然后使用返回的notify_uri生成二维码
- 当用于公众号支付时,页面直接跳转至
notify_uri,交易完成后会跳转至redirect_uri
- 当用于支付订单部分退款时,将原订单的
id传入original_id
POST http://${domain}/sovellpay/v2/trade?sign=${sign}
Authorization: Bearer ${token}
Content-Type: application/json
{
"intent": ${intent},
"title": ${title*},
"seq": ${seq},
"sub": ${sub},
"expire": ${expire*},
"attach": ${attach*},
"device": {
"id": ${device_id},
"name": ${device_name},
},
"goods": [{
"id": ${id},
"wechat_id": ${wechat_id},
"alipay_id": ${alipay_id},
"name": ${name},
"quantity": ${quantity},
"price": ${price}
}*...],
"pay_code": ${pay_code*},
"redirect_uri": ${redirect_uri*},
"app": ${app*},
"amount_due": ${amount_due*},
"original_id": ${original_id*},
"tags": [${tags}...]
}
| 参数名 |
类型 |
说明 |
| seq |
string |
客户端订单号(商户订单号、下位订单号)当退款时请使用和原订单不同的seq |
| title(可选) |
string(128) |
订单描述 |
| intent |
string |
交易意图,参数说明见下文 |
| original_id(退款时必选) |
string |
原单据${id},退款时该参数必须提供 |
| pay_code(B扫C时必选) |
string |
支付码,支持微信反扫、支付宝反扫 |
| redirect_uri(可选) |
string |
交易完成后的重定向地址 |
| app(可选) |
|
|
|
wechat |
微信扫码支付 |
|
alipay |
支付宝扫码支付 |
| goods(可选) |
|
商品,如果有 |
| └ id |
|
商品id |
| └ wechat_id |
|
微信支付定义的统一商品编号(如果有) |
| └ alipay_id |
|
支付宝支付定义的统一商品编号(如果有) |
| └ name |
|
商品名称 |
| └ quantity |
|
商品数量 |
| └ price |
|
商品单价(分) |
| operator(可选) |
string |
操作员,可空 |
| attach(可选) |
string |
附加字段,可空,支付宝赋值store_id,微信赋值device_info |
| device(可选) |
object |
设备附加属性 |
| └ device_id |
string |
设备id,统计时会使用这个id作为拆分凭据 |
| └ device_name |
string |
设备名 |
| amount_due(可选) |
int |
整单的应付金额,仅用作记录,为空是则自动计算 |
| expire(可选) |
string |
过期时间,由于各交易通道对过期的定义的差异,该时间定义为最大过期时间,实际过期时间会存在差异,没有单位时默认毫秒,格式为xxxs,默认300s,最大600s |
| tags(可选) |
|
交易描述标签,用于策略消费 |
|
breakfast |
早餐 |
|
lunch |
午餐 |
|
dinner |
晚餐 |
|
snack |
夜宵 |
| 其他 |
|
见其他请求参数说明
|
| 格式 |
说明 |
| pay ${amount} |
支付指定金额(分) |
| pay ${amount} use #${user_account} |
支付指定金额(分),使用用户的指定账户 |
| charge ${amount} |
充值指定金额(分)(例如 charge 1) |
| refund ${amount} |
退款指定金额(分)(例如 refund 1) |
| refund * |
全额退款 |
D1.全额撤单
DELETE http://${domain}/sovellpay/v2/trade/${seq}?sign=${sign}
Authorization: Bearer ${token}
其他说明
支持的支付通道定义
| 定义名 |
说明 |
| wechat |
微信 |
| alipay |
支付宝 |
| xqpos |
享钱 |
| kqpay |
快钱 |
| unionpay |
银联 |
| icbc |
工商银行 |
| jsb |
江苏银行 |
| cmb |
招商银行 |
| swiftpay |
兴业银行(威富通) |
| idish |
智盘本地卡 |
| third |
本地第三方系统 |
支持的标签
| 定义名 |
说明 |
| wx |
微信 |
| ali |
支付宝 |
| jd |
京东 |
| qq |
QQ |
| sh |
大众闪惠 |
| wing |
翼支付 |
| scan |
刷卡支付,B扫C,商户扫用户 |
| qr |
扫码支付,C扫B,用户扫商户 |
| h5 |
公众号支付 |
| app |
APP支付 |
| card |
卡支付 |
| wallet |
储值消费 |
| helpbuy |
代付优惠 |
| credit |
透支消费 |
| subsidy |
福利消费 |
| coupon |
券 |
| settle |
结算,一般不计入总金额 |
其他请求参数说明
| 参数名 |
类型 |
说明 |
| domain |
string |
接口域名 |
| token |
string |
访问令牌,通过OAuth2授权取得 |
| timeout |
string |
阻塞查询时用,传0则立即返回结果,否则在timeout时间内若交易单状态发生变化时返回结果,格式为xxxs,默认10s |
| sign |
string |
签名,请看如何签名
|
其他返回参数说明
|参数名|类型|说明|
|:---|:---|:---|:---|
|code |int |结果状态码|
| |204 |不存在指定订单|
| |400 |请求参数错误|
| |402 |余额不足|
| |404 |不存在指定订单|
| |406 |支付异常|
| |408 |请求已接受但未及时完成,需要重新查询订单状态|
| |409 |订单已创建,需要查询订单状态|
| |其他 |未知状态|
|msg |string |结果消息|
注:
1. 所有错误还将以HTTP协议错误的形式返回
订单状态status的流转说明
graph LR
accepted
accepted --> |支付失败| closed
closed --> |退款请求| refunding(refunding)
accepted --> |发起了撤单| refunding
accepted --> |支付成功| normal
normal --> |退款请求| refunding(refunding)
refunding --> |退款成功| closed
refunding --> |退款失败| normal
refunding --> |部分退款成功| refund
refund --> |退款请求| refunding4(refunding)
refunding4 --> |部分退款成功| refund
refunding4 --> |部分退款失败| refund
推荐的支付流程
H5支付流程
- 需要从前端页面跳转至notify_uri,用户在该页面完成相关业务操作后再回跳到商户指定页面。
sequenceDiagram
用户->>商户系统: 下单
商户系统->>PasS: 调用交易接口C1
note right of PasS: 填写商户单号seq <br/>填写支付金额to <br/>填写回调地址redirect_uri
PasS->>商户系统: 返回结果,取到notify_uri
opt 失败重试
商户系统->>PasS: 检查参数并重新发起交易C1
PasS->>商户系统: 返回结果
end
商户系统->>用户: 跳转至notify_uri
用户->>PasS: 进入支付页面
PasS->>商户系统: 完成支付,进入redirect_uri
商户系统-->>用户: 结果展示页面
用户-->>商户系统: 查询支付结果
loop status!=accepted
商户系统->>PasS: 查询交易单状态R1
PasS->>商户系统: 返回结果
end
商户系统-->>用户: 结果展示页面
opt 如果需要撤单
商户系统->>PasS: 发起撤单D1
PasS->>商户系统: 返回结果
loop status!=refudning
商户系统->>PasS: 查询交易单状态R1
PasS->>商户系统: 返回结果
end
end
如何签名
-
设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串。
特别注意以下重要规则:
- 参数名ASCII码从小到大排序(字典序);
- 如果参数的值为空不参与签名;
- 参数名区分大小写;
- 验证时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
- 接口可能增加字段,验证签名时必须支持增加的扩展字段
-
最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
举例:
假设传送的参数如下:
{
"title": "这是演习",
"seq": "15",
"to": "alipay:1",
"expire": "60s",
"app": "alipay",
"goods": [
{
"name": "hello",
"id": "123",
"quantity": 1,
"price": 1
}
]
}
第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:
app=alipay&expire=60s&goods=[{"name":"hello","id":"123","quantity":1,"price":1}]&seq=15&title=这是演习&to=alipay:1
第二步:拼接API密钥,key为终端密钥(即client_secret):
app=alipay&expire=60s&goods=[{"name":"hello","id":"123","quantity":1,"price":1}]&seq=15&title=这是演习&to=alipay:1&key=3AjtoC3NpW
第三步:计算签名
//注:MD5签名方式
sign = MD5(stringSignTemp)="8b8c383ad3c10bc3e22cdd1aff4e86f2"
最终得到最终发送的数据:
{
"title": "这是演习",
"seq": "15",
"to": "alipay:1",
"expire": "60s",
"app": "alipay",
"goods": [
{
"name": "hello",
"id": "123",
"quantity": 1,
"price": 1
}
],
"sign": "8b8c383ad3c10bc3e22cdd1aff4e86f2"
}
调用示例
支付码反扫成功示例
POST http://dev.sovell.com/sovellpay/v1/trade HTTP/1.1
Authorization: Bearer ee3ddf8ef03fa549028ff18a7d314116
Content-Type: application/json; charset=utf-8
Host: dev.sovell.com
Content-Length: 75
Connection: Keep-Alive
{
"pay_code": "280978609094625751",
"seq": 636207894193718264,
"to": "cmbcpay:1"
}
HTTP/1.1 200 OK
Server: nginx/1.6.3
Date: Mon, 23 Jan 2017 09:31:08 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 12
Connection: keep-alive
{
"id": "203b4954eeb949b183c94f859397de2a",
"status": "normal",
"create_timestamp": 1487908305815,
"title": "这是演习",
"seq": "636207894193718264"
}