refund.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package refund
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "github.com/silenceper/wechat/v2/pay/config"
  6. "github.com/silenceper/wechat/v2/util"
  7. )
  8. var refundGateway = "https://api.mch.weixin.qq.com/secapi/pay/refund"
  9. // Refund struct extends context
  10. type Refund struct {
  11. *config.Config
  12. }
  13. // NewRefund return an instance of refund package
  14. func NewRefund(cfg *config.Config) *Refund {
  15. refund := Refund{cfg}
  16. return &refund
  17. }
  18. // Params 调用参数
  19. type Params struct {
  20. TransactionID string
  21. OutRefundNo string
  22. OutTradeNo string
  23. TotalFee string
  24. RefundFee string
  25. RefundDesc string
  26. RootCa string // ca证书
  27. NotifyURL string
  28. SignType string
  29. }
  30. // request 接口请求参数
  31. type request struct {
  32. AppID string `xml:"appid"`
  33. MchID string `xml:"mch_id"`
  34. NonceStr string `xml:"nonce_str"`
  35. Sign string `xml:"sign"`
  36. SignType string `xml:"sign_type,omitempty"`
  37. TransactionID string `xml:"transaction_id,omitempty"`
  38. OutTradeNo string `xml:"out_trade_no,omitempty"`
  39. OutRefundNo string `xml:"out_refund_no"`
  40. TotalFee string `xml:"total_fee"`
  41. RefundFee string `xml:"refund_fee"`
  42. RefundDesc string `xml:"refund_desc,omitempty"`
  43. NotifyURL string `xml:"notify_url,omitempty"`
  44. }
  45. // Response 接口返回
  46. type Response struct {
  47. ReturnCode string `xml:"return_code"`
  48. ReturnMsg string `xml:"return_msg"`
  49. AppID string `xml:"appid,omitempty"`
  50. MchID string `xml:"mch_id,omitempty"`
  51. NonceStr string `xml:"nonce_str,omitempty"`
  52. Sign string `xml:"sign,omitempty"`
  53. ResultCode string `xml:"result_code,omitempty"`
  54. ErrCode string `xml:"err_code,omitempty"`
  55. ErrCodeDes string `xml:"err_code_des,omitempty"`
  56. TransactionID string `xml:"transaction_id,omitempty"`
  57. OutTradeNo string `xml:"out_trade_no,omitempty"`
  58. OutRefundNo string `xml:"out_refund_no,omitempty"`
  59. RefundID string `xml:"refund_id,omitempty"`
  60. RefundFee string `xml:"refund_fee,omitempty"`
  61. SettlementRefundFee string `xml:"settlement_refund_fee,omitempty"`
  62. TotalFee string `xml:"total_fee,omitempty"`
  63. SettlementTotalFee string `xml:"settlement_total_fee,omitempty"`
  64. FeeType string `xml:"fee_type,omitempty"`
  65. CashFee string `xml:"cash_fee,omitempty"`
  66. CashFeeType string `xml:"cash_fee_type,omitempty"`
  67. }
  68. // Refund 退款申请
  69. func (refund *Refund) Refund(p *Params) (rsp Response, err error) {
  70. param := refund.GetSignParam(p)
  71. sign, err := util.ParamSign(param, refund.Key)
  72. if err != nil {
  73. return
  74. }
  75. req := request{
  76. AppID: param["appid"],
  77. MchID: param["mch_id"],
  78. NonceStr: param["nonce_str"],
  79. Sign: sign,
  80. SignType: param["sign_type"],
  81. OutRefundNo: param["out_refund_no"],
  82. TotalFee: param["total_fee"],
  83. RefundFee: param["refund_fee"],
  84. RefundDesc: param["refund_desc"],
  85. NotifyURL: param["notify_url"],
  86. }
  87. if p.OutTradeNo != "" {
  88. req.OutTradeNo = p.OutTradeNo
  89. }
  90. if p.TransactionID != "" {
  91. req.TransactionID = p.TransactionID
  92. }
  93. rawRet, err := util.PostXMLWithTLS(refundGateway, req, p.RootCa, refund.MchID)
  94. if err != nil {
  95. return
  96. }
  97. err = xml.Unmarshal(rawRet, &rsp)
  98. if err != nil {
  99. return
  100. }
  101. if rsp.ReturnCode == "SUCCESS" {
  102. if rsp.ResultCode == "SUCCESS" {
  103. err = nil
  104. return
  105. }
  106. err = fmt.Errorf("refund error, errcode=%s,errmsg=%s", rsp.ErrCode, rsp.ErrCodeDes)
  107. return
  108. }
  109. err = fmt.Errorf("[msg : xmlUnmarshalError] [rawReturn : %s] [sign : %s]", string(rawRet), sign)
  110. return
  111. }
  112. // GetSignParam 获取签名的参数
  113. func (refund *Refund) GetSignParam(p *Params) (param map[string]string) {
  114. nonceStr := util.RandomStr(32)
  115. param = make(map[string]string)
  116. param["appid"] = refund.AppID
  117. param["mch_id"] = refund.MchID
  118. param["nonce_str"] = nonceStr
  119. param["out_refund_no"] = p.OutRefundNo
  120. param["refund_desc"] = p.RefundDesc
  121. param["refund_fee"] = p.RefundFee
  122. param["total_fee"] = p.TotalFee
  123. if p.SignType == "" {
  124. param["sign_type"] = util.SignTypeMD5
  125. }
  126. if p.OutTradeNo != "" {
  127. param["out_trade_no"] = p.OutTradeNo
  128. }
  129. if p.TransactionID != "" {
  130. param["transaction_id"] = p.TransactionID
  131. }
  132. if p.NotifyURL != "" {
  133. param["notify_url"] = p.NotifyURL
  134. }
  135. return param
  136. }