|
|
|
[[_TOC_]]
|
|
|
|
|
|
|
|
写在前面
|
|
|
|
===
|
|
|
|
|
|
|
|
- 文中将${seq}定义为参数名为seq的参数,相关的表中有参数描述
|
|
|
|
- 文中将${timeout*}定义为可选的参数timeout
|
|
|
|
- 本文当中所有涉及到时间的参数都使用精确到毫秒的时间戳,例1535595007983表示Thu Aug 30 2018 10:10:07 GMT+0800 (中国标准时间)
|
|
|
|
- 本文中涉及到分页的接口使用skip、limit参数,表示跳过skip行取至多limit行,返回头X-Total-Count参数表示查询总行数
|
|
|
|
- 所有接口使用HTTP状态码200表示成功,其他状态码表示失败,如有特例将在具体接口中说明
|
|
|
|
|
|
|
|
|
|
|
|
接口描述
|
|
|
|
===
|
|
|
|
|
|
|
|
### R1.查询交易单
|
|
|
|
|
|
|
|
> - 本接口调用频率同一订单不超过1次/秒
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
GET http://${domain}/sovellpay/v2/trade/${seq}
|
|
|
|
?timeout=${timeout*}
|
|
|
|
Authorization: Bearer ${token}
|
|
|
|
```
|
|
|
|
|
|
|
|
- 返回`PAYMENT`结构
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
{
|
|
|
|
"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|客户端订单号|
|
|
|
|
|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|明细标签组合,这是一个集合,取值不仅限于所列出值|
|
|
|
|
||wx|微信|
|
|
|
|
||ali|支付宝|
|
|
|
|
||jd|京东|
|
|
|
|
||qq|QQ|
|
|
|
|
||sh|大众闪惠|
|
|
|
|
||wing|翼支付|
|
|
|
|
||scan|刷卡支付,B扫C,商户扫用户|
|
|
|
|
||qr|扫码支付,C扫B,用户扫商户|
|
|
|
|
||h5|公众号支付|
|
|
|
|
||app|APP支付|
|
|
|
|
||card|卡支付|
|
|
|
|
||wallet|储值消费|
|
|
|
|
||helpbuy|代付优惠|
|
|
|
|
||credit|透支消费|
|
|
|
|
||subsidy|福利消费|
|
|
|
|
||coupon|券|
|
|
|
|
||settle|结算,一般不计入总金额|
|
|
|
|
| └ card|string|如果是卡支付的话则输出为卡号,非卡支付不输出|
|
|
|
|
| └ amount|int|实付金额|
|
|
|
|
| └ balance|int|交易后余额,wallet时给出该值|
|
|
|
|
|
|
|
|
### R2.事件总线
|
|
|
|
|
|
|
|
> - 查询token所授权终端的订单状态变更事件
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
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.查询交易单`
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
[{
|
|
|
|
"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.查询可用账户
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
GET http://${domain}/sovellpay/v2/accounts
|
|
|
|
Authorization: Bearer ${token}
|
|
|
|
```
|
|
|
|
|
|
|
|
- 返回
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
[{
|
|
|
|
"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`
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
POST http://${domain}/sovellpay/v2/trade?sign=${sign}
|
|
|
|
Authorization: Bearer ${token}
|
|
|
|
|
|
|
|
{
|
|
|
|
"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}...]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
|参数名|类型|说明|
|
|
|
|
|:---|:---|:---|
|
|
|
|
|title|string(128)|订单描述|
|
|
|
|
|intent|string|交易意图,参数说明见下文|
|
|
|
|
|original_id|string|原单据ID,退款时该参数必须提供|
|
|
|
|
|pay_code|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|夜宵|
|
|
|
|
|其他||见`公共请求参数说明`|
|
|
|
|
|
|
|
|
- 关于intent字符串的说明
|
|
|
|
|
|
|
|
|格式|说明|
|
|
|
|
|:---|:---|
|
|
|
|
|pay ${amount}|支付指定金额(分)|
|
|
|
|
|charge ${amount}|充值指定金额(分)(例如 charge 1)|
|
|
|
|
|refund ${amount}|退款指定金额(分)(例如 refund 1)|
|
|
|
|
|refund *|全额退款|
|
|
|
|
|
|
|
|
> 注:
|
|
|
|
1. 使用半角冒号分隔
|
|
|
|
3. 退款接口会根据外部请求号seq幂等返回,因此同一笔需要多次部分退款时,必须使用不同的seq。
|
|
|
|
|
|
|
|
- 返回同 `R1.查询交易单`
|
|
|
|
|
|
|
|
### D1.全额撤单
|
|
|
|
|
|
|
|
> - 必须由发起交易所在终端发起撤单
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
DELETE http://${domain}/sovellpay/v2/trade/${seq}?sign=${sign}
|
|
|
|
Authorization: Bearer ${token}
|
|
|
|
```
|
|
|
|
- 返回同 `R1.查询交易单`
|
|
|
|
|
|
|
|
支持的支付通道定义
|
|
|
|
===
|
|
|
|
|
|
|
|
|定义名|说明|
|
|
|
|
|:---|:---|
|
|
|
|
|wechat|微信|
|
|
|
|
|alipay|支付宝|
|
|
|
|
|xqpos|享钱|
|
|
|
|
|idish|智盘本地卡|
|
|
|
|
|icbc|工商银行|
|
|
|
|
|jsb|江苏银行|
|
|
|
|
|third|本地第三方系统|
|
|
|
|
|
|
|
|
公共请求参数说明
|
|
|
|
===
|
|
|
|
|
|
|
|
|参数名|类型|说明|
|
|
|
|
|:---|:---|:---|
|
|
|
|
|domain|string|接口域名|
|
|
|
|
|token|string|访问令牌|
|
|
|
|
|timeout|string|阻塞查询时用,传0则立即返回结果,否则在timeout时间内若交易单状态发生变化时返回结果,格式为xxxs,默认10s|
|
|
|
|
|seq|string|客户端订单号,商户订单号,下位订单号|
|
|
|
|
|sign|string|签名,请看`如何签名`|
|
|
|
|
|
|
|
|
公共返回参数说明
|
|
|
|
===
|
|
|
|
|
|
|
|
### 错误返回
|
|
|
|
|
|
|
|
|参数名|类型|说明|
|
|
|
|
|:---|:---|:---|:---|
|
|
|
|
|code |int |结果状态码|
|
|
|
|
| |204 |不存在指定订单|
|
|
|
|
| |400 |请求参数错误|
|
|
|
|
| |402 |余额不足|
|
|
|
|
| |404 |不存在指定订单|
|
|
|
|
| |406 |支付异常|
|
|
|
|
| |408 |请求已接受但未及时完成,需要重新查询订单状态|
|
|
|
|
| |409 |订单已创建,需要查询订单状态|
|
|
|
|
| |其他 |未知状态|
|
|
|
|
|msg |string |结果消息|
|
|
|
|
|
|
|
|
```
|
|
|
|
注:
|
|
|
|
1. 所有错误还将以HTTP协议错误的形式返回
|
|
|
|
```
|
|
|
|
|
|
|
|
### 订单状态status的流转说明
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
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*,用户在该页面完成相关业务操作后再回跳到商户指定页面。
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
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
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
如何签名
|
|
|
|
===
|
|
|
|
|
|
|
|
1. 设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串。
|
|
|
|
特别注意以下重要规则:
|
|
|
|
|
|
|
|
1. 参数名ASCII码从小到大排序(字典序);
|
|
|
|
2. 如果参数的值为空不参与签名;
|
|
|
|
3. 参数名区分大小写;
|
|
|
|
4. 验证时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
|
|
|
|
5. 接口可能增加字段,验证签名时必须支持增加的扩展字段
|
|
|
|
|
|
|
|
2. 最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
|
|
|
|
|
|
|
|
|
|
|
|
举例:
|
|
|
|
假设传送的参数如下:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
{
|
|
|
|
"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
|
|
|
|
```
|
|
|
|
|
|
|
|
第三步:计算签名
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
//注:MD5签名方式
|
|
|
|
sign = MD5(stringSignTemp)="8b8c383ad3c10bc3e22cdd1aff4e86f2"
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
最终得到最终发送的数据:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
|
|
{
|
|
|
|
"title": "这是演习",
|
|
|
|
"seq": "15",
|
|
|
|
"to": "alipay:1",
|
|
|
|
"expire": "60s",
|
|
|
|
"app": "alipay",
|
|
|
|
"goods": [
|
|
|
|
{
|
|
|
|
"name": "hello",
|
|
|
|
"id": "123",
|
|
|
|
"quantity": 1,
|
|
|
|
"price": 1
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"sign": "8b8c383ad3c10bc3e22cdd1aff4e86f2"
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
调用示例
|
|
|
|
===
|
|
|
|
|
|
|
|
### 支付码反扫成功示例
|
|
|
|
```javascript
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
```javascript
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
|
|
|
``` |
|
|
|
\ No newline at end of file |