微信公眾號支付踩坑記

木瓜芒果發表於2018-09-30

  前兩週做微信H5支付,在瀏覽器端用的,天真地以為app掛到公眾號中也能用,結果不行>"<|||| ,只好再對接一次公眾號支付,微信的支付對接下來總體感覺就是封裝地不如支付寶,文件不完善,坑賊多。本文會主要關注對接過程中所遇到的問題,以及部分實現程式碼。

1.介紹

  公眾號支付(JSAPI支付)是指使用者在微信中開啟商戶的H5頁面,商戶在H5頁面通過呼叫微信支付提供的JSAPI介面調起微信支付模組來完成支付,適用於在公眾號、朋友圈、聊天視窗等微信內完成支付的場景。注意公眾號支付必須在微信環境中才能生效,在普通瀏覽器中是不能用的。

  現有微信支付的介面都分為普通商戶版、服務商版、銀行服務商版,普通商戶就是單個商戶,服務商版就是一個服務商下面可以掛很多子商戶,我這邊對接的是服務商版。

  公眾號的對接過程大致分為申請公眾號、提交資料、簽署協議、繫結場景等流程,其中第四步繫結場景是登入公眾平臺,確認商戶號和公眾號的繫結關係,這裡具體可以參考微信文件


 

  這裡需要注意區分公眾號和商戶號:

  公眾號:微信公眾號是開發者或商家在微信公眾平臺上申請的應用賬號,該帳號與QQ賬號互通,通過公眾號,商家可在微信平臺上實現和特定群體的文字、圖片、語音、視訊的全方位溝通、互動

  商戶號:指財付通的支付系統為公司配置的用來儲存公司的身份資訊、交易資訊並處理公司的交易指令的賬號。微信支付商戶號將直接與公司提供的合法銀行卡賬戶繫結,公司的銀行卡賬戶將根據微信支付商戶號的交易情況做相應的資金扣劃或歸集。說白了就是微信分配用於支付的賬戶。


 

 2. 實現

  本文不會過多的講述微信公眾號支付實現程式碼(部分程式碼可參考微信h5支付),主要從前期配置(這個很重要)和遇到問題這兩方面來論述。

2.1 前期配置

  涉及到的域名配置需要分別登陸公眾平臺和商戶平臺進行配置。

  在商戶平臺登陸商戶號之後,在產品中心--開發配置中可以設定支付授權目錄,這裡設定的是在發起支付時使用的域名,如果不設定在發起支付時會報商家存在未配置的引數,請聯絡商家解決錯誤。設定如下:

 

  在公眾平臺登陸公眾號後,需要在開發 - 介面許可權 - 網頁服務 - 網頁帳號 - 網頁授權獲取使用者基本資訊”的配置選項中,修改授權回撥域名,按照微信的要求改就行了。至於為什麼微信要設定兩個域名,而且分別是在公眾平臺和商戶平臺都要設定,這裡在說一下,一個是用於獲取openid時需要的,一個是支付時需要的,微信要求的沒辦法,為什麼不在一個賬號裡面設定?那就要問微信了--開發配置頁面遷移至商戶平臺

  注意,這裡的配置一定要重視,不然後面獲取openid和支付時通不過!!!

2.2 獲取openid

  公眾號支付必須傳使用者的openid,在服務商模式下,可傳openid(使用者標識)或者sub_openid(使用者子標識),如果選擇傳sub_openid,則必須傳sub_appid。使用者標識其實使用者在商戶appid下的唯一標識,傳openid就用appid獲取,傳sub_openid則需要通過sub_appid來獲取。

  由於我這裡只是獲取使用者的openid,並沒有獲取使用者資訊,所以分為2步,具體可以參考微信網頁授權

  a  使用者同意授權,獲取code

public RestResult<String> auth(@RequestBody TradeVo tradeVo){
        String redirectUrl = "";   //注意,此處的域名需要填寫前面在公眾號平臺設定的授權域名
        JSONObject jsonObject = (JSONObject) JSONObject.toJSON(tradeVo);
        String state = jsonObject.toJSONString();
        try{
            redirectUrl = URLEncoder.encode(redirectUrl,"UTF-8");
            state = URLEncoder.encode(state,"UTF-8");
        }catch (Exception e){
            return new RestResult<String>(new ServiceException(e));
        }
        String auth = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx468c78f63c050017&redirect_uri="+redirectUrl+"&response_type=code&scope=snsapi_base&state="+state+"#wechat_redirect";
        return new RestResult<>(auth);
    }

  這裡主要是後端將一些業務引數也一起封裝到state欄位裡面了,可以在下一次重定向時使用。該方法返回的是一個url地址,前端收到後端返回後直接跳轉到這個地址上,微信會進行授權生成code並將剛才的自定義欄位state一起放在一個url後面(就是程式碼中指定的回撥redirect_uri),然後頁面會自動重定向到這個地址,我這裡的是一箇中間頁面,這個頁面裡面會把code和state解析出來再呼叫後端另外一個介面,就是等下的第2步。。。

  b  通過code換取網頁授權access_token

public RestResult getOpenId(@RequestParam("code") String code, @RequestParam("state") String state){
    RestResult restResult = null;
    TradeVo tradeVo = null;
    try{
        getopenId(code);
        tradeVo = ((JSON)JSON.parse(URLDecoder.decode(state,"UTF-8"))).toJavaObject(TradeVo.class);
    }catch (Exception e){
        return new RestResult(new ServiceException(e));
    }
    // 此處才會呼叫微信統一下單介面,程式碼可以參考我的另一篇部落格微信h5支付
    
return restResult; }
private void getopenId(String code){
String host = "https://api.weixin.qq.com";
String path = "/sns/oauth2/access_token";
Map<String, String> headers = new HashMap<>();
Map<String, String> querys = new HashMap<>();
querys.put("appid","");   //公眾號appid,可以是服務商appid,也可以是子商戶sub_appid
querys.put("secret","");  //和上面公眾號appid對應的公眾號的appKey
querys.put("code",code);
querys.put("grant_type","authorization_code");
String result = null;
try{
HttpResponse response = HttpUtils.doGet(host,path,"",headers,querys);
HttpEntity entity = response.getEntity();
if(entity != null){
result = EntityUtils.toString(entity,"UTF-8");
System.out.println(result);
}
JSONObject jsonObject = JSON.parseObject(result);
     //這一步就可以獲取到openid了,接下來你需要儲存下來,在呼叫微信統一下單介面時這個引數是必傳的
String openid = String.valueOf(jsonObject.get("openid"));
// TODO 儲存openid,可以存session,也可以放快取,甚至可以放資料庫,隨你一 一+
}catch (Exception e){
logger.info("oh !!! 獲取openid失敗");
throw new ServiceException("獲取openid失敗" + e.getMessage());
}
}

  這裡主要是通過上一步獲取到的code,通過http請求的方式呼叫微信後臺介面獲取一個特殊的網頁授權access_token,從中獲取到openid,詳見微信網頁授權。關於HttpUtils這個網上有很多的資源。獲取到openid後你需要先儲存起來(為什麼,因為作用域的問題),然後再去呼叫微信統一下單介面,生成預訂單,然後將相關引數返回給前端,由前端來發起支付請求,調起微信支付,這部分可以參考微信內H5調起支付

  以上兩步實際執行過程中是調來調去,中間經過了多次前後端互動,但是都是對使用者無感的。

3. 遇到問題

3.1 點選支付之後,沒反應

  這種問題其實不太好排查,因為是在手機端,不能f12(你懂的),後來是通過一堆alert將資訊列印出來,才找到了問題。直接說結果吧,是由於WeixinJSBridge這個微信瀏覽器的內建物件還未載入完成,導致其invoke方法不能呼叫,自然就不能調起微信支付了。解決的辦法就是搞了一個定時器,通過typeof window.WeixinJSBridge != "undefined"來判斷WeixinJSBridge是否載入進來,載入好了再調微信支付,程式碼如下:

var test = setInterval(function(){
            if (typeof window.WeixinJSBridge != "undefined"){
                WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
              "appId":json.appId,
              "timeStamp":json.timeStamp,
              "nonceStr":json.nonceStr,
              "package":json.package,
              "signType":json.signType,
              "paySign":json.paySign
            },
            function (resp) {
              if (resp.err_msg === 'get_brand_wcpay_request:ok') {
                Toast('微信支付成功')
                        _this.$router.go(-2);
                  } else if (resp.err_msg === 'get_brand_wcpay_request:cancel') {
                    Toast('使用者取消支付')
                            _this.$router.go(-1);
                  } else if (resp.err_msg === 'get_brand_wcpay_request:fail') {
                    Toast('網路異常,請重試')
                            _this.$router.go(-1);
                  }
                }
              );
                clearInterval(test)
            }
        }, 100)  

3.2 下單賬號與支付賬號不一致,請核實後再支付怎麼解決?

  原因:請求支付的 openid 和呼叫起支付時使用者的 openid 不一致

  解決辦法:傳入的 openid 可以實時獲取,獲取支付使用者的 openid 和呼叫微信統一下單介面時傳的 openid 需要保證一致,不一樣則會在微信支付介面出現上面的錯誤提示。

3.3 微信公眾號支付出現:“當前頁面的URL未註冊”

  點選支付按鈕出現“當前頁面的URL未註冊”的提示,這是由於未在商戶平臺中設定支付授權目錄導致的。登入微信商戶平臺-產品中心-開發配置,配置支付授權路徑。

3.4 {"errMsg":"chooseWXPay:fail"}

  出現這個問題目前發現有兩個原因可能導致:

  1. 未設定授權回撥域名,需要在開發 - 介面許可權 - 網頁服務 - 網頁帳號 - 網頁授權獲取使用者基本資訊”的配置選項中,修改授權回撥域名。

  2. 是否這個時候還在pc端的微信開發者工具上測試,有時這也會導致這個問題,那就想辦法在手機上測試吧。

3.5 get_brand_wcpay_request:fail

  錯誤如下,這個報錯資訊代表調起支付失敗,原因同3.3。

4. 總結

  因為之前有h5對接的經驗,所以呼叫統一下單介面是使用的之前程式碼(部分引數需要修改),主要是獲取openid和配置域名,整個過程出的問題大部分還是自己去找的,微信文件中關於這方面的並不多,其實寫的問題並不代表全部,但是整個過程都是自己一點一點做的。最後附上一張成功調起支付的圖。最後祝大家國慶節快樂。

5 參考文獻

微信支付getBrandWCPayRequest和wx.chooseWXPay有何區別

深究WeixinJSBridge未定義之因

 微信公眾號支付

相關文章