package wechat import ( "encoding/xml" "errors" "fmt" "log" "reflect" "strings" "git.tetele.net/tgo/helper" ) // 请求支付 type PayXml struct { XMLName xml.Name `xml:"xml"` Appid string `xml:"appid"` MchId string `xml:"mch_id"` DeviceInfo string `xml:"device_info"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` Body string `xml:"body"` OutTradeNo string `xml:"out_trade_no"` TotalFee int `xml:"total_fee"` SpbillCreateIp string `xml:"spbill_create_ip"` AuthCode string `xml:"auth_code"` } // 支付结果 type PayReturnXml struct { ReturnCode string `xml:"return_code"` ReturnMsg string `xml:"return_msg"` Appid string `xml:"appid"` MchId string `xml:"mch_id"` DeviceInfo string `xml:"device_info"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` ResultCode string `xml:"result_code"` ErrCode string `xml:"err_code"` ErrCodeDes string `xml:"err_code_des"` Openid string `xml:"openid"` IsSubscribe string `xml:"is_subscribe"` TradeType string `xml:"trade_type"` BankType string `xml:"bank_type"` FeeType string `xml:"fee_type"` TotalFee string `xml:"total_fee"` SettlementTotalFee string `xml:"settlement_total_fee"` CouponFee string `xml:"coupon_fee"` CashFeeType string `xml:"cash_fee_type"` CashFee string `xml:"cash_fee"` TransactionId string `xml:"transaction_id"` OutTradeNo string `xml:"out_trade_no"` Attach string `xml:"attach"` TimeEnd string `xml:"time_end"` PromotionDetail string `xml:"promotion_detail"` } // 查询支付 type QueryXml struct { XMLName xml.Name `xml:"xml"` Appid string `xml:"appid"` MchId string `xml:"mch_id"` OutTradeNo string `xml:"out_trade_no"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` } // 查询结果 type QueryReturnXml struct { ReturnCode string `xml:"return_code"` ReturnMsg string `xml:"return_msg"` Appid string `xml:"appid"` MchId string `xml:"mch_id"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` ResultCode string `xml:"result_code"` ErrCode string `xml:"err_code"` ErrCodeDes string `xml:"err_code_des"` DeviceInfo string `xml:"device_info"` Openid string `xml:"openid"` IsSubscribe string `xml:"is_subscribe"` TradeType string `xml:"trade_type"` TradeState string `xml:"trade_state"` BankType string `xml:"bank_type"` TotalFee string `xml:"total_fee"` SettlementTotalFee string `xml:"settlement_total_fee"` FeeType string `xml:"fee_type"` CashFee string `xml:"cash_fee"` CashFeeType string `xml:"cash_fee_type"` CouponFee string `xml:"coupon_fee"` CouponCount string `xml:"coupon_count"` TransactionId string `xml:"transaction_id"` OutTradeNo string `xml:"out_trade_no"` Attach string `xml:"attach"` TimeEnd string `xml:"time_end"` TradeStateDesc string `xml:"trade_state_desc"` } // 撤消 type ReverseXml struct { XMLName xml.Name `xml:"xml"` Appid string `xml:"appid"` MchId string `xml:"mch_id"` OutTradeNo string `xml:"out_trade_no"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` } // 撤消结果 type ReverseReturnXml struct { ReturnCode string `xml:"return_code"` ReturnMsg string `xml:"return_msg"` Appid string `xml:"appid"` MchId string `xml:"mch_id"` NonceStr string `xml:"nonce_str"` Sign string `xml:"sign"` ResultCode string `xml:"result_code"` ErrCode string `xml:"err_code"` ErrCodeDes string `xml:"err_code_des"` Recall string `xml:"recall"` } /** * 微信二维码支付 * 2022/02/07 * @param device_info 设备号 * @param body 商品描述 * @param out_trade_no 商户订单号 * @param total_fee 订单金额 * @param auth_code 付款码 */ func MicroPay(appid, mchid, mch_key, device_info, body, out_trade_no, auth_code, total_fee string) (PayReturnXml, error) { log.Println("wechat micropay") var requestxml PayXml var retXml PayReturnXml requestxml.Appid = appid requestxml.MchId = mchid requestxml.DeviceInfo = device_info requestxml.NonceStr = helper.GetRandomString(30) requestxml.Body = body requestxml.OutTradeNo = out_trade_no requestxml.TotalFee = helper.ToInt(helper.FloatMul(total_fee, 100)) requestxml.SpbillCreateIp = GetLocalIp() requestxml.AuthCode = auth_code signStr := helper.StringJoin("appid=", requestxml.Appid, "&auth_code=", requestxml.AuthCode, "&body=", requestxml.Body, "&device_info=", requestxml.DeviceInfo, "&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr, "&out_trade_no=", requestxml.OutTradeNo, "&spbill_create_ip=", requestxml.SpbillCreateIp, "&total_fee=", helper.ToString(requestxml.TotalFee), "&key=", mch_key) signMd5 := Md5Str(signStr) requestxml.Sign = strings.ToUpper(signMd5) reqXmlJson, err := xml.Marshal(requestxml) log.Println(requestxml, string(reqXmlJson), err) url := "https://api.mch.weixin.qq.com/pay/micropay" ret, err := SendXml("POST", url, requestxml) log.Println("wechat micropay pay result:", string(ret), err) if err != nil { log.Println("Err:", err) return retXml, errors.New("微信支付失败,请检查网络") } err = xml.Unmarshal(ret, &retXml) if err != nil { log.Println("xml Unmarshal Err:", retXml, err) return retXml, errors.New("未知的支付结果") } if retXml.ResultCode == "SUCCESS" { retMap := XmlStructToMap(retXml) //验证sign signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key sign := strings.ToUpper(Md5Str(signStr)) if sign != retXml.Sign { log.Println("Err:签名错误", string(ret), sign) return retXml, errors.New("支付结果签名错误") } } else { return retXml, errors.New(retXml.ReturnMsg) } return retXml, nil } func MicroPayQuery(appid, mchid, out_trade_no, mch_key string) (QueryReturnXml, error) { log.Println("wechat micropay query", out_trade_no) var requestxml QueryXml var retXml QueryReturnXml requestxml.Appid = appid requestxml.MchId = mchid requestxml.NonceStr = helper.GetRandomString(30) requestxml.OutTradeNo = out_trade_no signStr := helper.StringJoin("appid=", requestxml.Appid, "&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr, "&out_trade_no=", requestxml.OutTradeNo, "&key=", mch_key) signMd5 := Md5Str(signStr) requestxml.Sign = strings.ToUpper(signMd5) url := "https://api.mch.weixin.qq.com/pay/orderquery" ret, err := SendXml("POST", url, requestxml) if err != nil { log.Println("Err:", err) return retXml, errors.New("微信支付失败,请检查网络") } err = xml.Unmarshal(ret, &retXml) if err != nil { log.Println("xml Unmarshal Err:", retXml, err) return retXml, errors.New("未知的支付结果") } if retXml.ReturnCode == "SUCCESS" { retMap := XmlStructToMap(retXml) //验证sign signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key sign := strings.ToUpper(Md5Str(signStr)) if sign != retXml.Sign { log.Println("Err:签名错误", string(ret), sign) return retXml, errors.New("查询支付结果签名错误") } } else { return retXml, errors.New(retXml.ReturnMsg) } return retXml, nil } /** * 撤消订单 * 2023/06/13 */ func MicroPayReverse(appid, mchid, out_trade_no, mch_key string) (ReverseReturnXml, error) { log.Println("wechat micropay reverse", out_trade_no) var requestxml ReverseXml var retXml ReverseReturnXml requestxml.Appid = appid requestxml.MchId = mchid requestxml.NonceStr = helper.GetRandomString(30) requestxml.OutTradeNo = out_trade_no signStr := helper.StringJoin("appid=", requestxml.Appid, "&mch_id=", requestxml.MchId, "&nonce_str=", requestxml.NonceStr, "&out_trade_no=", requestxml.OutTradeNo, "&key=", mch_key) signMd5 := Md5Str(signStr) requestxml.Sign = strings.ToUpper(signMd5) url := "https://api.mch.weixin.qq.com/secapi/pay/reverse" ret, err := SendXml("POST", url, requestxml) if err != nil { log.Println("Err:", err) return retXml, errors.New("撤消微信支付失败,请检查网络") } err = xml.Unmarshal(ret, &retXml) if err != nil { log.Println("xml Unmarshal Err:", retXml, err) return retXml, errors.New("未知的撤消结果") } if retXml.ReturnCode == "SUCCESS" { retMap := XmlStructToMap(retXml) //验证sign signStr = helper.HttpBuildQuery(retMap) + "&key=" + mch_key sign := strings.ToUpper(Md5Str(signStr)) if sign != retXml.Sign { log.Println("Err:签名错误", string(ret), sign) return retXml, errors.New("查询支付结果签名错误") } } else { return retXml, errors.New(retXml.ReturnMsg) } return retXml, nil } func XmlStructToMap(retXml interface{}) map[string]string { t := reflect.TypeOf(retXml) v := reflect.ValueOf(retXml) //转为map,验签 retMap := make(map[string]string) var retValue string var tag string for k := 0; k < t.NumField(); k++ { if t.Field(k).Name != "Sign" { retValue = helper.ToString(v.Field(k).Interface()) tag = fmt.Sprintf("%s", t.Field(k).Tag) tag = strings.ReplaceAll(tag, "xml:", "") tag = strings.ReplaceAll(tag, "\"", "") retMap[tag] = retValue } } return retMap }