Files
kami_frontend/src/assets/markdown/api.md
danial b0985850af refactor(api): 修改交易状态字段并添加失败原因字段
- 将 tradeStatus 字段改为 statusCode,类型从 string 改为 int
- 添加 failReason 字段,用于描述订单失败的具体原因
2025-02-12 12:10:43 +08:00

17 KiB
Raw Blame History

对接文档

AppKey{{.payKey}}
AppSecret{{.paySecret}}

1.请求支付页面

1.DEMO文件链接Python demo文件

请求卡密支付页面,页面如下:
img.png

请求参数

地址:{{.shopHost}}/?sign=xxx
方法GET

参数名称 类型 是否必须 描述 示例
sign string 签名 6634ddd39796e4e0a98d6f5e919ae50ddc82edbf255f15aa2e30c5c70381e7b9648
returnUrl string 返回地址 订单完成(成功或失败)后的返回地址 https://baidu.com/notify

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(p *OrderParams) string {
	r, err := json.Marshal(p)
	if err != nil {
		return ""
	}
	logs.Info(string(r))
	return string(r)
}

func Encrypt(p *OrderParams) string {
	prepareData := String(p)
	print(prepareData, "\n")
	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)
}

// 原始文本:{"orderNo":"20190520123456789","payKey":"20190520123456789","notifyUrl":"https://baidu.com/notify","generatedTime":1558312000,"duration":24,"productCode":"8645","showMMValue":100}
// aes加密后的文本94db297041a61fa78ac2176b9e8e1e9b34d2ae33b6d46fc8c731a34b4c2b76eea7738993c2c175e6d61648c31c00b5d6d465246aa39b5fba5d6b77599f112c791370486b9fb19ad675e228b1d36ec6ad890ac692a262a73f126dda2bffae493452ea20d8604874af1ff2ba67cf6aab5c5821d100beec4d4c4de86d4c014fc8fe0bfe61b259752007f498dbee92f0136b2cbb756ff5405f95fc7763405022c835812f58dda86c9d72f84c6d576d2e98909d0e499e3c4b4da552f478bfbce3ba63
// 签名key=thisis32bitlongpassphraseimusing iv=1234567890123456
// 算法CBC非对称加密/PKCS7填充

2.请求实际支付金额(可选)

通过面额获取实际支付金额。注意,实际支付面额可能会随时修改,建议每次拉取订单前重新获取最新面额,切勿缓存。
当前接口会返回当前面额在各个平台设置的实际支付金额,以及设置各个平台的链接,根据需要获取。

接口:{{.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 需要下单的链接

3.提交订单(创建订单)

提交订单接口,此接口用于第三方直接提交卡号/卡密,使用自有拉单页面

接口:{{.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
deviceId string 设备唯一标识 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 附带返回信息 成功

4.查询订单

地址:{{.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 订单状态 successfail, wait
faceVal float 面额
amount string 实际面额
CardReturnData string 充值失败原因(明细)

4.创建订单(新)

创建订单,并返回下单单号,地址等信息

地址:{{.remoteHost}}/gateway/createOrder

请求参数

参数名称 类型 是否必须 描述 示例
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

返回参数

参数名称 参数类型 描述 说明
code int 平台参数 0 成功 -1失败
msg string 信息提示 失败的具体原因
data interface 具体返回数据 返回数据如下

具体成功返回数据

参数名称 参数类型 描述 说明
productCode string 产品编码
paymentName string 产品名称
transactionType string 卡密 是兑换还是充值 暂时无用
payUrl string 支付链接 最终构建的支付链接
merchantOrderNo string 商户订单号 来源自请求传入订单号
orderNo string 订单编号 系统编号

签名算法sign

注意:
1 对参数进行签名时,一定不要写死!!!,请按签名算法进行计算!!
2 所有提交参数都需要参与签名计算 为空的也需要

参数签名算法示例算法

请注意: 1.对参数进行签名时,一定不要写死!!!,请按签名算法进行计算!! 2.所有提交参数都需要参与签名计算 为空的也需要3.下面的参数只是示例,具体参数要根据实际接口参数进行验签

package main

import (
	"crypto/md5"
	"encoding/hex"
	"sort"
	"strconv"
	"time"
)

func main() {
	//实际获取的appKey值
	appKey := "1f2811dcd32b2c5c"
	//实际获取的appSecret值
	appSecret := "3C569A210D1260B2"

	// 如果有其它参数,依次添加 key=value
	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
}


// 原始值
// {
//     "amount": "100",
//     "appKey": "1f2811dcd32b2c5c",
//     "phone": "13800138000",
//     "timestamp": "1737284505"
// }

// appSecret3C569A210D1260B2

// 加密前的值
// amount100appKey1f2811dcd32b2c5cphone13800138000timestamp17372845053C569A210D1260B2
// 加密后的值96ff597f7b89322e83e234e3ad1d42e5

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&param2=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
statusCode int 交易状态 1 成功0 通用失败 其他状态码待添加
failReason string 订单状态 失败的具体原因

签名算法

签名算法参见提交订单(创建订单)接口 ↑