快钱官方没有提供完整的SDK,仅有一些demo,要集成进现有go mod项目要花一点时间集成和测试,记录一下签名验证的关键点,支付方式是SDK网关支付和H5支付。
快钱会提供三个密钥文件:
- 商户应用私钥文件,
pfx
格式,有密码,用于签名数据。 - ssl证书,
pfx
格式,有密码,用于https
双向认证,快钱的证书测试下来会报错,所以用openssl
提取了证书和密钥使用,下面有写。 - 快钱公钥证书,用于加/解密和验证数据。
场景是把以上证书内容base64
编码成字符串存数据库,提供页面UI配置。
1、支付下单数据签名
按照文档里面的字段顺序排序字段,拼接成查询字符串,用商户私钥加签。
//SignData data 拼接的待签名字符串
func SignData(data string) (string, error) {
// merchantKey私钥
certBytes, err := base64.StdEncoding.DecodeString(merchantKey)
if err != nil {
return "", err
}
// merchantKeyPwd 私钥密码
// "golang.org/x/crypto/pkcs12"
pkey, _, err := pkcs12.Decode(certBytes, merchantKeyPwd)
if err != nil {
return "", err
}
privateKey, ok := pkey.(*rsa.PrivateKey)
if !ok {
return "", errors.New("parse private key fail")
}
hashed := sha256.Sum256([]byte(data))
// "crypto/rsa"
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
if err != nil {
return "", err
}
// 返回签名结果
return base64.StdEncoding.EncodeToString(signature), nil
}
2、http请求发送
SDK网关支付的下单,订单查询(支付和退款查询接口相同)和退款接口把数据编码成json普通post请求即可。
H5支付的有点特殊:
1. 组装数据
post json body数据分为两部分:
- head:接口和商户信息
- requestBody:接口业务数据,需要签名和加密
首先用户商户私钥调用p7crypto.GetSignedData
生成签名signedData
,再用快钱公钥调用p7crypto.GetEnvelopedData
加密数据envelopedData
。
上面两个函数在官方给的h5 demo里面有,p7crypto
目录(pkcs7加解密/签名),还有依赖kernel
目录,复制进自己的项目,函数里面写死的密钥文件改成传参即可。
最后组成requestBody:
requestBody = map[string]interface{}{
"signedData": signedData,
"envelopedData": envelopedData,
}
2. 发送请求
然后是Post josn数据的双向认证。
官方提供的pfx证书有点问题,测试下来会报错,所以用openssl
从里面分别提取了crt
证书和key
内容使用:
openssl pkcs12 -in ./server.pfx -clcerts -nokeys -out ./server.crt
openssl pkcs12 -in ./server.pfx -nocerts -nodes -out ./server.key
如果上面命令报错类似下面这样,则加上-legacy
参数:
Error outputting keys and certificates
4077214A00710000:error:0308010C:digital envelope routines..............
post关键代码:
//SSLCert: server.crt的base64
certPem, err := base64.StdEncoding.DecodeString(SSLCert)
if err != nil {
return "", err
}
//SSLKey: server.key的base64
keyPem, err := base64.StdEncoding.DecodeString(SSLKey)
if err != nil {
return "", err
}
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return "", err
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//Certificates和RootCAs二选一即可
Certificates: []tls.Certificate{cert},
//RootCAs: caCertPool,
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
},
},
}
contentType := "application/json; charset=utf-8"
rawData := strings.NewReader(requestData)
resp, err := client.Post(reqUrl, contentType, rawData)
if err != nil {
return "", err
}
3. 解析响应
- 解密:
p7crypto.GetDecryptData
,用商户私钥 - 验证签名:
p7crypto.GetVerifyResult
,上一步解密得到的数据,用快钱公钥验证
3、 支付成功异步通知验签
提取快钱证书里面的公钥,用rsa.VerifyPKCS1v15
验证签名。
H5的回调待签名字符串排序是按照文档给出的字段顺序。
SDK网关支付的待签名字段排序是字典序从小到大。
关键代码:
// KQCert 快钱公钥
certBytes, err := base64.StdEncoding.DecodeString(KQCert)
if err != nil {
return err
}
// "encoding/pem"
block, _ := pem.Decode(certBytes)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return err
}
log.Info("signStr = ", signStr)
pubkey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return errors.New("parse public key fail")
}
// signStr 待签名字符串,按文档字段顺序把异步通知数据组合成query格式的字符串
hashed := sha256.Sum256([]byte(signStr))
// signByte 异步通知数据的签名,base64 decode得到
return rsa.VerifyPKCS1v15(pubkey, crypto.SHA256, hashed[:], signByte)
4、小程序调起支付
快钱小程序支付是通过小程序方法navigateToMiniProgram
来调起的。
三个关键参数:
appId
:下单的接口返回数据的路径mpayInfo.appletInfo.kqWechatAppletAppId
envVersion
:按需填,小程序文档里面列出的值path
: 格式化值得代码如下
// appInfo和jsonObj都是用gjson库解析下单返回数据后的对象
fmt.Sprintf(
"%s?order=%s&appletInfo=%s&envVersion=%s",
appInfo.Get("kqWechatAppletAppUrl").String(), // mpayInfo.appletInfo.kqWechatAppletAppUrl
jsonObj.Get("mpayInfo.orderRequestInfo.orderRequestKey").String(),
jsonObj.Get("mpayInfo.appletInfo").String(),
"release", //可选值和envVersion一样
)
本文链接:https://360us.net/article/100.html