6.微信支付之JSAPI支付

JSAPI支付

JSAPI支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付

使用场景

  • 用户在微信公众账号(必须是服务号)内进入商家公众号,打开某个H5页面,完成支付

  • 用户的好友在朋友圈、聊天窗口等分享商家H5页面连接,用户点击链接打开商家H5页面,完成支付

  • 将商户H5页面转换成二维码,用户扫描二维码后在微信浏览器中打开h5页面后完成支付

JSAPI接口

  • 使用JS脚本:getBrandWCPayRequest 调起微信支付

    WeixinJSBridge.invoke(
        'getBrandWCPayRequest', {
            "appId": "{{.AppId}}",   //公众号名称,由商户传入
            "timeStamp": "{{.TimeStamp}}",//时间戳,自1970年以来的秒数
            "nonceStr": "{{.NonceStr}}",//随机串
            "package": "{{.Package}}",
            "signType": "{{.SignType}}",//微信签名方式:
            "paySign": "{{.PaySign}}"   //微信签名
        },
        function (res) {
            if (res.err_msg == "get_brand_wcpay_request:ok") {
                alert("支付成功");
    
            }else if (res.err_msg == "get_brand_wcpay_request:cancel")  {
                 alert("支付过程中用户取消");
             }else{
                //支付失败
                alert(res.err_msg)
             }
    
        }
    );
    

业务流程

  • 用户通过不同场景点击进入商户网页

  • 用户选择商品购买,完成选购流程

  • 使用网页授权获取用户基本信息,得到openid

  • 商户使用统一下单接口获取prepay_id

  • 商户网页使用getBrandWCPayRequest接口调起微信支付

  • 支付成功后,在该接口里面会返回结果,同时微信也会发送异步通知到统一下单时候填写的notify_url

开发实现

实现目标目标

打开网页http://config.HOST/buy.html,选购商品,最终打开http://config.HOST/jsapi.html调起微信支付

JSAPI必须先用网页授权的方式去获取用户的openid,在统一下单的时候需要,详情可以查看官方文档:网页授权获取获取用户基本信息

创建jsapipay包

创建文件夹jsapipay

设置网页获取用户基本信息的授权域名

只有在授权域名下的链接才能获取openid,所以第一步就去设置网页授权目录

假设我们网站的域名为:www.qq.com,那么久需要把网页授权目录设置为www.qq.com

设置方法:登陆公众平台,在开发中中心,往下翻功能列表里面有一项:网页授权获取用户基本信息,点击旁边的修改按钮即可,按照要求填入我们要设置的域名

在本节代码里面我们把域名保存在config.HOST变量里面

创建选购商品显示页面

创建buy.html,点击里面的购买按钮后,获取code并跳转到支付页面buy.html



商家网页

商家网页

创建支付发起显示页面

创建jsapi.html文件,实现:

  • 实现对getBrandWCPayRequest接口的调用,从而调起微信支付
  • 打印出我们使用的参数

代码如下:


JSAPI支付一分钱

 






微信jsapi支付 1分钱

Appid: {{.AppId}}
TimeStamp: {{.TimeStamp}}
Nonce_str: {{.NonceStr}}
Package: {{.Package}}
SignType: {{.SignType}}
PaySign: {{.PaySign}}

getBrandWCPayRequest接口参数生成

创建文件jsapirequest.go,实现

  • Jsapirequest结构体:存储getCPayReBrandWquest需要的参数,传递给jsapi.html页面
  • func (v *Jsapirequest) Signmd5()函数:对Jsapirequest结构体中的非空参数进行微信支付签名,并存储签名结果

代码如下:

package jsapipay

import (
    "encoding/xml"
    "wechatpaygolang/config"
    "wechatpaygolang/tools"
)

//1.定义Jsapirequest结构体,存储getBrandWCPayRequest接口要使用的参数
type Jsapirequest struct {
    XMLName   xml.Name `xml:"xml"`
    AppId     string   `xml:"appId"`     //公众账号ID
    NonceStr  string   `xml:"nonceStr"`  //随机字符串
    Package   string   `xml:"package"`   //账单类型
    SignType  string   `xml:"signType"`  //商户号
    TimeStamp string   `xml:"timeStamp"` //对账单日起

    PaySign string `xml:"-"` //最终请求串

}

//2.对Jsapirequest的非空字段进行md5签名,并存储签名结构
func (v *Jsapirequest) Signmd5() bool {
    sign := tools.Wechatpay_SignMD5(*v, config.API_KEY)
    v.PaySign = sign
    return true
}

获取openid

创建auth.go文件,实现

  • Resultauth结构体,存储获取到的y用户基本新消息
  • func Getopenid(w http.ResponseWriter, r *http.Request) (openid string)函数,根据code获取openid
  • func getauthurl(callurl string) string函数,生成网页授权获取code的url,code会回传给callurl,注意这里的callurl必须是
    urlencode编码

代码实现

package jsapipay

import (
    "fmt"
    "wechatpaygolang/config"
    "wechatpaygolang/tools"
)

type Resultauth struct {
    Access_token string `json:"access_token"`
    Expires_in   int    `json:"expires_in"`
    Rfresh_token string `json:"rfresh_token"`
    Openid       string `json:"openid"`
    Scope        string `json:"scope"`
}

//根据code获取openid
func Getopenid(code string) (openid string) {
    r.ParseForm()

    requestUrl := "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + config.APP_ID + "&secret=" + config.APP_SECRET + "&code=" + code + "&grant_type=authorization_code"
    rebots := tools.Get(requestUrl)

    var m Resultauth
    err := tools.XmlDecodebytes(rebots, &m)
    if err != nil {
        fmt.Println("error:", err)
    }
    openid = m.Openid

    fmt.Println("openid=" + openid)
    return openid

}

// 获取code
func getauthurl(url string) string {
    str := "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx2b029c08a6232582&redirect_uri=" + url + "&response_type=code&scope=snsapi_base&state=test#wechat_redirect"
    return str

}

创建jsapi.go

前面我们分别创建了2个html页面,一个是表示选购页面,一个是发起支付页面,那么决定显示那个html页面,并传递对应的参数进去,在jsapi.go里面实现

func Jsapi(w http.ResponseWriter, r *http.Request)为页面入口函数,在这个函数里面,进行显示页面的判断和参数传递

  • 如果判断链接没有code字段,我们进入商品选购页面buy.html
  • 如果判断链接有code字段,我们进入支付页面pay.html,因为有code字段肯定是进行可buy.html里面的授权跳转

    package jsapipay
    
    import (
        "fmt"
        "html/template"
        "io/ioutil"
        "net/http"
        "os"
        "wechatpaygolang/auth"
        "wechatpaygolang/config"
        "wechatpaygolang/tools"
        "wechatpaygolang/unifiedorder"
    )
    
    func Jsapi(w http.ResponseWriter, r *http.Request) {
        r.ParseForm()
    
        code := r.FormValue("code")
        //没有code·进入选购页面
        if code == "" {
            path, _ := os.Getwd()
            t, err := template.ParseFiles(path + "/jsapipay/buy.html")
            fmt.Println(err)
            m := map[string]string{}
            m["callbackurl"] = getauthurl("http://" + config.HOST + "/jsapi")
            da.Signmd5()
    
            t.Execute(w, da)
        } else {
            //获取openid
            openid := auth.Getopenid(w, r)
            //1.统一下单
            v := &unifiedorder.Unifieldrequest{Appid: config.APP_ID, Mch_id: config.MCH_ID}
    
            //支付金额,单位为分
            v.Total_fee = "1"
            //商品说明
            v.Body = "JSAPI"
            //32位随机字符串
            v.Nonce_str = tools.Getnoncestr(32)
            //商户订单号,此处也随机生成
            v.Out_trade_no = tools.Getnoncestr(32)
            //发起支付的机器IP
            v.Spbill_create_ip = "127.0.0.1"
            //设置openid
    
            v.Openid = "omL67jm0A1sKwystTq7WsU28MF_c"
    
            //最终支付成功的通知地址
            v.Notify_url = config.URL_UNIFIEDORDER_NOTIFYURL
            //支付方式为JSAPI
            v.Trade_type = "JSAPI"
            //对上面设置的字段进行签名
            v.Signmd5()
            //把所有的有效字段组织为xml格式
            v.Xml()
    
            //向统一下单接口发起请求,并把返回请求结果
            res := v.Dorequest()
            fmt.Println("下单结果=", res.ReponseXML)
    
            fmt.Println("prepayid=", res.Prepay_id)
            fmt.Println("========================================")
    
            //打开支付网页,并传递参数,包括传递我们上面统一下单获取到的prepay_id
            path, _ := os.Getwd()
            t, err := template.ParseFiles(path + "/jsapipay/pay.html")
            fmt.Println(err)
            da := Jsapirequest{AppId: config.APP_ID}
            da.Package = "prepay_id=" + res.Prepay_id
            da.TimeStamp = tools.Time_stamp_seconds()
            da.NonceStr = tools.Getnoncestr(32)
            da.SignType = "MD5"
            da.Signmd5()
    
            t.Execute(w, da)
        }
    }
    

结果判定

jsapi.html里面,根据判定结果,进行相应的处理

返回值 描述
get_brand_wcpay_request:ok  支付成功
get_brand_wcpay_request:cancel  支付过程中用户取消
get_brand_wcpay_request:fail    支付失败

和/jsapi地址关联

main.go文件里面

  • 加入头文件"wechatpaygolang/jsapi"

  • rout函数里面加入路由

    func rout(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    
    path := r.URL.Path
    
    if path == "/helloworld" {
        fmt.Println("被扫支付")
        helloworld.HelloWorld(w, r)
    
    } else if path == "/micropay" {
        fmt.Println("被扫支付")
        micropay.Micropay(w, r)
    
    } else if path == "/jsapi" {
        fmt.Println("被扫支付")
        jsapi.Jsapi(w, r)
    
    } 
    
    }
    

设置支付目录

jsapi必须设置支付木有,只有在支付目录下的页面才能发起支付,否则会报错,上异步我们已经确认了我们要访问的地址为/jsapi,假设我们的域
名为www.qq.com,那么这个时候我们的支付域名就是www.qq.com/jsapi,那么我们就需要把www.paytest.com/设置为支付目录

设置方法:
登陆公众平台-微信支付-开发配置-JSAPI支付,添加支付目录为www.paytest.com/,注意添加的目录的域名必须经过备案

同时可以设置测试目录,测试目录不需要备案,但必须把测试的微信号加入白名单,而且测试的支付链接只能在这个公众号内打开

编译测试

在微信里面打开支付url,进行测试

异步通知

除了上面js接口里面的结果判定,同时在统一下单的时候,我们填写了一个notify_url,我们同时会发送异步通知到这个地址,这个后面再说,因为还有其他支付方式也会发送异步通知,所以作为一个单独模块来说,这里我们先随便填一个,无所谓的。

你可能感兴趣的:(微信支付golang)