13 KiB
对接文档
PayKey:{{.payKey}}
AppSecret:{{.paySecret}}
请求支付页面
请求参数
地址:http(s)://{{.shopHost}}/?sign=xxx 方法:GET
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
|---|---|---|---|---|
| sign | string | 是 | 签名 | 6634ddd39796e4e0a98d6f5e919ae50ddc82edbf255f15aa2e30c5c70381e7b9648 |
| returnUrl | string | 否 | 返回地址 | 订单完成(成功或失败)后的返回地址 |
sign 生成算法
注意:生成签名时,需要提前按照参数的参数名进行排序,按照 a-z 顺序排序
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
|---|---|---|---|---|
| payKey | string | 是 | 支付 key | 20190520123456789 |
| generatedTime | number | 是 | 支付时间 | 默认为 10 位当前时间(单位/秒) |
| duration | number | 是 | 订单存续时间 | 默认为 24 小时 |
| orderNo | string | 是 | 订单号(自定义生成) | 20190520123456789 |
| notifyUrl | string | 是 | 回调地址 | https://baidu.com/notify |
| showMMValue | string | 是 | 卡片面额 | 100 |
| productCode | string | 是 | 卡片编码 | 苹果卡 |
通道编码
| 通道 | 编码 | 说明 |
|---|---|---|
| 苹果充值卡 | 8545 | 苹果充值卡面,由卡号和卡密组成 |
| 沃尔玛充值卡 | 8645 | 沃尔玛充值卡面,有卡号和卡密组成 |
| 天猫店铺充值兑换|确认收货版 | 8745 | 天猫店铺充值,用户秒收货秒回调 |
| 天猫店铺充值兑换|好评版 | 8645 | 天猫店铺充值,用户秒好评秒回调 |
加密秘钥
需要将上述参数加密生成 sign 参数,sign 参数需要拼接在 url
后面,如:http://121.37.253.228:12305/?sign=6634ddd39796e4e0a98d6f5e919ae50ddc82edbf255f15aa2e30c5c70381e7b96489f102aa574e70a4ea1b874685dfafec957b237b02d5980f0a22139d860c7d7a8d5c58e3dfbde606a0de08186a4d45228bcc1917a35768f1903bee921b78b118e8b31d25642737cb3e094c140c81f7&showMM=100&linkID=736797719398。
sign 加密算法为 AES/CBC 加密,数据填充模式为 PKCS7 填充,加密 key 和 iv 如下:
key = thisis32bitlongpassphraseimusing
iv = 1234567890123456
加密算法参考
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"encoding/json"
"fmt"
)
type OrderParams struct {
OrderNo string `json:"orderNo"`
PayKey string `json:"payKey"` // 支付明文秘钥
NotifyUrl string `json:"notifyUrl"` // 回调地址
GeneratedTime int64 `json:"generatedTime"` // 过期时间
Duration int `json:"duration"` // 存续时间(小时)
ProductCode string `json:"productCode"`
ShowMMValue float64 `json:"showMMValue"`
}
func String() string {
r, err := json.Marshal(p)
if err != nil {
return ""
}
return string(r)
}
func Encrypt(p *OrderParams) string {
prepareData := String()
if prepareData == "" {
return prepareData
}
// 加密密码数据
key := "thisis32bitlongpassphraseimusing"
iv := "1234567890123456"
result, _ := AesCBCEncrypt([]byte(prepareData), []byte(key), []byte(iv))
return hex.EncodeToString(result)
}
// AesCBCEncrypt AES/CBC/PKCS7Padding 加密
func AesCBCEncrypt(plaintext []byte, key []byte, iv []byte) ([]byte, error) {
// AES
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// PKCS7 填充
plaintext = paddingPKCS7(plaintext, aes.BlockSize)
// CBC 加密
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(plaintext, plaintext)
return plaintext, nil
}
// PKCS7 填充
func paddingPKCS7(plaintext []byte, blockSize int) []byte {
paddingSize := blockSize - len(plaintext)%blockSize
paddingText := bytes.Repeat([]byte{byte(paddingSize)}, paddingSize)
return append(plaintext, paddingText...)
}
func main() {
orderParams := OrderParams{
OrderNo: "20190520123456789",
PayKey: "20190520123456789",
NotifyUrl: "https://baidu.com/notify",
GeneratedTime: 1558312000,
Duration: 24,
ProductCode: "8645",
ShowMMValue: 100,
}
sign := Encrypt(&orderParams)
fmt.Println(sign)
}
请求实际支付金额(可选)
通过面额获取实际支付金额。注意,实际支付面额可能会随时修改,建议每次拉取订单前重新获取最新面额,切勿缓存。 当前接口会返回当前面额在各个平台设置的实际支付金额,以及设置各个平台的链接,根据需要获取。
接口:http(s)://{{.remoteHost}}/gateway/getAllowedMM
请求方式:GET
| 参数名称 | 类型 | 是否必须 | 描述 | 实例 |
|---|---|---|---|---|
| showMMValue | string | 是 | 展示面额 | 充值卡上的面额 |
| payKey | string | 是 | 支付 key | 20190520123456789 |
| productCode | string | 卡片编码 | 是 | 8456 |
返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
|---|---|---|---|
| code | int | 平台参数 | 0 成功 其他失败 |
| msg | string | 信息提示 | 错误原因 |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 参数类型 | 描述 | 说明 |
|---|---|---|---|
| showLabel | string | 卡面面值 | |
| factLabel | string | 实际面值 | |
| linkID | string | 需要下单的链接 |
请求订单(创建订单)
接口:http(s)://{{.remoteHost}}/gateway/scan
请求方式:POST
请求参数
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
|---|---|---|---|---|
| exValue | string | 是 | 扩展参数 | 卡密数据(具体格式如下) |
| orderNo | string | 是 | 订单号 | 20190520123456789 |
| orderPeriod | int | 否 | 订单有效期(小时) | 24 |
| orderPrice | float | 是 | 订单价格 | 100.12 |
| notifyUrl | string | 是 | 回调地址 | https://baidu.com/notify?sign=xxx |
| payKey | string | 是 | 商户秘钥 | |
| timestamp | int | 是 | 时间戳 | Unix 时间戳,时区为 GMT+8 允许与服务器最大误差 30 秒 |
| sign | string | 是 | 签名 | 签名算法如下 |
| productCode | string | 卡片编码 | 是 | 8456 |
| ip | string | 是 | 客户端IP | 127.0.0.1 |
参数说明
exValue
内需参数
| 参数名称 | 参数类型 | 描述 | 说明 |
|---|---|---|---|
| data | string | 卡密 | --- |
| cardNo | string | 面额 | --- |
| recoveryType | string | 回收类型 | 2.仅有卡密 8.卡密/卡号 |
返回参数
返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
|---|---|---|---|
| code | int | 平台参数 | 0 成功 其他失败 |
| msg | string | 信息提示 | |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 类型 | 是否必须 | 描述 | 示例 |
|---|---|---|---|---|
| statusCode | string | 是 | 返回状态码 | 00 成功,其他失败 |
| orderNo | string | 是 | 订单号 | 20190520123456789 |
| orderPrice | string | 是 | 订单价格 | 100.12 |
| sign | string | 是 | 签名 | xxx |
| msg | string | 是 | 附带返回信息 | 成功 |
查询订单
地址:http(s)://{{.remoteHost}}/gateway/merchant/query 方法:Get
查询参数
| 参数名称 | 参数类型 | 是否必须 | 描述 | 说明 |
|---|---|---|---|---|
| orderNo | string | 是 | 订单号 | |
| appKey | string | 是 | 应用 key | |
| timestamp | int | 是 | 时间戳 | Unix 时间戳,时区为 GMT+8 允许与服务器最大误差 30 秒 |
| sign | string | 是 | 签名 | xxx |
返回参数
| 参数名称 | 参数类型 | 描述 | 说明 |
|---|---|---|---|
| code | int | 平台参数 | 0 成功 其他失败 |
| msg | string | 信息提示 | |
| data | interface | 具体返回数据 | 返回数据如下 |
具体成功返回数据
| 参数名称 | 参数类型 | 描述 | 说明 |
|---|---|---|---|
| orderNo | string | 订单号 | |
| cardNo | string | 卡号 | |
| cardPwd | string | 卡密 | |
| status | string | 订单状态 | success,fail, wait |
| faceVal | float | 面额 | |
| amount | string | 实际面额 |
签名算法(sign)
注意:
1 对参数进行签名时,一定不要写死!!!,请按签名算法进行计算!!
2 所有提交参数都需要参与签名计算 为空的也需要
参数签名算法示例算法
请注意: 1.对参数进行签名时,一定不要写死!!!,请按签名算法进行计算!! 2.所有提交参数都需要参与签名计算 为空的也需要 3.下面的参数只是示例,具体参数要根据实际接口参数进行验签
Golang 算法
package main
import (
"crypto/md5"
"encoding/hex"
"sort"
"strconv"
"time"
)
func main() {
//实际获取的appKey值
appKey := "1f2811dcd32b2c5c"
//实际获取的appSecret值
appSecret := "3C569A210D1260B2"
params := map[string]string{}
params["appKey"] = appKey
params["phone"] = "13800138000"
params["timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
params["amount"] = "100"
strArr := SortMap(params)
signStr := ""
for i := 0; i < len(strArr); i++ {
k := strArr[i]
if len(params[k]) == 0 {
signStr += k
} else {
signStr += k + params[k]
}
}
signStr += appSecret
h := md5.New()
h.Write([]byte(signStr))
hex.EncodeToString(h.Sum(nil))
}
// SortMap 对map的key值进行排序
func SortMap(m map[string]string) []string {
var arr []string
for k := range m {
arr = append(arr, k)
}
sort.Strings(arr)
return arr
}
Java 算法
String appSecret = "3C569A210D1260B2";
TreeMap<String, String> params = new TreeMap<String,String>();
params.put("timestamp", ""+(System.currentTimeMillis()/1000L));
params.put("phone", "13800138000");
params.put("amount", "100");
StringBuilder sb = new StringBuilder();
for(Entry<String, String> item : params.entrySet()){
if(item.getKey().equals("sign"))
continue;
sb.append(item.getKey()).append(item.getValue());
}
sb.append(appSecret);
params.put("sign",md5(sb.toString()));
订单回调接口签名检验
回调接口示例
地址:http://baidu.com/notify?param1=xx¶m2=xx
方法:Get
返回参数:
| 参数名称 | 参数类型 | 含义 | 示例 |
|---|---|---|---|
| orderNo | string | 订单号(己方账号) | 20190520123456789 |
| orderPrice | string | 订单价格(面值,一般为整数) | 100 |
| factPrice | string | 实际价格 | 98.89 |
| orderTime | string | 回调时间 | 2006010215:04:05 |
| trxNo | string | 交易(系统账户)流水号 | 20190520123456789 |
| payKey | string | 支付通道 key | 20190520123456789 |
| sign | string | 签名 | xxx |
| tradeStatus | string | 交易状态 | success |
| statusCode | string | 订单状态 | success |
签名算法
签名算法参见请求订单(创建订单)接口 ↑
