微信支付之JSAPI支付

JSAPI支付

JSAPI支付是用戶在微信中打開商戶的H5頁面,商戶在H5頁面通過調用微信支付提供的JSAPI接口調起微信支付模塊完成支付

使用場景

  • 用戶在微信公衆賬號(必須是服務號)內進入商家公衆號,打開某個H5頁面,完成支付

  • 用戶的好友在朋友圈、聊天窗口等分享商家H5頁面連接,用戶點擊鏈接打開商家H5頁面,完成支付

  • 將商戶H5頁面轉換成二維碼,用戶掃描二維碼後在微信瀏覽器中打開h5頁面後完成支付

JSAPI接口

  • 使用JS腳本:getBrandWCPayRequest 調起微信支付

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
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

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
<!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>

創建支付發起顯示頁面

創建jsapi.html文件,實現:

  • 實現對getBrandWCPayRequest接口的調用,從而調起微信支付
  • 打印出我們使用的參數

代碼如下:


JSAPI支付一分錢

  
  
  • 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
<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>

getBrandWCPayRequest接口參數生成

創建文件jsapirequest.go,實現

  • Jsapirequest結構體:存儲getCPayReBrandWquest需要的參數,傳遞給jsapi.html頁面
  • func (v *Jsapirequest) Signmd5()函數:對Jsapirequest結構體中的非空參數進行微信支付簽名,並存儲簽名結果

代碼如下:

  
  
  • 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
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編碼

代碼實現

  
  
  • 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
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裏面的授權跳轉

  
  
  • 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
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裏面,根據判定結果,進行相應的處理

  
  
  • 1
  • 2
  • 3
  • 4
返回值 描述 get_brand_wcpay_request:ok 支付成功 get_brand_wcpay_request:cancel 支付過程中用戶取消 get_brand_wcpay_request:fail 支付失敗

和/jsapi地址關聯

main.go文件裏面

  • 加入頭文件"wechatpaygolang/jsapi"

  • rout函數裏面加入路由

  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
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,我們同時會發送異步通知到這個地址,這個後面再說,因爲還有其他支付方式也會發送異步通知,所以作爲一個單獨模塊來說,這裏我們先隨便填一個,無所謂的。