微信相关接口
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
9.4 KiB

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
}