package wxpayapi import ( "encoding/base64" "encoding/xml" "fmt" "io/ioutil" "net/http" "strings" "git.rosy.net.cn/baseapi/utils" "github.com/clbanning/mxj" "github.com/nanjishidu/gomini/gocrypto" ) type CData string func (c CData) MarshalXML(e *xml.Encoder, start xml.StartElement) error { return e.EncodeElement(struct { string `xml:",cdata"` }{string(c)}, start) } const ( MsgTypeUnkown = 0 // 未知 MsgTypePay = 1 // 支付结果回调 MsgTypeRefund = 2 // 退款结果回调 ) type CallbackResponse struct { XMLName xml.Name `json:"-" xml:"xml"` ReturnCode string `json:"return_code" xml:"return_code"` ReturnMsg CData `json:"return_msg" xml:"return_msg"` } type BaseResultMsg struct { ReturnCode string `json:"return_code" xml:"return_code"` ReturnMsg string `json:"return_msg" xml:"return_msg"` AppID string `json:"appid" xml:"appid"` DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"` MchID string `json:"mch_id" xml:"mch_id"` NonceStr string `json:"nonce_str" xml:"nonce_str"` Sign string `json:"sign" xml:"sign"` ResultCode string `json:"result_code" xml:"result_code"` ResultMsg string `json:"result_msg" xml:"result_msg"` ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"` ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"` } type PayResultMsg struct { ReturnCode string `json:"return_code" xml:"return_code"` ReturnMsg string `json:"return_msg" xml:"return_msg"` AppID string `json:"appid" xml:"appid"` DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"` MchID string `json:"mch_id" xml:"mch_id"` NonceStr string `json:"nonce_str" xml:"nonce_str"` Sign string `json:"sign" xml:"sign"` ResultCode string `json:"result_code" xml:"result_code"` ResultMsg string `json:"result_msg" xml:"result_msg"` ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"` ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"` BankType string `json:"bank_type"` CashFee string `json:"cash_fee"` FeeType string `json:"fee_type"` IsSubscribe string `json:"is_subscribe"` OpenID string `json:"openid"` OutTradeNo string `json:"out_trade_no"` TimeEnd string `json:"time_end"` TotalFee string `json:"total_fee"` TradeType string `json:"trade_type"` TransactionID string `json:"transaction_id"` } type RefundReqInfo struct { OutRefundNo string `json:"out_refund_no"` OutTradeNo string `json:"out_trade_no"` RefundAccount string `json:"refund_account"` RefundFee string `json:"refund_fee" xml:"refund_fee"` RefundID string `json:"refund_id"` RefundRecvAccout string `json:"refund_recv_accout"` RefundRequestSource string `json:"refund_request_source"` RefundStatus string `json:"refund_status"` SettlementRefundFee string `json:"settlement_refund_fee"` SettlementTotalFee string `json:"settlement_total_fee"` SuccessTime string `json:"success_time"` TotalFee string `json:"total_fee"` TransactionID string `json:"transaction_id"` } type RefundResultMsg struct { ReturnCode string `json:"return_code" xml:"return_code"` ReturnMsg string `json:"return_msg" xml:"return_msg"` AppID string `json:"appid" xml:"appid"` DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"` MchID string `json:"mch_id" xml:"mch_id"` NonceStr string `json:"nonce_str" xml:"nonce_str"` Sign string `json:"sign" xml:"sign"` ResultCode string `json:"result_code" xml:"result_code"` ResultMsg string `json:"result_msg" xml:"result_msg"` ErrCode string `json:"err_code,omitempty" xml:"err_code,omitempty"` ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"` ReqInfoStr string `json:"req_info,omitempty"` ReqInfoObj *RefundReqInfo `json:"req_info_obj,omitempty"` } type CallbackMsg struct { MsgType int MapData map[string]interface{} Data interface{} } var ( SuccessResponse = &CallbackResponse{ ReturnCode: ResponseCodeSuccess, ReturnMsg: "OK", } ) func Err2CallbackResponse(err error, data string) *CallbackResponse { if err == nil { return SuccessResponse } returnMsg := err.Error() if data != "" { returnMsg = fmt.Sprintf("%s,%s", returnMsg, data) } return &CallbackResponse{ ReturnCode: ResponseCodeFail, ReturnMsg: CData(returnMsg), } } func (a *API) decodeReqInfo(msg string) (decryptedMsg string, err error) { binMsg, err := base64.StdEncoding.DecodeString(msg) if err == nil { // aesKey := []byte(fmt.Sprintf("%x", md5.Sum([]byte(a.appKey)))) // binResult, err2 := utils.AESCBCDecpryt(binMsg, aesKey, aesKey[:16]) gocrypto.SetAesKey(strings.ToLower(gocrypto.Md5(a.appKey))) binResult, err2 := gocrypto.AesECBDecrypt(binMsg) if err = err2; err == nil { decryptedMsg = string(binResult) } } return decryptedMsg, err } func (a *API) GetCallbackMsg(request *http.Request) (msg *CallbackMsg, callbackResponse *CallbackResponse) { data, err := ioutil.ReadAll(request.Body) if err != nil { return nil, Err2CallbackResponse(err, "") } return a.getCallbackMsg(string(data)) } func (a *API) getCallbackMsg(msgBody string) (msg *CallbackMsg, callbackResponse *CallbackResponse) { mapData, _, err := a.parseXmlStrAsMap(msgBody) if err != nil { return nil, Err2CallbackResponse(err, "") } returnCode := utils.Interface2String(mapData["return_code"]) if returnCode != ResponseCodeSuccess { // 如果return_code出错,直接返回 return nil, SuccessResponse } reqInfo := utils.Interface2String(mapData["req_info"]) transactionID := utils.Interface2String(mapData["transaction_id"]) if reqInfo == "" && transactionID != "" { // 对于支付结果通知进行签名验证(退款结果通知不支持验证) sigType := utils.Interface2String(mapData[sigTypeKey]) sign := utils.Interface2String(mapData[sigKey]) desiredSign := a.signParam(sigType, mapData) if desiredSign != sign { return nil, Err2CallbackResponse(fmt.Errorf("desiredSign:%s <> sign:%s", desiredSign, sign), "") } } msg = &CallbackMsg{ MapData: mapData, } if reqInfo != "" { msg.MsgType = MsgTypeRefund var refundResult *RefundResultMsg if err = utils.Map2StructByJson(mapData, &refundResult, false); err == nil { if reqInfo, err = a.decodeReqInfo(reqInfo); err == nil { mv, err2 := mxj.NewMapXml([]byte(reqInfo)) if err = err2; err == nil { reqInfoMap := mv["root"].(map[string]interface{}) if err = utils.Map2StructByJson(reqInfoMap, &refundResult.ReqInfoObj, false); err == nil { msg.Data = refundResult } } } } } else if transactionID != "" { msg.MsgType = MsgTypePay var payResult *PayResultMsg if err = utils.Map2StructByJson(mapData, &payResult, false); err == nil { msg.Data = payResult } } if err != nil { callbackResponse = Err2CallbackResponse(err, "") } return msg, callbackResponse }