golang实现微信小程序支付服务端

小程序支付的交互图如下:

小程序支付时序图

商户系统和微信支付系统主要交互:
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】
2、商户server调用支付统一下单,api参见公共api【统一下单API】
3、商户server调用再次签名,api参见公共api【再次签名】
4、商户server接收支付通知,api参见公共api【支付结果通知API】
5、商户server查询支付结果,api参见公共api【查询订单API】

以下是统一下单API

//响应信息
type WXPayResp 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 (index *IndexController) WxPay(){
    info := make(map[string]interface{}, 0)

    fmt.Println("访问ip",index.Request.RemoteAddr)
    ip := utils.Substr(index.Request.RemoteAddr, 0,strings.Index(index.Request.RemoteAddr,":"))

    total_fee,_ := strconv.ParseFloat(index.GetString("total_fee"),64)  //单位 分
    openId := index.GetString("openId");        //"oKYr_0GkE-Izt9N9Wn43sapI9Pqw"
    body := "费用说明";
    //订单号
    orderNo := index.GetString("orderNo"); //"wx"+utils.ToStr(time.Now().Unix()) + string(utils.Krand(4,0))
    //随机数
    nonceStr := time.Now().Format("20060102150405") + string(utils.Krand(4, 0))
    var reqMap = make(map[string]interface{}, 0)
    reqMap["appid"] = utils.Wx_Appid//微信小程序appid
    reqMap["body"] = body           //商品描述
    reqMap["mch_id"] = utils.Wx_Mchid   //商户号
    reqMap["nonce_str"] = nonceStr      //随机数
    reqMap["notify_url"] = "http://test.com.cn/weixinNotice.jspx"   //通知地址
    reqMap["openid"] = openId       //商户唯一标识 openid
    reqMap["out_trade_no"] = orderNo    //订单号
    reqMap["spbill_create_ip"] = ip     //用户端ip //订单生成的机器 IP
    reqMap["total_fee"] = total_fee * 100   //订单总金额,单位为分
    reqMap["trade_type"] = "JSAPI"      //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
    reqMap["sign"] = WxPayCalcSign(reqMap,utils.WX_KEY)

    reqStr := Map2Xml(reqMap)
    fmt.Println("请求xml",reqStr)

    client := &http.Client{}

    // 调用支付统一下单API
    req,err := http.NewRequest("POST","https://api.mch.weixin.qq.com/pay/unifiedorder",strings.NewReader(reqStr))
    if err != nil {
        // handle 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 {
        // handle error
        fmt.Println("解析响应内容失败",err)
        return
    }
    fmt.Println("响应数据",string(body2))

    var resp1 WXPayResp
    err = xml.Unmarshal(body2,&resp1)
    if err != nil {
        panic(err)
        return
    }

    // 返回预付单信息
    if strings.ToUpper(resp1.Return_code) == "SUCCESS"{
        fmt.Println("预支付申请成功")
        // 再次签名
        var resMap = make(map[string]interface{}, 0)
        resMap["appId"] = utils.Wx_Appid
        resMap["nonceStr"] = resp1.Nonce_str            //商品描述
        resMap["package"] = "prepay_id=" + resp1.Prepay_id  //商户号
        resMap["signType"] = "MD5"              //签名类型
        resMap["timeStamp"] = utils.ToStr(time.Now().Unix())    //当前时间戳

        resMap["paySign"] = WxPayCalcSign(resMap,utils.WX_KEY)
        // 返回5个支付参数及sign 用户进行确认支付

        fmt.Println("支付参数",resMap)
        index.Console(resMap)
    }else{
        info["msg"] = "微信请求支付失败"
        index.Console(info)
    }
}
//微信支付计算签名的函数
func WxPayCalcSign(mReq map[string]interface{},key string) (sign string) {
    //STEP 1,对key进行升序排序.
    sorted_keys := make([]string,0)
    for k,_ := range mReq { sorted_keys = append(sorted_keys,k) }
    sort.Strings(sorted_keys)

    //STEP2,对key=value的键值对用&连接起来,略过空值
    var signStrings string
    for _,k := range sorted_keys {
        logger.Printf("k=%v,v=%v\n",k,mReq[k])
        value := fmt.Sprintf("%v",mReq[k])
        if value != "" {
            signStrings = signStrings + k + "=" + value + "&"
        }
    }

    //STEP3,在键值对的最后加上key=API_KEY
    if key != "" {
        signStrings = signStrings + "key=" + key
    }

    fmt.Println("加密前-----",signStrings)
    //STEP4,进行MD5签名并且将所有字符转为大写.
    md5Ctx := md5.New()
    md5Ctx.Write([]byte(signStrings)) //
    cipherStr := md5Ctx.Sum(nil)
    upperSign := strings.ToUpper(hex.EncodeToString(cipherStr))

    fmt.Println("加密后-----",upperSign)
    return upperSign
}
//微信支付计算签名的函数
func Map2Xml(mReq map[string]interface{}) (xml string) {
    sb := bytes.Buffer{}
    sb.WriteString("<xml>")
    for k,v := range mReq{
        sb.WriteString("<"+k+">"+utils.ToStr(v)+"</"+k+">")
    }
    sb.WriteString("</xml>")
    return sb.String()
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


类型转换 1、int转string 2、string转int 3、string转float 4、用户结构类型转换
package main import s &quot;strings&quot; import &quot;fmt&quot; var p = fmt.Println func main() { p(&quot;Contains: &quot;, s.Contains(&quot;test&quo
类使用:实现一个people中有一个sayhi的方法调用功能,代码如下: 接口使用:实现上面功能,代码如下:
html代码: beego代码:
1、读取文件信息: 2、读取文件夹下的所有文件: 3、写入文件信息 4、删除文件,成功返回true,失败返回false
配置环境:Windows7+推荐IDE:LiteIDEGO下载地址:http://www.golangtc.com/downloadBeego开发文档地址:http://beego.me/docs/intro/ 安装步骤: 一、GO环境安装 二、配置系统变量 三、Beego安装 一、GO环境安装 根
golang获取程序运行路径:
Golang的文档和社区资源:为什么它可以帮助开发人员快速上手?
Golang:AI 开发者的实用工具
Golang的标准库:为什么它可以大幅度提高开发效率?
Golang的部署和运维:如何将应用程序部署到生产环境中?
高性能AI开发:Golang的优势所在
本篇文章和大家了解一下go语言开发优雅得关闭协程的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1.简介本文将介绍首先为什么需要主...
这篇文章主要介绍了Go关闭goroutine协程的方法,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。1.简介本文将介绍首先为什么需要主动关闭gor...
本篇文章和大家了解一下go关闭GracefulShutdown服务的几种方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。目录Shutdown方法Regi...
这篇文章主要介绍了Go语言如何实现LRU算法的核心思想和实现过程,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。GO实现Redis的LRU例子常
今天小编给大家分享的是Go简单实现多租户数据库隔离的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会...
这篇“Linux系统中怎么安装NSQ的Go语言客户端”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希
本文小编为大家详细介绍“怎么在Go语言中实现锁机制”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么在Go语言中实现锁机制”文章能帮助大家解决疑惑,下面...
今天小编给大家分享一下Go语言中interface类型怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考