微信支付之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) } } );
- 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獲取openidfunc 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
,我們同時會發送非同步通知到這個地址,這個後面再說,因為還有其他支付方式也會發送非同步通知,所以作為一個單獨模組來說,這裡我們先隨便填一個,無所謂的。