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, "><")
  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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解基于python-django框架的支付宝支付案例

    一. 开发前的准备 1. 必须了解的知识 SDK:软件开发工具包,可以为开发者提供快速开发的工具 沙箱环境:也就是测试环境 支付宝支付金额的精度:小数点后两位(面试) 支付宝用的什么加密方式:RSA 2. 沙箱环境的配置 ① 登录支付宝开放平台 https://auth.alipay.com/login/ant_sso_index.htm?goto=https%3A%2F%2Fopen.alipay.com%2Fplatform%2Fhome.htm ② 进入管理中心后选择研[研发服务] ③ 生

  • django实现支付宝支付实例讲解

    安装python-alipay-sdk pip install python-alipay-sdk --upgrade 配置 视图函数orders/views.py # 订单支付 # /order/pay from alipay import AliPay, ISVAliPay import os from django.conf import settings class OrderPayView(View): def post(self, request): '''支付页面''' # 判断用

  • Django1.11配合uni-app发起微信支付的实现

    Django1.11配合uni-app发起微信支付! 经过三天的断断续续的奋战,我终于是干动了微信支付.为了以后不忘记,现在来一篇教程,来来来,开干!!! 一.准备阶段 1.准备阶段我们需要去微信官网申请一个小程序或者公众号.获得AppID和AppSecret. 2.去微信商户平台 成为商家,开通JSAPI用来获得商户号和自己配置的钥匙.然后再商户平台上面绑定小程序appid. (点击下面图片进入官方链接!) 在配置里面配置一个自己的key,需要记住后台开发的时候需要! 关联后即可在小程序管理页

  • django 实现电子支付功能的示例代码

    思路:调用第三方支付 API 接口实现支付功能.本来想用支付宝来实现第三方网站的支付功能的,但是在实际操作中发现支付宝没有 Python 接口,网上虽然有他人二次封装的的 Python 接口,但是对我这个小白白来说上手还是有点难度,后来发现 PayPal 有现成的 Django 模块,想着以学习的目的来实现这一功能(其实还是自己辣鸡),就决定以 PayPal 的电子支付功能来练手. 首先,安装 PayPal 的 Django 模块:django-paypal,具体介绍可以参考 GitHub上说明

  • Django实现支付宝付款和微信支付的示例代码

    支付宝支付和微信支付是当今互联网产品常用的功能,我使用Django Rest Framework实现了网页上支付宝支付和微信支付的一个通用服务,提供rpc接口给其他服务,包括获取支付宝支付页面url的rpc接口.支付宝支付成功异步回调http接口.获取微信支付二维码rpc接口.主动查询微信订单是否支付的rpc接口等. 支付宝网站支付需要蚂蚁金服开放平台账号,创建应用.配置秘钥等步骤请参考:蚂蚁金服支付宝电脑网站支付快速接入 微信网站支付需要到微信支付官网注册服务商账号, 目录结构如下: 1.mo

  • python采用django框架实现支付宝即时到帐接口

    因工作需要研究了支付宝即时到帐接口,并成功应用到网站上,把过程拿出来分享. 即时到帐只是支付宝众多商家服务中的一个,表示客户付款,客户用支付宝付款,支付宝收到款项后,马上通知你,并且此笔款项与交易脱离关系,商家可以马上使用. 即时到帐只对企业客户服务,注册成功企业账号以后,申请签约即时到帐产品,大约3-5个工作日后,签约成功,可以马上进入集成产品阶段. 这个是支付宝提供的接口,有asp,c#,java,php四种语言的,每种语言提供GBK和UTF-8两种方案.另带一份支付宝的文档,这份文档我感觉

  • 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/docLi

  • golang实现微信支付v3版本的方法

    一.准备阶段 获取私钥 官方文档 https://kf.qq.com/faq/161222N... 获取私钥证书的序列号 https://pay.weixin.qq.com/wik... openssl x509 -in 1900009191_20180326_cert.pem -noout -serial serial=1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C 私钥获取后有三个文件 apiclient_key.p12 apiclient_cert.pem

  • Golang Map实现赋值和扩容的示例代码

    golang map 操作,是map 实现中较复杂的逻辑.因为当赋值时,为了减少hash 冲突链的长度过长问题,会做map 的扩容以及数据的迁移.而map 的扩容以及数据的迁移也是关注的重点. 数据结构 首先,我们需要重新学习下map实现的数据结构: type hmap struct { count int flags uint8 B uint8 noverflow uint16 hash0 uint32 buckets unsafe.Pointer oldbuckets unsafe.Poin

  • Java调用微信支付功能的方法示例代码

    Java 使用微信支付 前言百度搜了一下微信支付,都描述的不太好,于是乎打算自己写一个案例,希望以后拿来直接改造使用. 因为涉及二维码的前端显示,所以有前端的内容 一. 准备工作 所需微信公众号信息配置 APPID:绑定支付的APPID(必须配置) MCHID:商户号(必须配置) KEY:商户支付密钥,参考开户邮件设置(必须配置) APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置) 我这个案例用的是尚硅谷一位老师提供的,这里不方便提供出来,需要大家自己找,或者公司提供 二

  • Golang实现AES加密和解密的示例代码

    目录 对称加密 AES 算法 加解密 文件加密解密 说明 对称加密 AES 算法 (Advanced Encryption Standard ,AES) 优点 算法公开.计算量小.加密速度快.加密效率高. 缺点 发送方和接收方必须商定好密钥,然后使双方都能保存好密钥,密钥管理成为双方的负担. 应用场景 相对大一点的数据量或关键数据的加密. 加解密 package helpers import ( "bytes" "crypto/aes" "crypto/c

  • Golang实现可重入锁的示例代码

    目录 什么是可重入锁 具体实现 项目中遇到了可重入锁的需求和实现,具体记录下. 什么是可重入锁 我们平时说的分布式锁,一般指的是在不同服务器上的多个线程中,只有一个线程能抢到一个锁,从而执行一个任务.而我们使用锁就是保证一个任务只能由一个线程来完成.所以我们一般是使用这样的三段式逻辑: Lock();DoJob();Unlock(); 但是由于我们的系统都是分布式的,这个锁一般不会只放在某个进程中,我们会借用第三方存储,比如 Redis 来做这种分布式锁.但是一旦借助了第三方存储,我们就必须面对

  • Golang实现图片上传功能的示例代码

    目录 1.前端代码 2.JS代码 3.后端代码 该代码为使用beego实现前后端图片上传.话不多说,直接上代码. 1.前端代码 html代码: <div class="col-5 f-l text text-r">背景图(必须):</div> <div class="img-box"> <label> <span class="copy-btn Hui-iconfont"></s

  • golang NewRequest/gorequest实现http请求的示例代码

    通过go语言实现http请求 http.Post import (     "net/http"     "net/url" ) data := url.Values{"start":{"100"}, "hobby":{"xxxx"}} body := strings.NewReader(data.Encode()) resp, err := http.Post("127.0.

  • golang模拟实现带超时的信号量示例代码

    前言 最近在写项目,需要用到信号量等待一些资源完成,但是最多等待N毫秒.在看本文的正文之前,我们先来看下C语言里的实现方法. 在C语言里,有如下的API来实现带超时的信号量等待: SYNOPSIS #include <pthread.h> int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); 然后在查看golang的document后,发

  • PHP获取php,mysql,apche的版本信息示例代码

    直接获取PHP的版本 复制代码 代码如下: <?php echo "PHP软件版本:".phpversion(); ?> 直接获取MYSQL版本 复制代码 代码如下: <?php mysql_connect('127.0.0.1','root',''); echo mysql_get_server_info(); ?> 直接获取Apache版本 复制代码 代码如下: <?php $version=apache_get_version(); echo &qu

随机推荐