github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mch/pay/orderquery.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  // OrderQuery 查询订单.
    13  func OrderQuery(clt *core.Client, req map[string]string) (resp map[string]string, err error) {
    14  	return clt.PostXML(core.APIBaseURL()+"/pay/orderquery", req)
    15  }
    16  
    17  type OrderQueryRequest struct {
    18  	XMLName struct{} `xml:"xml" json:"-"`
    19  
    20  	// 下面这些参数至少提供一个
    21  	TransactionId string `xml:"transaction_id"` // 微信的订单号,优先使用
    22  	OutTradeNo    string `xml:"out_trade_no"`   // 商户系统内部的订单号,当没提供transaction_id时需要传这个。
    23  
    24  	// 可选参数
    25  	NonceStr string `xml:"nonce_str"` // 随机字符串,不长于32位。NOTE: 如果为空则系统会自动生成一个随机字符串。
    26  	SignType string `xml:"sign_type"` // 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    27  }
    28  
    29  type OrderQueryResponse struct {
    30  	XMLName struct{} `xml:"xml" json:"-"`
    31  
    32  	// 必选返回
    33  	TradeState     string    `xml:"trade_state"`      // 交易状态
    34  	TradeStateDesc string    `xml:"trade_state_desc"` // 对当前查询订单状态的描述和下一步操作的指引
    35  	OpenId         string    `xml:"openid"`           // 用户在商户appid下的唯一标识
    36  	TransactionId  string    `xml:"transaction_id"`   // 微信支付订单号
    37  	OutTradeNo     string    `xml:"out_trade_no"`     // 商户系统的订单号,与请求一致。
    38  	TradeType      string    `xml:"trade_type"`       // 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP,MICROPAY,详细说明见参数规定
    39  	BankType       string    `xml:"bank_type"`        // 银行类型,采用字符串类型的银行标识
    40  	TotalFee       int64     `xml:"total_fee"`        // 订单总金额,单位为分
    41  	CashFee        int64     `xml:"cash_fee"`         // 现金支付金额订单现金支付金额,详见支付金额
    42  	TimeEnd        time.Time `xml:"time_end"`         // 订单支付时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    43  
    44  	// 下面字段都是可选返回的(详细见微信支付文档), 为空值表示没有返回, 程序逻辑里需要判断
    45  	DeviceInfo         string `xml:"device_info"`          // 微信支付分配的终端设备号
    46  	IsSubscribe        *bool  `xml:"is_subscribe"`         // 用户是否关注公众账号
    47  	SubOpenId          string `xml:"sub_openid"`           // 用户在子商户appid下的唯一标识
    48  	SubIsSubscribe     *bool  `xml:"sub_is_subscribe"`     // 用户是否关注子公众账号
    49  	SettlementTotalFee *int64 `xml:"settlement_total_fee"` // 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    50  	FeeType            string `xml:"fee_type"`             // 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    51  	CashFeeType        string `xml:"cash_fee_type"`        // 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    52  	Detail             string `xml:"detail"`               // 商品详情
    53  	Attach             string `xml:"attach"`               // 附加数据,原样返回
    54  }
    55  
    56  // OrderQuery2 查询订单.
    57  //
    58  //	NOTE: 该函数不支持 代金券 功能, 如果有 代金券 功能请使用 OrderQuery 函数.
    59  func OrderQuery2(clt *core.Client, req *OrderQueryRequest) (resp *OrderQueryResponse, err error) {
    60  	m1 := make(map[string]string, 8)
    61  	if req.TransactionId != "" {
    62  		m1["transaction_id"] = req.TransactionId
    63  	}
    64  	if req.OutTradeNo != "" {
    65  		m1["out_trade_no"] = req.OutTradeNo
    66  	}
    67  	if req.NonceStr != "" {
    68  		m1["nonce_str"] = req.NonceStr
    69  	} else {
    70  		m1["nonce_str"] = wechatutil.NonceStr()
    71  	}
    72  	if req.SignType != "" {
    73  		m1["sign_type"] = req.SignType
    74  	}
    75  
    76  	m2, err := OrderQuery(clt, m1)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	// 判断 trade_state
    82  	tradeState := m2["trade_state"]
    83  	if tradeState != "SUCCESS" {
    84  		resp = &OrderQueryResponse{
    85  			TradeState:     tradeState,
    86  			TradeStateDesc: m2["trade_state_desc"],
    87  			OutTradeNo:     m2["out_trade_no"],
    88  			Attach:         m2["attach"],
    89  		}
    90  		return resp, nil
    91  	}
    92  
    93  	resp = &OrderQueryResponse{
    94  		TradeState:     tradeState,
    95  		TradeStateDesc: m2["trade_state_desc"],
    96  		OpenId:         m2["openid"],
    97  		TransactionId:  m2["transaction_id"],
    98  		OutTradeNo:     m2["out_trade_no"],
    99  		TradeType:      m2["trade_type"],
   100  		BankType:       m2["bank_type"],
   101  		DeviceInfo:     m2["device_info"],
   102  		SubOpenId:      m2["sub_openid"],
   103  		FeeType:        m2["fee_type"],
   104  		CashFeeType:    m2["cash_fee_type"],
   105  		Detail:         m2["detail"],
   106  		Attach:         m2["attach"],
   107  	}
   108  
   109  	// 校验返回参数
   110  	if req.TransactionId != "" && resp.TransactionId != "" && req.TransactionId != resp.TransactionId {
   111  		err = fmt.Errorf("transaction_id mismatch, have: %s, want: %s", resp.TransactionId, req.TransactionId)
   112  		return nil, err
   113  	}
   114  	if req.OutTradeNo != "" && resp.OutTradeNo != "" && req.OutTradeNo != resp.OutTradeNo {
   115  		err = fmt.Errorf("out_trade_no mismatch, have: %s, want: %s", resp.OutTradeNo, req.OutTradeNo)
   116  		return nil, err
   117  	}
   118  
   119  	if str := m2["total_fee"]; str != "" {
   120  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   121  			err = fmt.Errorf("parse total_fee:%q to int64 failed: %s", str, err.Error())
   122  			return nil, err
   123  		} else {
   124  			resp.TotalFee = n
   125  		}
   126  	}
   127  	if str := m2["cash_fee"]; str != "" {
   128  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   129  			err = fmt.Errorf("parse cash_fee:%q to int64 failed: %s", str, err.Error())
   130  			return nil, err
   131  		} else {
   132  			resp.CashFee = n
   133  		}
   134  	}
   135  	if str := m2["time_end"]; str != "" {
   136  		if t, err := core.ParseTime(str); err != nil {
   137  			err = fmt.Errorf("parse time_end:%q to time.Time failed: %s", str, err.Error())
   138  			return nil, err
   139  		} else {
   140  			resp.TimeEnd = t
   141  		}
   142  	}
   143  
   144  	if str := m2["is_subscribe"]; str != "" {
   145  		if str == "Y" || str == "y" {
   146  			resp.IsSubscribe = wechatutil.Bool(true)
   147  		} else {
   148  			resp.IsSubscribe = wechatutil.Bool(false)
   149  		}
   150  	}
   151  	if str := m2["sub_is_subscribe"]; str != "" {
   152  		if str == "Y" || str == "y" {
   153  			resp.SubIsSubscribe = wechatutil.Bool(true)
   154  		} else {
   155  			resp.SubIsSubscribe = wechatutil.Bool(false)
   156  		}
   157  	}
   158  	if str := m2["settlement_total_fee"]; str != "" {
   159  		if n, err := strconv.ParseInt(str, 10, 64); err != nil {
   160  			err = fmt.Errorf("parse settlement_total_fee:%q to int64 failed: %s", str, err.Error())
   161  			return nil, err
   162  		} else {
   163  			resp.SettlementTotalFee = wechatutil.Int64(n)
   164  		}
   165  	}
   166  	return resp, nil
   167  }