一、準(zhǔn)備階段
pch5 接入步驟
官方文檔 https://payapi.jd.com/docList...
查看主要接入步驟
密鑰生成
• 需要設(shè)置desc key
• md5 key 和 app id app對(duì)接會(huì)使用
• 證書文件名稱
my_rsa_private_pkcs8_key.pem
wy_rsa_public_key.pem
示例程序使用私鑰格式為 pkcs8 格式
官方的SDK中的數(shù)據(jù)可以在示例程序中使用
下載SDK地址 https://payapi.jd.com/docList...
找到接口文檔中的Demo
還會(huì)用到的包
import (
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"time"
)
加密、解密、驗(yàn)證簽名
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
//簽名校驗(yàn)
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填充規(guī)則
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數(shù)據(jù)長(zhǎng)度
for i := 0; i 4; i++ {
resultByte = append(resultByte, sizeByte[i])
}
//填充原數(shù)據(jù)長(zhǎng)度
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
}
// 字節(jié)數(shù)組表示的實(shí)際長(zhǎng)度
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轉(zhuǎn)16進(jìn)制字符串
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
}
// 十六進(jìn)制字符串轉(zhuǎn)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))
}
// 對(duì)消息的散列值進(jìn)行數(shù)字簽名
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
}
// ------------
// 驗(yàn)證簽名
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
}
二、發(fā)起支付
常量
常量
const version = "V2.0" // 京東支付版本
const merchantId = "" // 商戶id
const desKey = "" // desc key
const timeLayout = "20060102150405"
const cny = "CNY"
const practicalityGoodsType = "GT01" //商品類型-實(shí)物
const businessServiceConsumeCode = "100001"
const practicality = "0" //商品類型-實(shí)物
調(diào)用在線支付接口
pc h5 發(fā)起支付
官方文檔 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"` //商戶號(hào)
Encrypt string `xml:"encrypt" json:"encrypt"` //加密數(shù)據(jù)
}
const version = "V2.0" // 京東支付版本
const merchantId = "22294531"
const desKey = "ta4E/aspLA3lgFGKmNDNRYU92RkZ4w2t"
const timeLayout = "20060102150405"
const cny = "CNY"
const practicalityGoodsType = "GT01" //商品類型-實(shí)物
const businessServiceConsumeCode = "100001"
const practicality = "0" //商品類型-實(shí)物
type GoodsInfo struct {
Id string `json:"id"` //商品編號(hào)
Name string `json:"name"` //商品名稱
Price int64 `json:"price"` //商品單價(jià),單位分
Num uint32 `json:"num"` //商品數(shù)量
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"` // 是否報(bào)關(guān)Y/N
GoodsUnderBonded string `json:"goodsUnderBonded"` // 是否保稅貨物項(xiàng)下付款Y/N
}
const jdPayUrl = "https://wepay.jd.com/jdpay/saveOrder"
// pc h5 form 表單提交支付
func postFormPay(arg Order) (payStr string, err error) {
// 支付參數(shù)
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, // 商品類型-實(shí)物
})
}
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, // 收貨人手機(jī)號(hào)
Address: detailAddress, // 地址要求包過(guò)省市區(qū)
Province: arg.ShippingAddress.Province, // 省
City: arg.ShippingAddress.City, // 市
Country: arg.ShippingAddress.Country, // 區(qū)
}
receiverInfoBytes, _ := json.Marshal(receiverInfo)
orderId := fmt.Sprintf("test%s%s", time.Now().Format("20060102150405"), randNumber())
paramMap["version"] = version
paramMap["merchant"] = merchantId
paramMap["tradeNum"] = orderId // 訂單號(hào)
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" //訂單失效時(shí)長(zhǎng),單位秒
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
}
// 數(shù)據(jù)加密
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
}
三、異步通知
數(shù)據(jù)解密、簽名校驗(yàn)
// 異步通知信息解密
type NotifyQuery struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本號(hào)
Merchant string `xml:"merchant" json:"merchant"` // 商戶號(hào)
Result NotifyResult `xml:"result" json:"result"` // 交易結(jié)果
Encrypt string `xml:"encrypt" json:"encrypt"` // 加密信息
}
type NotifyDecrypt struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` // 版本號(hào)
Merchant string `xml:"merchant" json:"merchant"` // 商戶號(hào)
Result NotifyResult `xml:"result" json:"result"` // 交易結(jié)果
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 訂單號(hào)
TradeType int `xml:"tradeType" json:"tradeType"` // 交易類型
Sign string `xml:"sign" json:"sign"` // 數(shù)據(jù)簽名
Amount int64 `xml:"amount" json:"amount"` // 人民幣支付總金額
OrderId string `json:"order_id"` // 京東交易流水號(hào)
Status string `xml:"status" json:"status"` // 交易狀態(tài)
PayList NotifyPayList `xml:"payList" json:"payList"` // 支付方式明細(xì)
}
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"` // 交易時(shí)間
}
// 異步通知信息解密
func notifyDataDecrypt(rawPost string) (notifyDecrypt NotifyDecrypt, err error) {
// 解析加密的支付機(jī)構(gòu)參數(shù)為結(jié)構(gòu)體
var notifyQuery NotifyQuery
err = xml.Unmarshal([]byte(rawPost), notifyQuery)
if err != nil {
return notifyDecrypt, err
}
// 解密支付機(jī)構(gòu)參數(shù)
decryptBytes, err := decryptArg(notifyQuery, desKey)
if err != nil {
return notifyDecrypt, err
}
// 解析解密后的支付機(jī)構(gòu)參數(shù)為結(jié)構(gòu)體
err = xml.Unmarshal(decryptBytes, notifyDecrypt)
if err != nil {
return notifyDecrypt, err
}
// 證書
publicKey, err := getKey("public_key")
if err != nil {
return notifyDecrypt, err
}
// 校驗(yàn)簽名
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"` // 商戶號(hào)
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 訂單編號(hào)
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"` // 原交易流水號(hào)
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"` // 商戶號(hào)
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 訂單編號(hào)
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"` // 原交易流水號(hào)
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"` // 版本號(hào)
Merchant string `xml:"merchant" json:"merchant"` // 商戶號(hào)
Result SearchResultRsp `xml:"result" json:"result"` // 交易結(jié)果
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"` // 商戶號(hào)
TradeNum string `xml:"tradeNum" json:"tradeNum"` // 訂單編號(hào)
TradeType string `xml:"tradeType" json:"tradeType"` // 交易類型
Result SearchResultRsp `xml:"result" json:"result"` // 交易結(jié)果
Sign string `xml:"sign" json:"sign"` // 數(shù)據(jù)簽名
Amount int64 `xml:"amount" json:"amount"` // 人民幣支付總金額
Status string `xml:"status" json:"status"` // 交易狀態(tài)
PayList SearchPayListRsp `xml:"payList" json:"payList"` // 支付方式明細(xì)
}
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"` // 交易時(shí)間
}
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)
}
// 解密數(shù)據(jù)
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
}
// 校驗(yàn)簽名
if !checkSign(rspDecryptBytes, searchDecryptRsp.Sign, publicKey) {
return searchDecryptRsp, err
}
return searchDecryptRsp, nil
}
五、申請(qǐng)退款
申請(qǐng)退款
// 退款
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"` // 版本號(hào)
Merchant string `xml:"merchant" json:"merchant"` // 商戶號(hào)
Result RefundPayResultRsp `xml:"result" json:"result"` // 退款結(jié)果
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"` // 版本號(hào)
Merchant string `xml:"merchant" json:"merchant"` // 商戶號(hào)
TradeNum string `xml:"tradeNum" json:"tradeNum"`
TradeType string `xml:"tradeType"json:"tradeType"`
Result RefundPayResultRsp `xml:"result" json:"result"` // 退款結(jié)果
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"
// 申請(qǐng)退款
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
}
// 解密數(shù)據(jù)
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
}
// 校驗(yàn)簽名
if !checkSign(rspDecryptBytes, refundPayDecryptRsp.Sign, publicKey) {
return refundPayDecryptRsp, err
}
return refundPayDecryptRsp, nil
}
到此這篇關(guān)于golang實(shí)現(xiàn)京東支付v2版本的文章就介紹到這了,更多相關(guān)go京東支付v2內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- django實(shí)現(xiàn)支付寶支付實(shí)例講解
- Django1.11配合uni-app發(fā)起微信支付的實(shí)現(xiàn)
- 詳解基于python-django框架的支付寶支付案例
- Django實(shí)現(xiàn)支付寶付款和微信支付的示例代碼
- django 實(shí)現(xiàn)電子支付功能的示例代碼
- python采用django框架實(shí)現(xiàn)支付寶即時(shí)到帳接口