1. 程式人生 > >利用H5開發微信公眾號

利用H5開發微信公眾號

enc 兩個 寫入 iss mps rep ast htm chang

一、 首先授權配置

 公眾號設置 --》功能設置
 設置業務域名!

技術分享
這裏的MP_verify_w7tdZrafqhkK9Mcj.txt文件,需要放到你項目的根目錄下,例子:你的項目war包叫 test.war ,裏面包含了src,WEB-INFO兩個文件夾,則把MP_verify_w7tdZrafqhkK9Mcj.txt放到這兩個文件夾的同級目錄下;即現在test.war下有兩個文件夾一個txt文件:src,WEB-INFO,MP_verify_w7tdZrafqhkK9Mcj.txt.
後面就需要配置js接口安全域名和網頁授權域名了,最好和業務域名配置為一樣就好了。
二、開發中的配置
在開發目錄下的基本配置中進行配置。
基本配置–》開發者ID–》AppID(應用ID),AppSecret(應用密鑰);
基本配置–》微信開放平臺賬號綁定
這裏需要綁定微信開放平臺,若不綁定,在獲取用戶信息的時候就只能獲取到用戶的openId,不能獲取到unionId.
技術分享


註意:定義菜單的時候有講究了,
如果你想在用戶點這個菜單的時候就拿到用戶的微信基本信息(性別,昵稱,openId,unionId),這裏直接可以配置成為授權鏈接,授權鏈接回調url直接寫成你後臺的一個接口地址,然後由這個接口來跳轉到其它頁面.
例子:H5授權的鏈接:
http://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxx&redirect_uri=http://xxxx.com/test/login&response_type=code&scope=snsapi_userinfo&state=xxx&connect_redirect=1#wechat_redirect
微信回調的url是一個接口地址:http://xxxx.com/test/login,不能回調網頁,因為存在跨域錯誤。
state的值是可以自定義的。
這裏的scope是用的snsapi_userinfo,這樣可以直接在後臺拿到用戶信息。
微信回調這個接口的時候會把code和state的值返回,接口就可以通過code去拿用戶的信息了。
若要用code去拿用戶的信息,又會去做一堆事情,這些事情確實麻煩;推薦直接使用第三方的jar包,一步就拿到了。
推薦的jar包:weixin-Java-mp-2.5.0.jar,weixin-java-common-2.5.0.jar;
maven地址

    <dependency>
      <groupId>com.github.binarywang</groupId>
      <artifactId>weixin-java-mp</artifactId>
      <version>2.5.0</version>
    </dependency>
 在做微信支付的時候有可能會用到xstream的包,有需要也拿去
 xstream-1.4.7.jar,xxp3_min-1.1.4.jar,xmlpull-1.1.3.1.jar
 maven地址 
    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.4.7</version>
    </dependency>
    <dependency>
        <groupId>xpp3</groupId>
        <artifactId>xpp3</artifactId>
        <version>1.1.4c</version>
    </dependency>
    <dependency>
        <groupId>xmlpull</groupId>
        <artifactId>xmlpull</artifactId>
        <version>1.1.3.1</version>
    </dependency>

這裏說下weixin-java-mp-2.5.0.jar的使用方法,
1.weixin-java-mp-2.5.0.jar裏面最重要的類是
WxMpInMemoryConfigStorage和WxMpService;
WxMpInMemoryConfigStorage是用來存微信公眾號的基本信息的,
在spring+SpringMvc中的使用方法例子:

@Configuration
@PropertySource(
        value={"classpath:wxProperties.properties"},
        ignoreResourceNotFound = true)
[email protected]("propertyPlaceholderConfigurer")
public class WeixinConfig {
    //直接獲取資源文件中的配置的值
    @Value("${wxProperties.appid}")
    private String appid;//appId

    @Value("${wxProperties.appsecret}")
    private String appsecret;//Appsecret

    @Value("${wxProperties.token}")
    private String token;//Token

    @Value("${wxProperties.aeskey}")
    private String aesKey;//aeskey,有就填,沒有就不填

    @Value("${wxProperties.partener_id}")
    private String partenerId;//商戶號

    @Value("${wxProperties.partener_key}")
    private String partenerKey;//商戶秘鑰

    @Value("${wxProperties.notify_url}")
    private String notifyUrl;//支付後臺通知接口地址

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
       PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
       ppc.setIgnoreUnresolvablePlaceholders(true);
       return ppc;
    }

    @Bean
    public WxMpConfigStorage wxMpConfigStorage() {
        WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();
        configStorage.setAppId(this.appid);
        configStorage.setSecret(this.appsecret);
        configStorage.setToken(this.token);
        configStorage.setAesKey(this.aesKey);
        configStorage.setPartnerId(this.partenerId);
        configStorage.setPartnerKey(this.partenerKey);
        configStorage.setNotifyURL(this.notifyUrl);
        return configStorage;
    }

    @Bean
    public WxMpService wxMpService() {
        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
        return wxMpService;
    }

}

這裏是配置微信公眾號的基本信息,其它地方要使用就直接使用:

    @Autowired
    protected WxMpService wxMpService;

2.獲取微信用戶基本信息示例代碼:

WxMpOAuth2AccessToken accessToken;
WxMpUser wxMpUser = null;
accessToken = this.wxMpService.oauth2getAccessToken(code);
wxMpUser = this.wxMpService.getUserService().userInfo(accessToken.getOpenId(), null);
//用戶的基本信息就在wxMpUser中了,需要的就拿去用了。(註意:微信用戶的性別:0:未知,1:男 2:女)

3.H5若要使用微信封裝好的js,則需要一些基本的配置信息,完全可以從後天臺獲取然後返回,示例代碼:

 public StatusResult<Map<String, Object>> createJsapiSignature(String url) {
        Map<String, Object> result = new HashMap<String, Object>();
        try {
            WxJsapiSignature  wxJsapiSignature = wxMpService.createJsapiSignature(url);
            String getJsapiTicket = wxMpService.getJsapiTicket();
            result.put("wxJsapiSignature", wxJsapiSignature);
            return StatusResult.success(result, "");
        } catch (WxErrorException e) {
            return StatusResult.failed("未知錯誤出現", result);
        }
    }

這裏的url是H5用js代碼獲取的:

var url = location.href.split(‘#‘)[0];

4.公眾號支付
流程:
頁面發起–>後臺下單後返回前端所需要的參數–>頁面發起支付–>用戶輸入密碼付款成功–>微信回調後臺通知接口–>業務處理完成
H5頁面發起支付:
微信公眾號的頁面支付首先要配置微信公眾號:
微信支付–>開發配置–>支付授權目錄(正式、測試的目錄必須要不一樣,否則有你好看的,)
註意:設置授權目錄的時候必須要精確到需要支付頁面的目錄文件夾;
例子:需要支付的H5頁面 http://xxxxx.com/test/html/pay/pay.html
則授權目錄配置為 http://xxxxx.com/test/html/pay/
配置完成後就可以發起下單了,
後臺下單代碼示例:

public StatusResult<Map<String, String>> getJSSDKPayInfo(HttpServletResponse response,
                                HttpServletRequest request) {
        StatusResult<Map<String, String>> result = null;
        String spbill_create_ip = request.getRemoteAddr();//訂單生成的機器 IP
        Map<String, String> map = new HashMap<String, String>();    
        WxMpConfigStorage wx= wxMpService.getWxMpConfigStorage();
        WxPayUnifiedOrderRequest prepayInfo = new WxPayUnifiedOrderRequest();
        //TODO change all request parameters to a VO class
        prepayInfo.setOpenid("openId");        
        prepayInfo.setOutTradeNo("out_trade_no");//設置訂單商戶號
        int total_fee = 0.01 * 100;
        total_fee = 1;
        prepayInfo.setTotalFee(Integer.valueOf(total_fee));//設置支付金額 單位為分 
        prepayInfo.setBody("xxxxx");//支付的內容簡介
        prepayInfo.setTradeType("JSAPI");//渠道:公眾號支付
        prepayInfo.setSpbillCreateIp(spbill_create_ip);//終端ip 
        //TODO(user) 填寫通知回調地址
        prepayInfo.setNotifyURL(wx.getNotifyURL());
        try {
            兩種下單方式,如果報錯請先仔細檢查微信配置的各種參數
            //WxPayUnifiedOrderResult  wxPayUnifiedOrderResult= wxMpService.getPayService().unifiedOrder(prepayInfo);
            Map<String, String> payInfo = this.wxMpService.getPayService().getPayInfo(prepayInfo);
            if(payInfo != null){
                //業務代碼
            }
            result = StatusResult.success(payInfo);
            return result;
        } catch (WxErrorException e) {
            log.error(e.getError().toString());
            map.put("error", e.getError().toString());
            return StatusResult.failed("微信下單失敗",map);
        }
    }

H5頁面支付js代碼(需要引入微信js哈):

function callPay(){
            if (typeof WeixinJSBridge == "undefined"){
               if( document.addEventListener ){
                   document.addEventListener(‘WeixinJSBridgeReady‘, onBridgeReady, false);
               }else if (document.attachEvent){
                   document.attachEvent(‘WeixinJSBridgeReady‘, onBridgeReady); 
                   document.attachEvent(‘onWeixinJSBridgeReady‘, onBridgeReady);
               }
            }else{
               onBridgeReady();
            }       
        }
        function onBridgeReady(){
               WeixinJSBridge.invoke(
                   ‘getBrandWCPayRequest‘, {
                       "appId":appId,     //公眾號名稱,由商戶傳入     
                       "timeStamp":timeStamp,         //時間戳,自1970年以來的秒數     
                       "nonceStr":nonceStr, //隨機串     
                       "package":package,     
                       "signType":"MD5",         //微信簽名方式:     
                       "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將在用戶支付成功後返回    ok,但並不保證它絕對可靠。 
                   }
               ); 
        }

微信回調後臺通知接口代碼示例:

 public void getJSSDKCallbackData(HttpServletRequest request,
            HttpServletResponse response) {
        try {
            synchronized (this) {
                Map<String, String> kvm = XMLUtil.parseRequestXmlToMap(request);
                System.out.println("微信通知返回結果:\t"+kvm.toString());
                WxPayOrderQueryResult  wxPayOrderQueryResult = wxMpService.getPayService().queryOrder("", kvm.get("out_trade_no"));//用訂單號去查詢訂單狀態,冗余代碼可看可刪
//                if (this.wxMpService.getPayService().checkSign(kvm,  kvm.get("sign"))) {
                System.out.println("查詢訂單返回結果:\t"+wxPayOrderQueryResult.getTradeState());
                if ("SUCCESS".equals(wxPayOrderQueryResult.getTradeState())) {
                    if (kvm.get("result_code").equals("SUCCESS")) {
                        //TODO(user) 微信服務器通知此回調接口支付成功後,通知給業務系統做處理
                        log.info("out_trade_no: " + kvm.get("out_trade_no") + " pay SUCCESS!");
                        String out_trade_no = kvm.get("out_trade_no"); //支付訂單號,接下來寫業務代碼                       
                                }
                            }
                        } 
                        log.info("已經支付的訂單詳情\t"+aleadyPayOrder);
                        response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[ok]]></return_msg></xml>");
                    } else {
                        log.error("out_trade_no: "
                            + kvm.get("out_trade_no") + " result_code is FAIL");
                        response.getWriter().write(
                            "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[result_code is FAIL]]></return_msg></xml>");
                    }
                } else {
                    response.getWriter().write(
                        "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[check signature FAIL]]></return_msg></xml>");
                    log.error("out_trade_no: " + kvm.get("out_trade_no")
                        + " check signature FAIL");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

到此為止公眾號支付完成。

三、微信js中接口使用
分享等很常見的,需要初始化微信的js,需要利用到上面寫的createJsapiSignature後臺接口。
公眾號的分享給朋友、朋友圈、QQ空間等方法在微信js中是可以直接調用的,分享的內容可以自己改變的,但是分享出去的按鈕只能是微信右上角的分享,開發者不能自定義分享按鈕。。。這一點讓人非常不爽。
H5頁面js的代碼示例:

引入微信的js,然後初始化,然後微信會自動執行wx.ready中的方法

wx.config({
    debug: false,   //開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。  
    appId: appId,   //必填,公眾號的唯一標識
    timestamp: timestamp,   // 必填,生成簽名的時間戳 
    nonceStr: nonceStr,   //必填,生成簽名的隨機串 
    signature: signature,   // 必填,簽名
    jsApiList: [//需要多少接口就寫多少接口
        ‘checkJsApi‘,//判斷當前客戶端是否支持指定JS接口
        ‘onMenuShareAppMessage‘//獲取“分享給朋友”按鈕點擊狀態及自定義分享內容接口
    ] //必填,需要使用的JS接口列表,所有JS接口列表 
}); 
wx.ready(function () {  
    var title = "xxxx";
    var desc = "xxxx";
    var imgUrl = "http://xxx.com/test/picture/xxxxx.png";
    wx.onMenuShareAppMessage({
        title: title, // 分享標題
        desc: desc, // 分享描述
        link: url, // 分享鏈接,h5網頁的地址或者其它
        imgUrl: imgUrl,
        trigger: function(res) {
            alert(‘用戶點擊發送給朋友‘);
        },
        success: function(res) {
            alert(‘已分享‘);
        },
        cancel: function(res) {
            alert(‘已取消‘);
        },
        fail: function(res) {
            alert(JSON.stringify(res));
        }
    });
}); 

調用微信js中的方法流程:
初始化config–>執行wx.ready
開發者需要更多的功能就依照葫蘆畫瓢了,或者去微信js sdk文檔去copy方法了。

讓人不愉快的事情又發生了,微信在4月29日開始限制自定義分享的鏈接啦,必須要是安全域名下的鏈接才可以分享。
詳情請看:
JSSDK自定義分享接口的策略調整
這樣導致不能直接分享自己想要的鏈接了,但是解決方法還是有的:
在你的域名下新建一個H5頁面,在這個H5頁面的js代碼中做一個跳轉就好啦(但是如果你分享出去的是支付頁面,那多半是支付不了地)!

四、處理微信直接可以分享頁面問題
有時候業務需要不能把當前的頁面分享出去,但是微信自帶的分享、復制鏈接按鈕是可以在任何頁面拿到當前頁面的地址,如果別人點擊就會進入。為了避免這個情況發生,有幾種處理方法:
1.後端足夠強大,頁面跳轉完全由後端完成,在加入了權限驗證的情況下就不怕這個的,後端會攔截請求驗證,驗證不過就跳指定的error頁面就好。
2.前端做驗證
jsp可以用session,判斷session中的一個全局參數即可。
H5可以使用cookie,在項目的開始頁寫入cookie,在js中寫一個驗證方法,每個頁面都調用這個驗證方法進行驗證,雖然有冗余代碼,但是這個是可以實現地,簡單粗暴,速度快。
設置cookie的方法:

jquery(function() {
    var expiresDate= new Date();
    expiresDate.setTime(expiresDate.getTime() + (30 * 60 * 1000));//半小時
    jquery.cookie("openId",‘${openId}‘, {
          path : ‘/‘,//cookie的作用域為根目錄,任何頁面都是有效的
          expires : expiresDate
         });//cookie裏面保存openId
});

然後在js中寫一個方法每個頁面都調用判斷。

利用H5開發微信公眾號