微信支付之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)
}
}
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
业务流程
用户通过不同场景点击进入商户网页
用户选择商品购买,完成选购流程
使用网页授权获取用户基本信息,得到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
<!DOCTYPE html>
<html>
<title>商家网页</title>
<head>
<form class="editPart" action="{{.callbackurl}}">
<fieldset>
<legend>商家网页</legend>
<p><label>商品金额1分钱测试</label></p>
<div class="lightEditor">
<iframe id="myFrame" class="editorFrame" style="height:162px;" frameborder="0px" tabindex=3></iframe>
</div>
</p>
<input type="submit" class="commentSubmit" value="点击购买" />
</fieldset>
</form>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
创建支付发起显示页面
创建jsapi.html
文件,实现:
- 实现对getBrandWCPayRequest接口的调用,从而调起微信支付
- 打印出我们使用的参数
代码如下:
JSAPI支付一分钱
<script type="text/javascript">
//实现微信支付JS脚本
function pay() {
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)
}
}
);
}
</script>
</head>
<body>
<!--打印出参数-->
<h1>微信jsapi支付 1分钱</h1>
<a>Appid: {{.AppId}}</a><br>
<a>TimeStamp: {{.TimeStamp}}</a><br>
<a>Nonce_str: {{.NonceStr}}</a><br>
<a>Package: {{.Package}}</a><br>
<a>SignType: {{.SignType}}</a><br>
<a>PaySign: {{.PaySign}}</a><br>
<button type="button" onclick="pay()">点击微信支付</button>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
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
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
获取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
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
创建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)
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
结果判定
在jsapi.html
里面,根据判定结果,进行相应的处理
返回值 描述
get_brand_wcpay_request:ok 支付成功
get_brand_wcpay_request:cancel 支付过程中用户取消
get_brand_wcpay_request:fail 支付失败
- 1
- 2
- 3
- 4
和/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)
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
设置支付目录
jsapi必须设置支付木有,只有在支付目录下的页面才能发起支付,否则会报错,上异步我们已经确认了我们要访问的地址为/jsapi,假设我们的域
名为www.qq.com,那么这个时候我们的支付域名就是www.qq.com/jsapi,那么我们就需要把www.paytest.com/设置为支付目录
设置方法:
登陆公众平台-微信支付-开发配置-JSAPI支付,添加支付目录为www.paytest.com/,注意添加的目录的域名必须经过备案
同时可以设置测试目录,测试目录不需要备案,但必须把测试的微信号加入白名单,而且测试的支付链接只能在这个公众号内打开
编译测试
在微信里面打开支付url,进行测试
异步通知
除了上面js接口里面的结果判定,同时在统一下单的时候,我们填写了一个notify_url
,我们同时会发送异步通知到这个地址,这个后面再说,因为还有其他支付方式也会发送异步通知,所以作为一个单独模块来说,这里我们先随便填一个,无所谓的。