1. 程式人生 > >微信支付之JSAPI支付

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

JSAPI支付