当前位置: 首页 > 工具软件 > go-wxpay > 使用案例 >

go实现微信支付

欧阳何平
2023-12-01

package api

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "net/http"
    "strconv"
    "strings"
    "time"

    "zhoujinhui/model"

    "zhoujinhui/util"

    "github.com/globalsign/mgo/bson"
)

const APPID, SECRET = "wxxxxxxxxxx86", "06xxxxxxxxxxx08"
const WX_LOGIN_API = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code&appid=" + APPID + "&secret=" + SECRET

const MCH_ID, MCH_KEY, TRADE_TYPE = "1xxxxxx2", "34xxxxxxxxx87", "JSAPI"
const NOTIFY_URL, WX_PAY_API = "https://admin.qhx-xhx.com/api/notifyRrl", "https://api.mch.weixin.qq.com/pay/unifiedorder"

func GetOpenId(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()

    resp, err := http.Get(WX_LOGIN_API + "&js_code=" + r.Form.Get("res_code"))

    defer resp.Body.Close()
    if err == nil {
        body, _ := ioutil.ReadAll(resp.Body)

        var respStr = string(body)

        var jm map[string]string
        json.Unmarshal([]byte(respStr), &jm)

        if jm["openid"] != "" {
            if model.IsExistsWxUserByOpenId(jm["openid"]) {
                model.EditWxUser(jm["openid"])
            } else {
                model.InsertWxUser(jm["openid"])
            }
        }

        fmt.Fprint(w, respStr)
    }
}

func UpdateUserInfo(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    openId := r.Form.Get("open_id")

    if openId != "" {
        var wxUser model.WU
        json.Unmarshal([]byte(r.Form.Get("user_info")), &wxUser)

        model.UpdateWxUserInfo(openId, wxUser)
    }

}

// fmt.Fprint(w, wxPay(wxPayRequest{"JSAPI pay test", util.Krand(16, 3),
//             openId, outTradeNo, controller.GetRemoteIp(r), 1}))

//支付输入信息
type wxPayRequest struct {
    Body             string //商品描述
    Nonce_str        string //随机数
    Openid           string //商户唯一标识 openid
    Out_trade_no     string //订单号
    Spbill_create_ip string //用户端ip   //订单生成的机器 IP
    Total_fee        int    //订单总金额,单位为分
}

//支付反馈信息
type wxPayResponse struct {
    Return_code string `xml:"return_code"`
    Return_msg  string `xml:"return_msg"`
    Nonce_str   string `xml:"nonce_str"`
    Prepay_id   string `xml:"prepay_id"`
}

//支付
func wxPay(payRequest wxPayRequest) string {

    reqStr := getReqStr(payRequest) //通过签名算法计算得出的xml

    client := &http.Client{}

    // 调用支付统一下单API
    req, err := http.NewRequest("POST", WX_PAY_API, strings.NewReader(reqStr))
    if err != nil {
        fmt.Println("访问预支付交易地址:https://api.mch.weixin.qq.com/pay/unifiedorder 失败!")
        return "{\"msg\":\"err\",\"info\":\"访问预支付交易地址失败:" + err.Error() + "\"}"
    }
    req.Header.Set("Content-Type", "text/xml;charset=utf-8")

    resp, err := client.Do(req)
    defer resp.Body.Close()

    body2, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("解析响应内容失败:", err)
        return "{\"msg\":\"err\",\"info\":\"解析响应内容失败:" + err.Error() + "\"}"
    }

    var payResponse wxPayResponse
    err = xml.Unmarshal(body2, &payResponse)
    if err != nil {
        panic(err)
        return "{\"msg\":\"err\",\"info\":\"" + err.Error() + "\"}"
    }

    // 返回预付单信息
    if strings.ToUpper(payResponse.Return_code) == "SUCCESS" {
        // 再次签名
        return getRepStr(payResponse)
    } else {
        return "{\"msg\":\"err\",\"info\":\"" + payResponse.Return_msg + "\"}"
    }
}

//通过签名算法计算得出的xml
func getReqStr(payRequest wxPayRequest) string {
    feeStr := strconv.Itoa(payRequest.Total_fee)

    stringSign := "appid=" + APPID +
        "&body=" + payRequest.Body +
        "&mch_id=" + MCH_ID +
        "&nonce_str=" + payRequest.Nonce_str +
        "&notify_url=" + NOTIFY_URL +
        "&openid=" + payRequest.Openid +
        "&out_trade_no=" + payRequest.Out_trade_no +
        "&spbill_create_ip=" + payRequest.Spbill_create_ip +
        "&total_fee=" + feeStr +
        "&trade_type=" + TRADE_TYPE +
        "&key=" + MCH_KEY

    //进行MD5签名并且将所有字符转为大写.
    sign := strings.ToUpper(util.GetMd5String(stringSign))

    reqStr := "<xml>" +
        "<appid>" + APPID + "</appid>" +
        "<body>" + payRequest.Body + "</body>" +
        "<mch_id>" + MCH_ID + "</mch_id>" +
        "<nonce_str>" + payRequest.Nonce_str + "</nonce_str>" +
        "<notify_url>" + NOTIFY_URL + "</notify_url>" +
        "<openid>" + payRequest.Openid + "</openid>" +
        "<out_trade_no>" + payRequest.Out_trade_no + "</out_trade_no>" +
        "<spbill_create_ip>" + payRequest.Spbill_create_ip + "</spbill_create_ip>" +
        "<total_fee>" + feeStr + "</total_fee>" +
        "<trade_type>" + TRADE_TYPE + "</trade_type>" +
        "<sign>" + sign + "</sign></xml>"

    return reqStr
}

//再次签名算法计算得出的json
func getRepStr(payResponse wxPayResponse) string {
    //当前时间戳
    timeStamp := strconv.FormatInt(time.Now().Unix(), 10)

    stringSign := "appId=" + APPID +
        "&nonceStr=" + payResponse.Nonce_str +
        "&package=prepay_id=" + payResponse.Prepay_id +
        "&signType=MD5&timeStamp=" + timeStamp +
        "&key=" + MCH_KEY

    // 进行MD5签名并且将所有字符转为大写.
    paySign := strings.ToUpper(util.GetMd5String(stringSign))

    repStr := "{\"msg\":\"ok\",\"timeStamp\":\"" + timeStamp +
        "\",\"nonceStr\":\"" + payResponse.Nonce_str +
        "\",\"package\":\"prepay_id=" + payResponse.Prepay_id +
        "\",\"signType\":\"MD5\",\"paySign\":\"" + paySign + "\"}"

    return repStr
}

//商户处理后同步返回给微信参数
func NotifyRrl(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        // fmt.Println("读取http body失败,原因!", err)
        http.Error(w.(http.ResponseWriter), http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
        return
    }
    defer r.Body.Close()

    type payNotifyReq struct {
        Appid          string  `xml:"appid"`
        Bank_type      string  `xml:"bank_type"`
        Cash_fee       float64 `xml:"cash_fee"`
        Fee_type       string  `xml:"fee_type"`
        Is_subscribe   string  `xml:"is_subscribe"`
        Mch_id         string  `xml:"mch_id"`
        Nonce_str      string  `xml:"nonce_str"`
        Openid         string  `xml:"openid"`
        Out_trade_no   string  `xml:"out_trade_no"`
        Result_code    string  `xml:"result_code"`
        Return_code    string  `xml:"return_code"`
        Sign           string  `xml:"sign"`
        Time_end       string  `xml:"time_end"`
        Total_fee      float64 `xml:"total_fee"`
        Trade_type     string  `xml:"trade_type"`
        Transaction_id string  `xml:"transaction_id"`
    }

    var mr payNotifyReq
    err = xml.Unmarshal(body, &mr)
    if err != nil {
        // fmt.Println("解析HTTP Body格式到xml失败,原因!", err)
        http.Error(w.(http.ResponseWriter), http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
        return
    }

    if orderIdStr := mr.Out_trade_no; orderIdStr != "" && bson.IsObjectIdHex(orderIdStr) {
        // model.UpdateOrderSta(bson.ObjectIdHex(orderIdStr), util.ORDER_PAYED)
        fmt.Fprint(w, "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>")
    } else {
        fmt.Fprint(w, "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[订单号格式校验错误]]></return_msg></xml>")
    }

}

 类似资料: