First, the preparation stage
Pc&h 5 Access procedure
The official documentation payapi.jd.com/docList.htm… View main access steps
Key generation
• Need to set the DESC key • MD5 key and APP ID • Name of the certificate file used for app interconnect
my_rsa_private_pkcs8_key.pem
wy_rsa_public_key.pem
Copy the code
The sample program uses the private key format pkCS8
The official data can be used in the sample program in the SDK download the SDK address payapi.jd.com/docList.htm… Find the Demo in the interface documentation
Bags you’ll also need
import (
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"time"
)
Copy the code
Encrypt, decrypt, and verify signatures
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(a) 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
// Signature verification
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)
}
// ------
/ / encryption
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))
iferr ! =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)
iferr ! =nil {
return nil, err
}
encryptBytes, err := base64.StdEncoding.DecodeString(notifyQuery.Encrypt)
iferr ! =nil {
return nil, err
}
encryptBytes, err = hexString2Bytes(string(encryptBytes))
iferr ! =nil {
return nil, err
}
decryptBytes, err = tripleEcbDesDecrypt(encryptBytes, desKeyBytes)
iferr ! =nil {
return nil, err
}
return decryptBytes, nil
}
// JDAPP populates rules
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
// Padding the length of byte data
for i := 0; i < 4; i++ {
resultByte = append(resultByte, sizeByte[i])
}
// Fill the length of the original data
for j := 0; j < merchantData; j++ {
resultByte = append(resultByte, origData[j])
}
/ / fill 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
}
// The actual length of the byte array
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 to a hexadecimal character string
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
}
// The hexadecimal string is converted to byte
func hexString2Bytes(str string) ([]byte, error) {
Bys, err := hex.DecodeString(str)
iferr ! =nil {
return nil, err
}
return Bys, nil
}
/ / base decoding
func base64DecodeStr(src string) string {
a, err := base64.StdEncoding.DecodeString(src)
iferr ! =nil {
return ""
}
return string(a)
}
/ / Des declassified
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)
iferr ! =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)
iferr ! =nil {
return nil, err
}
buf2, err := encrypt(buf1, k2)
iferr ! =nil {
return nil, err
}
out, err := decrypt(buf2, k1)
iferr ! =nil {
return nil, err
}
out = jdUnPadding(out)
return out, nil
}
/ / sha256 encryption
func hasha256(str string) string {
h := sha256.New()
h.Write([]byte(str))
cipherStr := h.Sum(nil)
//return cipherStr
return hex.EncodeToString(cipherStr)
}
// base unencode
func base64EncodeStr(src string) string {
return base64.StdEncoding.EncodeToString([]byte(src))
}
// Digitally sign the hash value of the message
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)
iferr ! =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)
iferr ! =nil {
return nil, errors.New("sign error")}return sign, nil
}
/ / Des encryption
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)
iferr ! =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)
iferr ! =nil {
return nil, err
}
buf2, err := decrypt(buf1, k2)
iferr ! =nil {
return nil, err
}
out, err := encrypt(buf2, k3)
iferr ! =nil {
return nil, err
}
return out, nil
}
// ------------
// Verify the signature
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)
iferr ! =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))
iferr ! =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)
iferr ! =nil {
return false
}
sha256Str := hasha256(paramStr)
return verifyPKCS1v15([]byte(sha256Str), signByte, []byte(publicKey), crypto.Hash(0))}// -------
// String concatenation
// Pay string concatenation
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()
}
Copy the code
Program to load the key
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)
iferr ! =nil {
return "", err
}
defer fileHandler.Close()
keyBytes, err := ioutil.ReadAll(fileHandler)
iferr ! =nil {
return "", err
}
return string(keyBytes), nil
}
Copy the code
2. Initiate payment
constant
constantconst version = "V2.0" // Jd Pay version
const merchantId = "" / / merchant id
const desKey = "" // desc key
const timeLayout = "20060102150405"
const cny = "CNY"
const practicalityGoodsType = "GT01" // Item type - physical object
const businessServiceConsumeCode = "100001"
const practicality = "0" // Item type - physical object
Copy the code
Invoke the online payment interface
PC h5 payapi.jd.com/docList.htm initiate payment official document…
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"` / / version
Merchant string `xml:"merchant" json:"merchant"` / / merchants
Encrypt string `xml:"encrypt" json:"encrypt"` // Encrypt data
}
const version = "V2.0" // Jd Pay version
const merchantId = "22294531"
const desKey = "ta4E/aspLA3lgFGKmNDNRYU92RkZ4w2t"
const timeLayout = "20060102150405"
const cny = "CNY"
const practicalityGoodsType = "GT01" // Item type - physical object
const businessServiceConsumeCode = "100001"
const practicality = "0" // Item type - physical object
type GoodsInfo struct {
Id string `json:"id"` // Product id
Name string `json:"name"` // Product name
Price int64 `json:"price"` // Unit price
Num uint32 `json:"num"` // Quantity of goods
Type string `json:"type"` // Item 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"` // Whether declare Y/N
GoodsUnderBonded string `json:"goodsUnderBonded"` // Whether to pay Y/N under bonded goods
}
const jdPayUrl = "https://wepay.jd.com/jdpay/saveOrder"
// PC H5 form submits payment
func postFormPay(arg Order) (payStr string, err error) {
// Payment parameters
paramMap := make(map[string]string)
amountStr := fmt.Sprintf("%.f", arg.Amount*100)
totalFee, err := strconv.ParseInt(amountStr, 10.64)
iferr ! =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)
iferr ! =nil {
return payStr, err
}
goodsInfos = append(goodsInfos, GoodsInfo{
Id: v.GoodsNo,
Name: v.GoodsName,
Price: price,
Num: v.GoodsNum,
Type: practicalityGoodsType, // Item type - physical object
})
}
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, // Name of consignee
Mobile: arg.ShippingAddress.Mobile, // Consignee's mobile phone number
Address: detailAddress, // The address must be within the province
Province: arg.ShippingAddress.Province, / / province
City: arg.ShippingAddress.City, / / the city
Country: arg.ShippingAddress.Country, / / area
}
receiverInfoBytes, _ := json.Marshal(receiverInfo)
orderId := fmt.Sprintf("test%s%s", time.Now().Format("20060102150405"), randNumber())
paramMap["version"] = version
paramMap["merchant"] = merchantId
paramMap["tradeNum"] = orderId / / order number
paramMap["tradeName"] = orderId // Order description
paramMap["tradeDesc"] = orderId // Order description
paramMap["payMerchant"] = "test" // Merchant name
paramMap["tradeTime"] = time.Now().Format(timeLayout)
paramMap["amount"] = fmt.Sprintf("%v", totalFee)
paramMap["orderType"] = practicality
paramMap["currency"] = cny
paramMap["userId"] = "100"
paramMap["expireTime"] = "3600" // Order expiration time, in seconds
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)
/ / certificate
privateKey, err := getKey("private_key")
iferr ! =nil {
return payStr, err
}
/ / signature
paramMap["sign"], err = getPaySign(paramMap, privateKey)
iferr ! =nil {
return payStr, err
}
// Data encryption
paramMap, err = encrypt3DES(paramMap, desKey)
iferr ! =nil {
return payStr, err
}
// Splice the payment form
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 += ""
return payStr, nil
}
Copy the code
Asynchronous notification
Data decryption and signature verification
// Decrypt asynchronous notification messages
type NotifyQuery struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` / / version number
Merchant string `xml:"merchant" json:"merchant"` / / merchants
Result NotifyResult `xml:"result" json:"result"` // Transaction result
Encrypt string `xml:"encrypt" json:"encrypt"` // Encrypt information
}
type NotifyDecrypt struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` / / version number
Merchant string `xml:"merchant" json:"merchant"` / / merchants
Result NotifyResult `xml:"result" json:"result"` // Transaction result
TradeNum string `xml:"tradeNum" json:"tradeNum"` / / order number
TradeType int `xml:"tradeType" json:"tradeType"` // Transaction type
Sign string `xml:"sign" json:"sign"` // Data signature
Amount int64 `xml:"amount" json:"amount"` // Total amount paid in RMB
OrderId string `json:"order_id"` // Jingdong transaction serial number
Status string `xml:"status" json:"status"` // Transaction status
PayList NotifyPayList `xml:"payList" json:"payList"` // Details of payment methods
}
type NotifyResult struct {
Code string `xml:"code" json:"code"` // Return code
Desc string `xml:"desc" json:"desc"` // Return code information
}
type NotifyPayList struct {
Pay []NotifyPay `xml:"pay" json:"pay"`
}
type NotifyPay struct {
PayType int `xml:"payType" json:"payType"` // Payment method
Amount int64 `xml:"amount" json:"amount"` // Transaction amount
Currency string `xml:"currency" json:"currency"` // Transaction currency
TradeTime string `xml:"tradeTime" json:"tradeTime"` // Transaction time
}
// Decrypt asynchronous notification messages
func notifyDataDecrypt(rawPost string) (notifyDecrypt NotifyDecrypt, err error) {
// Parse the encrypted payment mechanism argument as a structure
var notifyQuery NotifyQuery
err = xml.Unmarshal([]byte(rawPost), ¬ifyQuery)
iferr ! =nil {
return notifyDecrypt, err
}
// Decrypt the payment institution parameters
decryptBytes, err := decryptArg(notifyQuery, desKey)
iferr ! =nil {
return notifyDecrypt, err
}
// Parse the decrypted payment mechanism parameters as structs
err = xml.Unmarshal(decryptBytes, ¬ifyDecrypt)
iferr ! =nil {
return notifyDecrypt, err
}
/ / certificate
publicKey, err := getKey("public_key")
iferr ! =nil {
return notifyDecrypt, err
}
// Verify the signature
if! checkSign(decryptBytes, notifyDecrypt.Sign, publicKey) {return notifyDecrypt, err
}
return notifyDecrypt, nil
}
Copy the code
Fourth, transaction inquiry
Query order
type SearchWithoutSignRequest struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` / / version
Merchant string `xml:"merchant" json:"merchant"` / / merchants
TradeNum string `xml:"tradeNum" json:"tradeNum"` // Order no
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"` // Original transaction serial number
TradeType string `xml:"tradeType" json:"tradeType"` // Transaction type
}
type SearchWithSignRequest struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` / / version
Merchant string `xml:"merchant" json:"merchant"` / / merchants
TradeNum string `xml:"tradeNum" json:"tradeNum"` // Order no
OTradeNum string `xml:"oTradeNum" json:"oTradeNum"` // Original transaction serial number
TradeType string `xml:"tradeType" json:"tradeType"` // Transaction type
Sign string `xml:"sign" json:"sign"` / / signature
}
type SearchResult struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` / / version number
Merchant string `xml:"merchant" json:"merchant"` / / merchants
Result SearchResultRsp `xml:"result" json:"result"` // Transaction result
Encrypt string `xml:"encrypt" json:"encrypt"` // Encrypt information
}
type SearchResultRsp struct {
Code string `xml:"code" json:"code"` // Return code
Desc string `xml:"desc" json:"desc"` // Return code information
}
type SearchDecryptRsp struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Merchant string `xml:"merchant" json:"merchant"` / / merchants
TradeNum string `xml:"tradeNum" json:"tradeNum"` // Order no
TradeType string `xml:"tradeType" json:"tradeType"` // Transaction type
Result SearchResultRsp `xml:"result" json:"result"` // Transaction result
Sign string `xml:"sign" json:"sign"` // Data signature
Amount int64 `xml:"amount" json:"amount"` // Total amount paid in RMB
Status string `xml:"status" json:"status"` // Transaction status
PayList SearchPayListRsp `xml:"payList" json:"payList"` // Details of payment methods
}
type SearchPayListRsp struct {
Pay []SearchPayRsp `xml:"pay" json:"pay"`
}
type SearchPayRsp struct {
PayType int `xml:"payType" json:"payType"` // Payment method
Amount int64 `xml:"amount" json:"amount"` // Transaction amount
Currency string `xml:"currency" json:"currency"` // Transaction currency
TradeTime string `xml:"tradeTime" json:"tradeTime"` // Transaction time
}
const tradeWayUrl = "https://paygate.jd.com/service/query"
const customTradeType = "0" // Transaction type
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)
/ / certificate
privateKey, err := getKey("private_key")
iferr ! =nil {
return searchDecryptRsp, err
}
/ / signature
sign, err := getRsaSign(xmlStr, privateKey)
iferr ! =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)
iferr ! =nil {
return searchDecryptRsp, err
}
encryptBytes, err := tripleEcbDesEncrypt([]byte(xmlStr), desKeyBytes)
iferr ! =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)
iferr ! =nil {
return searchDecryptRsp, err
}
xmlStr = strings.TrimRight(xml.Header, "\n") + string(xmlBytes)
request, err := http.NewRequest(http.MethodPost, tradeWayUrl, strings.NewReader(xmlStr))
iferr ! =nil {
return searchDecryptRsp, err
}
request.Header.Add("content-type"."application/xml; charset=utf-8")
client := http.DefaultClient
response, err := client.Do(request)
iferr ! =nil {
return searchDecryptRsp, err
}
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
iferr ! =nil {
return searchDecryptRsp, err
}
searchResult := new(SearchResult)
iferr = xml.Unmarshal(bodyBytes, searchResult); err ! =nil {
return searchDecryptRsp, err
}
ifsearchResult.Result.Code ! = successCode {return searchDecryptRsp, errors.New(searchResult.Result.Desc)
}
// Decrypt data
rspEncryptBytes, err := base64.StdEncoding.DecodeString(searchResult.Encrypt)
rspEncryptBytes, err = hexString2Bytes(string(rspEncryptBytes))
iferr ! =nil {
return searchDecryptRsp, err
}
rspDecryptBytes, err := tripleEcbDesDecrypt(rspEncryptBytes, desKeyBytes)
iferr ! =nil {
return searchDecryptRsp, err
}
err = xml.Unmarshal(rspDecryptBytes, &searchDecryptRsp)
iferr ! =nil {
return searchDecryptRsp, err
}
/ / certificate
publicKey, err := getKey("public_key")
iferr ! =nil {
return searchDecryptRsp, err
}
// Verify the signature
if! checkSign(rspDecryptBytes, searchDecryptRsp.Sign, publicKey) {return searchDecryptRsp, err
}
return searchDecryptRsp, nil
}
Copy the code
Apply for a refund
To apply for a refund
/ / refund
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"` / / version number
Merchant string `xml:"merchant" json:"merchant"` / / merchants
Result RefundPayResultRsp `xml:"result" json:"result"` // Result of refund
Encrypt string `xml:"encrypt" json:"encrypt"` // Encrypt information
}
type RefundPayResultRsp struct {
Code string `xml:"code" json:"code"` // Return code
Desc string `xml:"desc" json:"desc"` // Return code information
}
type RefundPayDecryptRsp struct {
XMLName xml.Name `xml:"jdpay" json:"-"`
Version string `xml:"version" json:"version"` / / version number
Merchant string `xml:"merchant" json:"merchant"` / / merchants
TradeNum string `xml:"tradeNum" json:"tradeNum"`
TradeType string `xml:"tradeType"json:"tradeType"`
Result RefundPayResultRsp `xml:"result" json:"result"` // Result of refund
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"
// Request a refund
func refundTrade(orderId string, amount float64) (refundPayDecryptRsp RefundPayDecryptRsp, err error) {
totalFee, err := strconv.ParseUint(fmt.Sprintf("%.f", amount*100), 10.64)
iferr ! =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)
/ / certificate
privateKey, err := getKey("private_key")
iferr ! =nil {
return refundPayDecryptRsp, err
}
/ / signature
sign, err := getRsaSign(xmlStr, privateKey)
iferr ! =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)
iferr ! =nil {
return refundPayDecryptRsp, err
}
encryptBytes, err := tripleEcbDesEncrypt([]byte(xmlStr), desKeyBytes)
iferr ! =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))
iferr ! =nil {
return refundPayDecryptRsp, err
}
request.Header.Add("content-type"."application/xml; charset=utf-8")
client := http.DefaultClient
response, err := client.Do(request)
iferr ! =nil {
return refundPayDecryptRsp, err
}
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
iferr ! =nil {
return refundPayDecryptRsp, err
}
refundResult := new(RefundResult)
iferr = xml.Unmarshal(bodyBytes, refundResult); err ! =nil {
return refundPayDecryptRsp, err
}
// Decrypt data
rspEncryptBytes, err := base64.StdEncoding.DecodeString(refundResult.Encrypt)
iferr ! =nil {
return refundPayDecryptRsp, err
}
rspEncryptBytes, err = hexString2Bytes(string(rspEncryptBytes))
iferr ! =nil {
return refundPayDecryptRsp, err
}
rspDecryptBytes, err := tripleEcbDesDecrypt(rspEncryptBytes, desKeyBytes)
iferr ! =nil {
return refundPayDecryptRsp, err
}
err = xml.Unmarshal(rspDecryptBytes, &refundPayDecryptRsp)
iferr ! =nil {
return refundPayDecryptRsp, err
}
/ / certificate
publicKey, err := getKey("public_key")
iferr ! =nil {
return refundPayDecryptRsp, err
}
// Verify the signature
if! checkSign(rspDecryptBytes, refundPayDecryptRsp.Sign, publicKey) {return refundPayDecryptRsp, err
}
return refundPayDecryptRsp, nil
}
Copy the code