github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mch/pay/refundquery.go (about)

     1  package pay
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"github.com/chanxuehong/wechat/mch/core"
     9  	wechatutil "github.com/chanxuehong/wechat/util"
    10  )
    11  
    12  // RefundQuery 查询退款.
    13  func RefundQuery(clt *core.Client, req map[string]string) (resp map[string]string, err error) {
    14  	return clt.PostXML(core.APIBaseURL()+"/pay/refundquery", req)
    15  }
    16  
    17  type RefundQueryRequest struct {
    18  	XMLName struct{} `xml:"xml" json:"-"`
    19  
    20  	// 必选参数, 四选一
    21  	TransactionId string `xml:"transaction_id"` // 微信订单号
    22  	OutTradeNo    string `xml:"out_trade_no"`   // 商户订单号
    23  	OutRefundNo   string `xml:"out_refund_no"`  // 商户退款单号
    24  	RefundId      string `xml:"refund_id"`      // 微信退款单号
    25  
    26  	// 可选参数
    27  	NonceStr string `xml:"nonce_str"` // 随机字符串,不长于32位。NOTE: 如果为空则系统会自动生成一个随机字符串。
    28  	SignType string `xml:"sign_type"` // 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    29  }
    30  
    31  type RefundQueryResponse struct {
    32  	XMLName struct{} `xml:"xml" json:"-"`
    33  
    34  	// 必选返回
    35  	TransactionId string       `xml:"transaction_id"` // 微信订单号
    36  	OutTradeNo    string       `xml:"out_trade_no"`   // 商户系统内部的订单号
    37  	TotalFee      int64        `xml:"total_fee"`      // 订单总金额,单位为分,只能为整数,详见支付金额
    38  	CashFee       int64        `xml:"cash_fee"`       // 现金支付金额,单位为分,只能为整数,详见支付金额
    39  	RefundCount   int          `xml:"refund_count"`   // 退款笔数
    40  	RefundList    []RefundItem `xml:"refund_list"`    // 退款列表
    41  
    42  	// 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断
    43  	SettlementTotalFee *int64 `xml:"settlement_total_fee"` // 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    44  	FeeType            string `xml:"fee_type"`             // 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    45  	CashFeeType        string `xml:"cash_fee_type"`        // 现金支付货币类型
    46  }
    47  
    48  type RefundItem struct {
    49  	XMLName struct{} `xml:"xml" json:"-"`
    50  
    51  	// 必选返回
    52  	OutRefundNo      string `xml:"out_refund_no"`      // 商户退款单号
    53  	RefundId         string `xml:"refund_id"`          // 微信退款单号
    54  	RefundFee        int64  `xml:"refund_fee"`         // 申请退款金额
    55  	RefundStatus     string `xml:"refund_status"`      // 退款状态
    56  	RefundRecvAccout string `xml:"refund_recv_accout"` // 退款入账账户
    57  
    58  	// 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断
    59  	RefundChannel       string    `xml:"refund_channel"`        // 退款渠道
    60  	SettlementRefundFee *int64    `xml:"settlement_refund_fee"` // 退款金额
    61  	RefundAccount       string    `xml:"refund_account"`        // 退款资金来源
    62  	RefundSuccessTime   time.Time `xml:"refund_success_time"`   // 退款成功时间
    63  }
    64  
    65  // RefundQuery2 查询退款.
    66  //
    67  //	NOTE: 该函数不支持 代金券 功能, 如果有 代金券 功能请使用 RefundQuery 函数.
    68  func RefundQuery2(clt *core.Client, req *RefundQueryRequest) (resp *RefundQueryResponse, err error) {
    69  	m1 := make(map[string]string, 16)
    70  	if req.TransactionId != "" {
    71  		m1["transaction_id"] = req.TransactionId
    72  	}
    73  	if req.OutTradeNo != "" {
    74  		m1["out_trade_no"] = req.OutTradeNo
    75  	}
    76  	if req.OutRefundNo != "" {
    77  		m1["out_refund_no"] = req.OutRefundNo
    78  	}
    79  	if req.RefundId != "" {
    80  		m1["refund_id"] = req.RefundId
    81  	}
    82  	if req.NonceStr != "" {
    83  		m1["nonce_str"] = req.NonceStr
    84  	} else {
    85  		m1["nonce_str"] = wechatutil.NonceStr()
    86  	}
    87  	if req.SignType != "" {
    88  		m1["sign_type"] = req.SignType
    89  	}
    90  
    91  	m2, err := RefundQuery(clt, m1)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	resp = &RefundQueryResponse{
    97  		TransactionId: m2["transaction_id"],
    98  		OutTradeNo:    m2["out_trade_no"],
    99  		FeeType:       m2["fee_type"],
   100  		CashFeeType:   m2["cash_fee_type"],
   101  	}
   102  
   103  	if str := m2["total_fee"]; str != "" {
   104  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   105  			err = fmt.Errorf("parse total_fee:%q to int64 failed: %s", str, err.Error())
   106  			return nil, err
   107  		} else {
   108  			resp.TotalFee = n
   109  		}
   110  	}
   111  	if str := m2["cash_fee"]; str != "" {
   112  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   113  			err = fmt.Errorf("parse cash_fee:%q to int64 failed: %s", str, err.Error())
   114  			return nil, err
   115  		} else {
   116  			resp.CashFee = n
   117  		}
   118  	}
   119  	if str := m2["refund_count"]; str != "" {
   120  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   121  			err = fmt.Errorf("parse refund_count:%q to int64 failed: %s", str, err.Error())
   122  			return nil, err
   123  		} else {
   124  			resp.RefundCount = int(n)
   125  		}
   126  	}
   127  	if str := m2["settlement_total_fee"]; str != "" {
   128  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   129  			err = fmt.Errorf("parse settlement_total_fee:%q to int64 failed: %s", str, err.Error())
   130  			return nil, err
   131  		} else {
   132  			resp.SettlementTotalFee = wechatutil.Int64(n)
   133  		}
   134  	}
   135  
   136  	resp.RefundList = make([]RefundItem, resp.RefundCount)
   137  	for i := 0; i < resp.RefundCount; i++ {
   138  		resp.RefundList[i].OutRefundNo = m2["out_refund_no_"+strconv.Itoa(i)]
   139  		resp.RefundList[i].RefundId = m2["refund_id_"+strconv.Itoa(i)]
   140  		resp.RefundList[i].RefundStatus = m2["refund_status_"+strconv.Itoa(i)]
   141  		resp.RefundList[i].RefundRecvAccout = m2["refund_recv_accout_"+strconv.Itoa(i)]
   142  		resp.RefundList[i].RefundChannel = m2["refund_channel_"+strconv.Itoa(i)]
   143  		resp.RefundList[i].RefundAccount = m2["refund_account_"+strconv.Itoa(i)]
   144  
   145  		if str := m2["refund_fee_"+strconv.Itoa(i)]; str != "" {
   146  			if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   147  				err = fmt.Errorf("parse refund_fee_%d:%q to int64 failed: %s", i, str, err.Error())
   148  				return nil, err
   149  			} else {
   150  				resp.RefundList[i].RefundFee = n
   151  			}
   152  		}
   153  		if str := m2["settlement_refund_fee_"+strconv.Itoa(i)]; str != "" {
   154  			if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   155  				err = fmt.Errorf("parse settlement_refund_fee_%d:%q to int64 failed: %s", i, str, err.Error())
   156  				return nil, err
   157  			} else {
   158  				resp.RefundList[i].SettlementRefundFee = wechatutil.Int64(n)
   159  			}
   160  		}
   161  		if str := m2["refund_success_time_"+strconv.Itoa(i)]; str != "" {
   162  			// 2016-07-25 15:26:26
   163  			if t, err := time.ParseInLocation("2006-01-02 15:04:05", str, wechatutil.BeijingLocation); err != nil {
   164  				err = fmt.Errorf("parse refund_success_time_%d:%q to time.Time failed: %s", i, str, err.Error())
   165  				return nil, err
   166  			} else {
   167  				resp.RefundList[i].RefundSuccessTime = t
   168  			}
   169  		}
   170  	}
   171  
   172  	// 校验返回参数
   173  	if req.TransactionId != "" && resp.TransactionId != "" && req.TransactionId != resp.TransactionId {
   174  		err = fmt.Errorf("transaction_id mismatch, have: %s, want: %s", resp.TransactionId, req.TransactionId)
   175  		return nil, err
   176  	}
   177  	if req.OutTradeNo != "" && resp.OutTradeNo != "" && req.OutTradeNo != resp.OutTradeNo {
   178  		err = fmt.Errorf("out_trade_no mismatch, have: %s, want: %s", resp.OutTradeNo, req.OutTradeNo)
   179  		return nil, err
   180  	}
   181  
   182  	return resp, nil
   183  }