一、准备阶段
pch5 接入步骤
官方文档 https://payapi.jd.com/docList...
查看主要接入步骤
密钥生成
• 需要设置desc key
• md5 key 和 app id app对接会使用
• 证书文件名称
my_rsa_private_pkcs8_key.pem
wy_rsa_public_key.pem
示例程序使用私钥格式为 pkcs8 格式
官方的SDK中的数据可以在示例程序中使用
下载SDK地址 https://payapi.jd.com/docList...
找到接口文档中的Demo
还会用到的包
import (
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"time"
)
加密、解密、验证签名
package main
import (
"bytes"
"crypto"
"crypto/des"
cryptoRand "crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"math/rand"
"regexp"
"sort"
"strings"
"time"
)
func randNumber() string {
return fmt.Sprintf("%05v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(100000))
}
func checkSign(decryptBytes []byte, sign, publicKey string) bool {
decrypt := string(decryptBytes)
clipStartIndex := strings.Index(decrypt, "sign>")
clipEndIndex := strings.Index(decrypt, "/sign>")
xmlStart := decrypt[0:clipStartIndex]
xmlEnd := decrypt[clipEndIndex+7 : len(decrypt)]
originXml := xmlStart + xmlEnd
//签名校验
if sign == "" {
return false
}
return checkRsaSign(originXml, publicKey, sign)
}
func replaceXmlStrBlankChar(str string) string {
str = strings.Replace(str, "\r", "", -1)
str = strings.Replace(str, "\n", "", -1)
str = strings.Replace(str, "\t", "", -1)
reg, _ := regexp.Compile(">\\s+")
str = reg.ReplaceAllString(str, ">")
reg, _ = regexp.Compile("\\s+\\/>")
str = reg.ReplaceAllString(str, "/>")
return str
}
func getPaySign(paramMap map[string]string, privateKey string) (string, error) {
payString := getSortString(paramMap)
return getRsaSign(payString, privateKey)
}
// ------
// 加密
func encrypt3DES(paramMap map[string]string, desKey string) (map[string]string, error) {
desKey = base64DecodeStr(desKey)
for k, v := range paramMap {
if k == "sign" || k == "merchant" || k == "version" {
continue
}
encrypt, err := tripleEcbDesEncrypt([]byte(v), []byte(desKey))
if err != nil {
return paramMap, err
}
paramMap[k] = decimalByteSlice2HexString(encrypt)
}
return paramMap, nil
}
func decryptArg(notifyQuery NotifyQuery, desKey string) (decryptBytes []byte, err error) {
desKeyBytes, err := base64.StdEncoding.DecodeString(desKey)
if err != nil {
return nil, err
}
encryptBytes, err := base64.StdEncoding.DecodeString(notifyQuery.Encrypt)
if err != nil {
return nil, err
}
encryptBytes, err = hexString2Bytes(string(encryptBytes))
if err != nil {
return nil, err
}
decryptBytes, err = tripleEcbDesDecrypt(encryptBytes, desKeyBytes)
if err != nil {
return nil, err
}
return decryptBytes, nil
}
// JDAPP填充规则
func jdPadding(origData []byte) []byte {
merchantData := len(origData)
x := (merchantData + 4) % 8
y := 0
if x == 0 {
y = 0
} else {
y = 8 - x
}
sizeByte := integerToBytes(merchantData)
var resultByte []byte
//填充byte数据长度
for i := 0; i 4; i++ {
resultByte = append(resultByte, sizeByte[i])
}
//填充原数据长度
for j := 0; j merchantData; j++ {
resultByte = append(resultByte, origData[j])
}
//填充0
for k := 0; k y; k++ {
resultByte = append(resultByte, 0x00)
}
return resultByte
}
func jdUnPadding(unPaddingResult []byte) []byte {
var Result []byte
var dataSizeByte []byte
for i := 0; i 4; i++ {
dataSizeByte = append(dataSizeByte, unPaddingResult[i])
}
decimalDataSize := byteArrayToInt(dataSizeByte)
for j := 0; j decimalDataSize; j++ {
Result = append(Result, unPaddingResult[4+j])
}
return Result
}
// 字节数组表示的实际长度
func byteArrayToInt(dataSizeByte []byte) int {
value := 0
for i := 0; i 4; i++ {
shift := byte((4 - 1 - i) * 8)
value = value + int(dataSizeByte[i]0x000000FF)shift
}
return value
}
func integerToBytes(val int) [4]byte {
byt := [4]byte{}
byt[0] = byte(val >> 24 0xff)
byt[1] = byte(val >> 16 0xff)
byt[2] = byte(val >> 8 0xff)
byt[3] = byte(val 0xff)
return byt
}
// byte转16进制字符串
func decimalByteSlice2HexString(DecimalSlice []byte) string {
var sa = make([]string, 0)
for _, v := range DecimalSlice {
sa = append(sa, fmt.Sprintf("%02X", v))
}
ss := strings.Join(sa, "")
return ss
}
// 十六进制字符串转byte
func hexString2Bytes(str string) ([]byte, error) {
Bys, err := hex.DecodeString(str)
if err != nil {
return nil, err
}
return Bys, nil
}
// base解码
func base64DecodeStr(src string) string {
a, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return ""
}
return string(a)
}
// Des解密
func decrypt(crypted, key []byte) ([]byte, error) {
if len(crypted) 1 || len(key) 1 {
return nil, errors.New("wrong data or key")
}
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
out := make([]byte, len(crypted))
dst := out
bs := block.BlockSize()
if len(crypted)%bs != 0 {
return nil, errors.New("wrong crypted size")
}
for len(crypted) > 0 {
block.Decrypt(dst, crypted[:bs])
crypted = crypted[bs:]
dst = dst[bs:]
}
return out, nil
}
// [golang ECB 3DES Decrypt]
func tripleEcbDesDecrypt(crypted, key []byte) ([]byte, error) {
tkey := make([]byte, 24, 24)
copy(tkey, key)
k1 := tkey[:8]
k2 := tkey[8:16]
k3 := tkey[16:]
buf1, err := decrypt(crypted, k3)
if err != nil {
return nil, err
}
buf2, err := encrypt(buf1, k2)
if err != nil {
return nil, err
}
out, err := decrypt(buf2, k1)
if err != nil {
return nil, err
}
out = jdUnPadding(out)
return out, nil
}
// sha256加密
func hasha256(str string) string {
h := sha256.New()
h.Write([]byte(str))
cipherStr := h.Sum(nil)
//return cipherStr
return hex.EncodeToString(cipherStr)
}
// base解编码
func base64EncodeStr(src string) string {
return base64.StdEncoding.EncodeToString([]byte(src))
}
// 对消息的散列值进行数字签名
func signPKCS1v15(msg, privateKey []byte, hashType crypto.Hash) ([]byte, error) {
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key format error")
}
pri, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, errors.New("parse private key error")
}
key, ok := pri.(*rsa.PrivateKey)
if ok == false {
return nil, errors.New("private key format error")
}
sign, err := rsa.SignPKCS1v15(cryptoRand.Reader, key, hashType, msg)
if err != nil {
return nil, errors.New("sign error")
}
return sign, nil
}
// Des加密
func encrypt(origData, key []byte) ([]byte, error) {
if len(origData) 1 || len(key) 1 {
return nil, errors.New("wrong data or key")
}
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
bs := block.BlockSize()
if len(origData)%bs != 0 {
return nil, errors.New("wrong padding")
}
out := make([]byte, len(origData))
dst := out
for len(origData) > 0 {
block.Encrypt(dst, origData[:bs])
origData = origData[bs:]
dst = dst[bs:]
}
return out, nil
}
// [golang ECB 3DES Encrypt]
func tripleEcbDesEncrypt(origData, key []byte) ([]byte, error) {
tkey := make([]byte, 24, 24)
copy(tkey, key)
k1 := tkey[:8]
k2 := tkey[8:16]
k3 := tkey[16:]
origData = jdPadding(origData) // PKCS5Padding(origData, bs)
buf1, err := encrypt(origData, k1)
if err != nil {
return nil, err
}
buf2, err := decrypt(buf1, k2)
if err != nil {
return nil, err
}
out, err := encrypt(buf2, k3)
if err != nil {
return nil, err
}
return out, nil
}
// ------------
// 验证签名
func verifyPKCS1v15(msg, sign, publicKey []byte, hashType crypto.Hash) bool {
block, _ := pem.Decode(publicKey)
if block == nil {
return false
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
err = rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), hashType, msg, sign)
return err == nil
}
func getRsaSign(paramStr string, privateKey string) (string, error) {
sha256Str := hasha256(paramStr)
sign, err := signPKCS1v15([]byte(sha256Str), []byte(privateKey), crypto.Hash(0))
if err != nil {
return "", err
}
base64String := base64.StdEncoding.EncodeToString(sign)
return base64String, nil
}
func checkRsaSign(paramStr string, publicKey, sign string) bool {
signByte, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
return false
}
sha256Str := hasha256(paramStr)
return verifyPKCS1v15([]byte(sha256Str), signByte, []byte(publicKey), crypto.Hash(0))
}
// -------
// 字符串拼接
// 支付字符串拼接
func getSortString(m map[string]string) string {
var buf bytes.Buffer
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
vs := m[k]
if buf.Len() > 0 {
buf.WriteByte('')
}
buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(vs)
}
return buf.String()
}
程序中加载密钥
func getKey(keyType string) (string, error) {
keyMap := map[string]string{
"private_key": "./private.pem",
"public_key": "./public.pem",
}
path, ok := keyMap[keyType]
if !ok {
return "", errors.New("key path not exists")
}
fileHandler, err := os.Open(path)
if err != nil {
return "", err
}
defer fileHandler.Close()
keyBytes, err := ioutil.ReadAll(fileHandler)
if err != nil {
return "", err
}
return string(keyBytes), nil
}
二、发起支付
常量
常量
const version = "V2.0" // 京东支付版本
const merchantId = "" // 商户id
const desKey = "" // desc key
const timeLayout = "20060102150405"
const cny = "CNY"
const practicalityGoodsType = "GT01" //商品类型-实物
const businessServiceConsumeCode = "100001"
const practicality = "0" //商品类型-实物
调用在线支付接口
pc h5 发起支付
官方文档 https://payapi.jd.com/docList...
type Order struct {
OrderId string `json:"order_id"`
Amount float64 `json:"amount"`
Items []*OrderItem `json:"items"`
ShippingAddress *OrderShippingAddress `json:"shipping_address"`
}
type OrderItem struct {
GoodsNo string `json:"goods_no"`
GoodsName string `json:"goods_name"`
GoodsPrice float64 `json:"goods_price"`
GoodsNum uint32 `json:"goods_num"`
}
type OrderShippingAddress struct {
Name string `json:"name"`
Mobile string `json:"mobile"`
Address string `json:"address"`
Province string `json:"province"`
City string `json:"city"`
Country string `json:"country"`
}
type ReqWithEncrypt struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` //版本
Merchant string `xml:"merchant" json:"merchant"` //商户号
Encrypt string `xml:"encrypt" json:"encrypt"` //加密数据
}
const version = "V2.0" // 京东支付版本
const merchantId = "22294531"
const desKey = "ta4E/aspLA3lgFGKmNDNRYU92RkZ4w2t"
const timeLayout = "20060102150405"
const cny = "CNY"
const practicalityGoodsType = "GT01" //商品类型-实物
const businessServiceConsumeCode = "100001"
const practicality = "0" //商品类型-实物
type GoodsInfo struct {
Id string `json:"id"` //商品编号
Name string `json:"name"` //商品名称
Price int64 `json:"price"` //商品单价,单位分
Num uint32 `json:"num"` //商品数量
Type string `json:"type"` //商品类型
}
type ReceiverInfo struct {
Name string `json:"name"`
Address string `json:"address"`
Mobile string `json:"mobile"`
Province string `json:"province"`
City string `json:"city"`
Country string `json:"country"`
}
type KjInfo struct {
GoodsSubmittedCustoms string `json:"goodsSubmittedCustoms"` // 是否报关Y/N
GoodsUnderBonded string `json:"goodsUnderBonded"` // 是否保税货物项下付款Y/N
}
const jdPayUrl = "https://wepay.jd.com/jdpay/saveOrder"
// pc h5 form 表单提交支付
func postFormPay(arg Order) (payStr string, err error) {
// 支付参数
paramMap := make(map[string]string)
amountStr := fmt.Sprintf("%.f", arg.Amount*100)
totalFee, err := strconv.ParseInt(amountStr, 10, 64)
if err != nil {
return payStr, err
}
var goodsInfos []GoodsInfo
for _, v := range arg.Items {
priceStr := fmt.Sprintf("%.f", v.GoodsPrice*100)
price, err := strconv.ParseInt(priceStr, 10, 64)
if err != nil {
return payStr, err
}
goodsInfos = append(goodsInfos, GoodsInfo{
Id: v.GoodsNo,
Name: v.GoodsName,
Price: price,
Num: v.GoodsNum,
Type: practicalityGoodsType, // 商品类型-实物
})
}
goodsInfoBytes, _ := json.Marshal(goodsInfos)
kjInfo := KjInfo{GoodsSubmittedCustoms: "N", GoodsUnderBonded: "N"}
kjInfoBytes, _ := json.Marshal(kjInfo)
detailAddress := arg.ShippingAddress.Province + arg.ShippingAddress.City + arg.ShippingAddress.Country + arg.ShippingAddress.Address
receiverInfo := ReceiverInfo{
Name: arg.ShippingAddress.Name, // 收货人姓名
Mobile: arg.ShippingAddress.Mobile, // 收货人手机号
Address: detailAddress, // 地址要求包过省市区
Province: arg.ShippingAddress.Province, // 省
City: arg.ShippingAddress.City, // 市
Country: arg.ShippingAddress.Country, // 区
}
receiverInfoBytes, _ := json.Marshal(receiverInfo)
orderId := fmt.Sprintf("test%s%s", time.Now().Format("20060102150405"), randNumber())
paramMap["version"] = version
paramMap["merchant"] = merchantId
paramMap["tradeNum"] = orderId // 订单号
paramMap["tradeName"] = orderId // 订单描述
paramMap["tradeDesc"] = orderId // 订单描述
paramMap["payMerchant"] = "test" // 商户名称
paramMap["tradeTime"] = time.Now().Format(timeLayout)
paramMap["amount"] = fmt.Sprintf("%v", totalFee)
paramMap["orderType"] = practicality
paramMap["currency"] = cny
paramMap["userId"] = "100"
paramMap["expireTime"] = "3600" //订单失效时长,单位秒
paramMap["goodsInfo"] = string(goodsInfoBytes)
paramMap["settleCurrency"] = cny
paramMap["kjInfo"] = string(kjInfoBytes)
paramMap["bizTp"] = businessServiceConsumeCode
paramMap["notifyUrl"] = "http://tools.localhost/notify"
paramMap["callbackUrl"] = "http://tools.localhost/verify"
paramMap["receiverInfo"] = string(receiverInfoBytes)
// 证书
privateKey, err := getKey("private_key")
if err != nil {
return payStr, err
}
// 签名
paramMap["sign"], err = getPaySign(paramMap, privateKey)
if err != nil {
return payStr, err
}
// 数据加密
paramMap, err = encrypt3DES(paramMap, desKey)
if err != nil {
return payStr, err
}
// 拼接支付表单
payStr = "form action='" + jdPayUrl + "' method='post' id='pay_form'>"
for k, v := range paramMap {
payStr += "input value='" + v + "' name='" + k + "' type='hidden'/>"
}
payStr += "/form>"
payStr += "script>var form = document.getElementById('pay_form');form.submit()/script>"
return payStr, nil
}
三、异步通知
数据解密、签名校验
// 异步通知信息解密
type NotifyQuery struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本号
Merchant string `xml:"merchant" json:"merchant"` // 商户号
Result NotifyResult `xml:"result" json:"result"` // 交易结果
Encrypt string `xml:"encrypt" json:"encrypt"` // 加密信息
}
type NotifyDecrypt struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本号
Merchant string `xml:"merchant" json:"merchant"` // 商户号
Result NotifyResult `xml:"result" json:"result"` // 交易结果
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 订单号
TradeType int `xml:"tradeType" json:"tradeType"` // 交易类型
Sign string `xml:"sign" json:"sign"` // 数据签名
Amount int64 `xml:"amount" json:"amount"` // 人民币支付总金额
OrderId string `json:"order_id"` // 京东交易流水号
Status string `xml:"status" json:"status"` // 交易状态
PayList NotifyPayList `xml:"payList" json:"payList"` // 支付方式明细
}
type NotifyResult struct {
Code string `xml:"code" json:"code"` // 交易返回码
Desc string `xml:"desc" json:"desc"` // 返回码信息
}
type NotifyPayList struct {
Pay []NotifyPay `xml:"pay" json:"pay"`
}
type NotifyPay struct {
PayType int `xml:"payType" json:"payType"` // 支付方式
Amount int64 `xml:"amount" json:"amount"` // 交易金额
Currency string `xml:"currency" json:"currency"` // 交易币种
TradeTime string `xml:"tradeTime" json:"tradeTime"` // 交易时间
}
// 异步通知信息解密
func notifyDataDecrypt(rawPost string) (notifyDecrypt NotifyDecrypt, err error) {
// 解析加密的支付机构参数为结构体
var notifyQuery NotifyQuery
err = xml.Unmarshal([]byte(rawPost), notifyQuery)
if err != nil {
return notifyDecrypt, err
}
// 解密支付机构参数
decryptBytes, err := decryptArg(notifyQuery, desKey)
if err != nil {
return notifyDecrypt, err
}
// 解析解密后的支付机构参数为结构体
err = xml.Unmarshal(decryptBytes, notifyDecrypt)
if err != nil {
return notifyDecrypt, err
}
// 证书
publicKey, err := getKey("public_key")
if err != nil {
return notifyDecrypt, err
}
// 校验签名
if !checkSign(decryptBytes, notifyDecrypt.Sign, publicKey) {
return notifyDecrypt, err
}
return notifyDecrypt, nil
}
四、交易查询
查询订单
type SearchWithoutSignRequest struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本
Merchant string `xml:"merchant" json:"merchant"` // 商户号
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 订单编号
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"` // 原交易流水号
TradeType string `xml:"tradeType" json:"tradeType"` // 交易类型
}
type SearchWithSignRequest struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本
Merchant string `xml:"merchant" json:"merchant"` // 商户号
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 订单编号
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"` // 原交易流水号
TradeType string `xml:"tradeType" json:"tradeType"` // 交易类型
Sign string `xml:"sign" json:"sign"` // 签名
}
type SearchResult struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本号
Merchant string `xml:"merchant" json:"merchant"` // 商户号
Result SearchResultRsp `xml:"result" json:"result"` // 交易结果
Encrypt string `xml:"encrypt" json:"encrypt"` // 加密信息
}
type SearchResultRsp struct {
Code string `xml:"code" json:"code"` // 交易返回码
Desc string `xml:"desc" json:"desc"` // 返回码信息
}
type SearchDecryptRsp struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Merchant string `xml:"merchant" json:"merchant"` // 商户号
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 订单编号
TradeType string `xml:"tradeType" json:"tradeType"` // 交易类型
Result SearchResultRsp `xml:"result" json:"result"` // 交易结果
Sign string `xml:"sign" json:"sign"` // 数据签名
Amount int64 `xml:"amount" json:"amount"` // 人民币支付总金额
Status string `xml:"status" json:"status"` // 交易状态
PayList SearchPayListRsp `xml:"payList" json:"payList"` // 支付方式明细
}
type SearchPayListRsp struct {
Pay []SearchPayRsp `xml:"pay" json:"pay"`
}
type SearchPayRsp struct {
PayType int `xml:"payType" json:"payType"` // 支付方式
Amount int64 `xml:"amount" json:"amount"` // 交易金额
Currency string `xml:"currency" json:"currency"` // 交易币种
TradeTime string `xml:"tradeTime" json:"tradeTime"` // 交易时间
}
const tradeWayUrl = "https://paygate.jd.com/service/query"
const customTradeType = "0" // 交易类型
const successCode = "000000"
func searchTrade(orderId string) (searchDecryptRsp SearchDecryptRsp, err error) {
searchWithoutSignRequest := SearchWithoutSignRequest{
Version: version,
Merchant: merchantId,
TradeNum: orderId,
OTradeNum: "",
TradeType: customTradeType,
}
xmlBytes, err := xml.Marshal(searchWithoutSignRequest)
xmlStr := xml.Header + string(xmlBytes)
xmlStr = replaceXmlStrBlankChar(xmlStr)
// 证书
privateKey, err := getKey("private_key")
if err != nil {
return searchDecryptRsp, err
}
// 签名
sign, err := getRsaSign(xmlStr, privateKey)
if err != nil {
return searchDecryptRsp, err
}
searchWithSignRequest := SearchWithSignRequest{
Version: searchWithoutSignRequest.Version,
Merchant: searchWithoutSignRequest.Merchant,
TradeNum: searchWithoutSignRequest.TradeNum,
OTradeNum: searchWithoutSignRequest.OTradeNum,
TradeType: searchWithoutSignRequest.TradeType,
Sign: sign,
}
xmlBytes, err = xml.Marshal(searchWithSignRequest)
xmlStr = strings.TrimRight(xml.Header, "\n") + string(xmlBytes)
desKeyBytes, err := base64.StdEncoding.DecodeString(desKey)
if err != nil {
return searchDecryptRsp, err
}
encryptBytes, err := tripleEcbDesEncrypt([]byte(xmlStr), desKeyBytes)
if err != nil {
return searchDecryptRsp, err
}
reqEncrypt := decimalByteSlice2HexString(encryptBytes)
reqEncrypt = base64.StdEncoding.EncodeToString([]byte(reqEncrypt))
searchWithEncrypt := ReqWithEncrypt{
Version: version,
Merchant: merchantId,
Encrypt: reqEncrypt,
}
xmlBytes, err = xml.Marshal(searchWithEncrypt)
if err != nil {
return searchDecryptRsp, err
}
xmlStr = strings.TrimRight(xml.Header, "\n") + string(xmlBytes)
request, err := http.NewRequest(http.MethodPost, tradeWayUrl, strings.NewReader(xmlStr))
if err != nil {
return searchDecryptRsp, err
}
request.Header.Add("content-type", "application/xml; charset=utf-8")
client := http.DefaultClient
response, err := client.Do(request)
if err != nil {
return searchDecryptRsp, err
}
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return searchDecryptRsp, err
}
searchResult := new(SearchResult)
if err = xml.Unmarshal(bodyBytes, searchResult); err != nil {
return searchDecryptRsp, err
}
if searchResult.Result.Code != successCode {
return searchDecryptRsp, errors.New(searchResult.Result.Desc)
}
// 解密数据
rspEncryptBytes, err := base64.StdEncoding.DecodeString(searchResult.Encrypt)
rspEncryptBytes, err = hexString2Bytes(string(rspEncryptBytes))
if err != nil {
return searchDecryptRsp, err
}
rspDecryptBytes, err := tripleEcbDesDecrypt(rspEncryptBytes, desKeyBytes)
if err != nil {
return searchDecryptRsp, err
}
err = xml.Unmarshal(rspDecryptBytes, searchDecryptRsp)
if err != nil {
return searchDecryptRsp, err
}
// 证书
publicKey, err := getKey("public_key")
if err != nil {
return searchDecryptRsp, err
}
// 校验签名
if !checkSign(rspDecryptBytes, searchDecryptRsp.Sign, publicKey) {
return searchDecryptRsp, err
}
return searchDecryptRsp, nil
}
五、申请退款
申请退款
// 退款
type Refund struct {
Merchant string `json:"merchant"`
TradeNum string `json:"tradeNum"`
OTradeNum string `json:"oTradeNum"`
Amount uint64 `json:"amount"`
Currency string `json:"currency"`
DesKey string `json:"desKey"`
PublicKey string `json:"publicKey"`
PrivateKey string `json:"privateKey"`
}
type RefundReqWithoutSign struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"`
Merchant string `xml:"merchant" json:"merchant"`
TradeNum string `xml:"tradeNum" json:"tradeNum"`
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"`
Amount uint64 `xml:"amount" json:"amount"`
Currency string `xml:"currency" json:"currency"`
}
type RefundReqWithSign struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"`
Merchant string `xml:"merchant" json:"merchant"`
TradeNum string `xml:"tradeNum" json:"tradeNum"`
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"`
Amount uint64 `xml:"amount" json:"amount"`
Currency string `xml:"currency" json:"currency"`
Sign string `xml:"sign" json:"sign"`
}
type RefundResult struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本号
Merchant string `xml:"merchant" json:"merchant"` // 商户号
Result RefundPayResultRsp `xml:"result" json:"result"` // 退款结果
Encrypt string `xml:"encrypt" json:"encrypt"` // 加密信息
}
type RefundPayResultRsp struct {
Code string `xml:"code" json:"code"` // 交易返回码
Desc string `xml:"desc" json:"desc"` // 返回码信息
}
type RefundPayDecryptRsp struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本号
Merchant string `xml:"merchant" json:"merchant"` // 商户号
TradeNum string `xml:"tradeNum" json:"tradeNum"`
TradeType string `xml:"tradeType"json:"tradeType"`
Result RefundPayResultRsp `xml:"result" json:"result"` // 退款结果
Sign string `xml:"sign" json:"sign"`
Amount uint64 `xml:"amount" json:"amount"`
Currency string `xml:"currency" json:"currency"`
TradeTime string `xml:"tradeTime" json:"tradeTime"`
Status string `xml:"status" json:"status"`
}
const refundGatewayUrl = "https://paygate.jd.com/service/refund"
// 申请退款
func refundTrade(orderId string, amount float64) (refundPayDecryptRsp RefundPayDecryptRsp, err error) {
totalFee, err := strconv.ParseUint(fmt.Sprintf("%.f", amount*100), 10, 64)
if err != nil {
return refundPayDecryptRsp, err
}
refundReqWithoutSign := RefundReqWithoutSign{
Version: version,
Merchant: merchantId,
TradeNum: orderId + "-1",
OTradeNum: orderId,
Amount: totalFee,
Currency: cny,
}
xmlBytes, err := xml.Marshal(refundReqWithoutSign)
xmlStr := xml.Header + string(xmlBytes)
xmlStr = replaceXmlStrBlankChar(xmlStr)
// 证书
privateKey, err := getKey("private_key")
if err != nil {
return refundPayDecryptRsp, err
}
// 签名
sign, err := getRsaSign(xmlStr, privateKey)
if err != nil {
return refundPayDecryptRsp, err
}
refundReqWithSign := RefundReqWithSign{
Version: refundReqWithoutSign.Version,
Merchant: refundReqWithoutSign.Merchant,
TradeNum: refundReqWithoutSign.TradeNum,
OTradeNum: refundReqWithoutSign.OTradeNum,
Amount: refundReqWithoutSign.Amount,
Currency: refundReqWithoutSign.Currency,
Sign: sign,
}
xmlBytes, err = xml.Marshal(refundReqWithSign)
xmlStr = strings.TrimRight(xml.Header, "\n") + string(xmlBytes)
desKeyBytes, err := base64.StdEncoding.DecodeString(desKey)
if err != nil {
return refundPayDecryptRsp, err
}
encryptBytes, err := tripleEcbDesEncrypt([]byte(xmlStr), desKeyBytes)
if err != nil {
return refundPayDecryptRsp, err
}
reqEncrypt := decimalByteSlice2HexString(encryptBytes)
reqEncrypt = base64.StdEncoding.EncodeToString([]byte(reqEncrypt))
refundReqWithEncrypt := ReqWithEncrypt{
Version: version,
Merchant: merchantId,
Encrypt: reqEncrypt,
}
xmlBytes, err = xml.Marshal(refundReqWithEncrypt)
xmlStr = strings.TrimRight(xml.Header, "\n") + string(xmlBytes)
request, err := http.NewRequest(http.MethodPost, refundGatewayUrl, strings.NewReader(xmlStr))
if err != nil {
return refundPayDecryptRsp, err
}
request.Header.Add("content-type", "application/xml; charset=utf-8")
client := http.DefaultClient
response, err := client.Do(request)
if err != nil {
return refundPayDecryptRsp, err
}
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return refundPayDecryptRsp, err
}
refundResult := new(RefundResult)
if err = xml.Unmarshal(bodyBytes, refundResult); err != nil {
return refundPayDecryptRsp, err
}
// 解密数据
rspEncryptBytes, err := base64.StdEncoding.DecodeString(refundResult.Encrypt)
if err != nil {
return refundPayDecryptRsp, err
}
rspEncryptBytes, err = hexString2Bytes(string(rspEncryptBytes))
if err != nil {
return refundPayDecryptRsp, err
}
rspDecryptBytes, err := tripleEcbDesDecrypt(rspEncryptBytes, desKeyBytes)
if err != nil {
return refundPayDecryptRsp, err
}
err = xml.Unmarshal(rspDecryptBytes, refundPayDecryptRsp)
if err != nil {
return refundPayDecryptRsp, err
}
// 证书
publicKey, err := getKey("public_key")
if err != nil {
return refundPayDecryptRsp, err
}
// 校验签名
if !checkSign(rspDecryptBytes, refundPayDecryptRsp.Sign, publicKey) {
return refundPayDecryptRsp, err
}
return refundPayDecryptRsp, nil
}
到此这篇关于golang实现京东支付v2版本的文章就介绍到这了,更多相关go京东支付v2内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:- django实现支付宝支付实例讲解
- Django1.11配合uni-app发起微信支付的实现
- 详解基于python-django框架的支付宝支付案例
- Django实现支付宝付款和微信支付的示例代码
- django 实现电子支付功能的示例代码
- python采用django框架实现支付宝即时到帐接口