個人微信開發代碼集:公衆號管理與微信支付

Github Repo與代碼地址html

WXWeChatToolkits

個人微信SDK,包括公衆平臺管理、微信支付等各個版本。老實說,微信的文檔並非很友好,坑很多啊~~ 筆者在這裏準備的算是半自動化的,自認爲的特性有:前端

  • 先後端分離,這裏的JS代碼和後端代碼是能夠單獨部署的。換言之,微信裏須要的各類各樣的域名配置與審覈,你只要保證你的HTML頁面在那個域名下就好,業務邏輯的代碼隨便放git

  • 後端這邊筆者本身開發時候用的是Spring Boot,可是這裏移除了全部Spring Boot的緊密耦合代碼,只是用Pure Java API進行實現,也方便單元測試github

  • 前端這邊用的是ES6 + Webpack,能夠參考筆者其餘前端項目算法

下面就大概描述下開發流程和能夠用到的本代碼集的東東,半成品,權當一樂。後端

公衆號管理

用戶鑑權

用戶鑑權首先須要檢測是否進行認證跳轉:api

WXService wxService = new WXService();


//判斷是否爲微信系統內
if (wxService.isWeixinBrowser()) {

    //若是是在微信瀏覽器內判斷是否須要進行登陸操做
    const openid = wxService.getOpenidOrRedirectToAuth("eapply");

    if (!openid) {
        //若是openid不存在,則提示錯誤,而且跳轉登陸受權
        message.info("未登陸,現進行登陸受權!");
    } else {
        //執行JSSDK的註冊
        wxService.jssdkConfig();
    }

}

這裏的跳轉大概是這樣的路徑:瀏覽器

const auth_url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${this.appid}&response_type=code&scope=snsapi_base&state=${state}&redirect_uri=${this.redirect_uri}#wechat_redirect`

注意,一開始筆者本身是想將回調以後的跳轉路徑放到State裏面的,可是微信好像對State作了限制,所以在這裏只是配置了一個標識,而具體的標識與跳轉地址映射寫到了後臺代碼裏:服務器

/**
     * @return
     * @function 生成State與跳轉的網頁的配置
     */
    private Map<String, String> stateMapGenerator() {

        Map<String, String> stateMap = new HashMap<>();

        //電子報名頁面默認的跳轉
        stateMap.put("eapply", "http://mp.dragon.live-forest.com/pay/index.html");

        return stateMap;

    }

JSSDK

微信的官方文檔裏提到,要申請JSSDK要先去獲取jsapi_ticket,而jsapi_ticket須要用access_token換取。要注意,微信的access_token是分類型的,上面用戶鑑權也用到了access_token,不過那個的類型是authentic。
這裏的access_token類型是jsapi。配置的前端代碼是:微信

jssdkConfig() {

        //插入JSSDK腳本
        // load a single file
        loadjs('http://res.wx.qq.com/open/js/jweixin-1.0.0.js', () => {
            // foo.js loaded'//從URL中獲取JSSDK信息

            //訪問遠端獲取JSSDK配置信息
            this.getWithQueryParams({
                path: "/mp/jssdk",
                requestData: {
                    url: location.href
                }
            }).then((jssdk)=> {

                console.log(jssdk);

                //配置JSSDK
                wx.config({

                    debug: false, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。

                    appId: this.appid, // 必填,公衆號的惟一標識

                    timestamp: jssdk.timestamp, // 必填,生成簽名的時間戳

                    nonceStr: jssdk.noncestr, // 必填,生成簽名的隨機串

                    signature: jssdk.signature,// 必填,簽名,見附錄1

                    jsApiList: ['chooseWXPay'] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2

                });

                //監控錯誤信息
                wx.error(function (res) {

                    // config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。
                    alert(JSON.stringify(res));
                });

            });


        });


    }

後端代碼是:

//初始化服務
        MPService mpService = new MPService();

        //首先獲取接口調用憑據
        String accessToken = mpService.fetchAccessToken4ClientCredential().get("access_token");

        //根據accessToken獲取Ticket
        //從微信服務端獲取到Ticket
        String apiTicket = mpService.fetchTicketByAccessToken(accessToken, "jsapi").get("ticket");

        //獲取隨機字符串
        String nonceStr = StringGenerator.getRandomString(10);

        //獲取當前時間戳
        Long timeStamp = Instant.now().getEpochSecond();

        //進行簽名
        Map<String, Object> signatureMap = new HashMap<>();

        signatureMap.put("noncestr", nonceStr);

        signatureMap.put("jsapi_ticket", apiTicket);

        signatureMap.put("timestamp", timeStamp);

        signatureMap.put("url", url);

        signatureMap.put("signature", Signature.getSign4MP(signatureMap));

        return signatureMap;

微信支付

注意,微信支付內也須要簽名,可是簽名的算法以及所須要的Key與公衆號管理內仍是有必定區別的。

統一下單獲取預支付代碼

前端代碼:

fetchPrepayId({
        body = "商品詳情",
        out_trade_no = "1415659990",
        total_fee = 1,
        openid = undefined,
        attach //附加信息
    }) {

        if (openid) {
            //若是存在openid,則是以JSAPI方式調用
            return this.getWithQueryParams({
                path: this.fetchPrepayIdUrl,
                requestData: {
                    body,
                    out_trade_no,
                    total_fee,
                    openid,
                    attach
                }
            });
        } else {
            //不然是以APP方式調用
            return this.getWithQueryParams({
                path: this.fetchPrepayIdUrl,
                requestData: {
                    body,
                    out_trade_no,
                    total_fee,
                    attach
                }
            });

        }


    }

後端代碼:

@SneakyThrows
    public Map<String, Object> prepay(
            String body,
            String out_trade_no,
            Integer total_fee,
            String openid,
            String attach) {

        //最終返回的結果
        Map<String, Object> result = new HashMap<>();

        //調用統一下單服務
        UnifiedOrderService unifiedOrderService = UnifiedOrderService
                .builder(body, out_trade_no, total_fee, getIp("127.0.0.1"))
                .attach(attach)
                .build();

        //獲取的返回的同一訂單信息
        Map<String, Object> unidiedOrder;

        //判斷openid是否存在
        if (openid != null) {
            //若是opendid存在,則建立JSAPI Order
            unidiedOrder = unifiedOrderService.jsApiOrder(openid);
        } else {
            unidiedOrder = unifiedOrderService.appOrder();
        }

//        System.out.println(unidiedOrder);

        /* 最終客戶端要提交給微信服務器的訂單,所以咱們也要將關鍵信息加進去
        "appId" : "wx2421b1c4370ec43b",     //公衆號名稱,由商戶傳入
        "timeStamp":" 1395712654",         //時間戳,自1970年以來的秒數
        "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //隨機串
        "package" : "prepay_id=u802345jgfjsdfgsdg888",
        "signType" : "MD5",         //微信簽名方式:
        "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名
        */

        //獲取隨機字符串
        String nonceStr = RandomStringGenerator.getRandomStringByLength(20);


        //返回商戶對應的AppID
        result.put("appId", Configure.appID);

        result.put("timeStamp", Instant.now().getEpochSecond());

        result.put("nonceStr", nonceStr);

        result.put("package", "prepay_id=" + unidiedOrder.get("prepay_id"));

        result.put("signType", "MD5");

        result.put("paySign", Signature.getSign4Pay(result));

        //直接返回
        return result;
    }

微信內H5支付

doSyncPay({
        appId="wx7d0444df2763bf91",
        timeStamp="1465698294",
        nonceStr="2g1w8kvb5lamqwfx6j8o",
        package_r="prepay_id=wx2016061210245447b57ae3b30364645260",
        signType="MD5",
        paySign="01B98B973451A1AA83EC062F2F46AB75"
    }, cb) {

        //調用微信支付的接口
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                "appId": appId,
                "timeStamp": timeStamp,
                "nonceStr": nonceStr,
                "package": package_r,
                "signType": signType,
                "paySign": paySign
            },
            function (res) {

                //打印支付信息
                console.log(res);

                if (res.err_msg == "get_brand_wcpay_request:ok") {
                    //支付成功
                    cb(res);

                } else {

                    // alert(JSON.stringify(res));

                    alert("您取消了支付!");
                }
            }
        )
        ;
    }

支付結果回調

public String wx_notify(String body) {

//        System.out.println(body);

        PayedOrderService payedOrderService = new PayedOrderService();

        //解析數據
        Map<String, String> parsedMap = payedOrderService.parseNotifyXML(body);

        System.out.println(parsedMap);

        //調用更新狀態的函數
        payedOrderService.updateOrderState(parsedMap.get("attach"), "1", "1", parsedMap.get("TransactionId"));

        return "success";
    }
相關文章
相關標籤/搜索