golang实现京东支付v2版本的示例代码
来源:脚本之家
2023-01-07 12:11:12
0浏览
收藏
知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个Golang开发实战,手把手教大家学习《golang实现京东支付v2版本的示例代码》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!
一、准备阶段
pc&h5 接入步骤
官方文档 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, "/>") 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 > 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) 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) 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 = "" payStr += "" 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), ¬ifyQuery) if err != nil { return notifyDecrypt, err } // 解密支付机构参数 decryptBytes, err := decryptArg(notifyQuery, desKey) if err != nil { return notifyDecrypt, err } // 解析解密后的支付机构参数为结构体 err = xml.Unmarshal(decryptBytes, ¬ifyDecrypt) 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版本的示例代码》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
版本声明
本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除

- 上一篇
- golang切片扩容规则实现

- 下一篇
- Go语言实现布谷鸟过滤器的方法
评论列表
-
- 矮小的花瓣
- 这篇文章内容出现的刚刚好,细节满满,真优秀,收藏了,关注up主了!希望up主能多写Golang相关的文章。
- 2023-01-10 01:27:38
-
- 开心的枫叶
- 这篇技术文章真是及时雨啊,好细啊,感谢大佬分享,mark,关注博主了!希望博主能多写Golang相关的文章。
- 2023-01-08 12:24:04
查看更多
最新文章
-
- Golang · Go教程 | 18分钟前 | golang docker DevOps 自动化部署 GoReleaser
- Golang多环境部署,GoReleaser工具链分享
- 234浏览 收藏
-
- Golang · Go教程 | 24分钟前 |
- Golang错误处理发展与版本变化解析
- 344浏览 收藏
-
- Golang · Go教程 | 24分钟前 |
- Golang防范Web漏洞:CSRF/XSS防护技巧
- 487浏览 收藏
-
- Golang · Go教程 | 35分钟前 |
- Go高效时间戳:毫秒级获取不分配内存
- 146浏览 收藏
-
- Golang · Go教程 | 44分钟前 |
- GolangRPC压缩与性能优化技巧
- 407浏览 收藏
-
- Golang · Go教程 | 47分钟前 |
- Go语言集成HypertableThrift方案详解
- 436浏览 收藏
-
- Golang · Go教程 | 51分钟前 |
- 自定义Golang错误类型,实现error接口方法
- 114浏览 收藏
-
- Golang · Go教程 | 53分钟前 |
- Golang实现规格模式,灵活构建过滤逻辑
- 299浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang并发缓存sync.Map原理解析
- 413浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Golang指针并发安全问题详解
- 247浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go获取终端大小的实用技巧
- 286浏览 收藏
查看更多
课程推荐
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
查看更多
AI推荐
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 96次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 89次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 107次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 98次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 98次使用
查看更多
相关文章
-
- 浅谈支付系统开发基本流程
- 2023-01-09 332浏览