微信支付的坑

環境/框架:windows7+Intellij Idea+jdk8+tomcat+Spring

支付類型:掃碼支付模式二(統一下單接口)

只是希望少一點人踩同樣坑的列表(未完):

  1. 測試接口問題:4月初開始寫的時候看到開發文檔的最佳實踐-支付驗收一節,一直以爲測試時需要走先沙盒路徑,結果報的錯和這個帖子(微信支付公衆號支付提示驗證簽名失敗、報錯“請調用getsignkey生成沙箱密鑰”)一模一樣,查完參數又查簽名,始終找不到原因。後來百度了幾篇教程才突然發現:用統一下單接口的都沒有提到沙盒……馬上試了下正式的unifiedorder接口、果然一次就拿到二維碼鏈接……再後來總算給賬號讓登公衆平臺,結果找了半天似乎也只能配置掃碼支付模式一的測試url。所以雖然明知會污染對賬單,爲了趕進度、目前還是直接拿正式付款的賬號和接口進行開發測試的。

  2. appid:微信支付需要提前申請商戶號mch_id和公衆賬號IDappid,這是所有請求的必帶參數,商戶號無歧義,但一個企業可能會同時有企業號corpid和公衆號openid,樣子也都是以 wx 2個字符開頭,填錯可能報“mchid和appid不符”。

  3. http返回沒有消息體:記得檢查是否漏了必填參數。微信只要正常收到請求,http response status code就都是200 ok 這一點上也真是省力…、client只需解析body string。但遇到一次消息體爲null的異常,檢查到最後發現是因爲讀取數據庫賬號id表時出了問題,漏掉了appid根本沒寫進xml。

  4. 簽名校驗問題
    微信支付文檔中關於簽名計算流程寫的挺明白的,因爲是Java沒有直接例程、抄起來就自己寫了、結果還是遇到了一下細節問題:

    1. Checksum需要用UTF-8:開始時圖省力調用了別人寫的md5 util方法、結果裏面使用了系統默認編碼(tomcat在windows下是GBK),而用Idea直接跑static main時用的卻是utf-8(猜測是自動檢測了源文件編碼)。最終現象是明明單元測試時sign生成方法和微信的簽名驗證工具一致、一放到tomcat跑起來就簽名失敗、調試很久才找到原因。所以後來整理了下相關的utf-8配置問題、寫在了這篇

    2. 除NULL外,值爲空字串的參數也要剔除:微信返回的xml裏可能包含whitespace node,比如<sub_mch_id></sub_mch_id>,而我當時使用的默認的Jackson xml mapper將它解析成了length=0的空string "",拼接signStr時就變成了...&sub_mch_id=&...,導致校驗失敗。

    3. 網上說的“一定要確認參數名稱和大小寫”確實要注意,但另外一些“有中文就不行”、“凡string都要包在CDATA內”等,似乎是沒什麼大關係的。

    4. 補一段簽名計算代碼吧:

      public static String computeSign(Map<String, String> params, String key) {
          if (params == null || key == null)
              throw new NullPointerException("param map or key is null");         
          String signSrc = params.entrySet().stream()
              .filter(e -> e.getValue() != null)
              .filter(e -> !(e.getValue().trim().length() == 0))
              .filter(e -> !"sign".equals(e.getKey()))
              .map(e -> e.getKey() + "=" + e.getValue())
              .sorted()
              .collect(Collectors.joining("&"));
          signSrc = signSrc + "&key=" + key;
          String sign = null;
          try {
              sign = compute("MD5", signSrc, UTF_8).toUpperCase();
          } catch (NoSuchAlgorithmException e) {
              LOGGER.error("computeSign", e);
          }
          return sign;
      }  
      // checksum
      public static String compute(String checksumAlg, String src, Charset charSet)
      throws NoSuchAlgorithmException {
          final byte[] hashBytes = MessageDigest.getInstance(checksumAlg).digest(src.getBytes(charSet));
          return DatatypeConverter.printHexBinary(hashBytes);
      }
  5. 退款

    1. 商戶證書相關寫在了這篇
    2. 返回結果格式 :申請退款和查詢退款的返回結果裏有很多out_refund_no_$n(甚至coupon_refund_fee_$n_$m)這樣帶後綴的參數,初次看文檔時整個人也是“$$”了。直到測試時收到的實際結果……我並沒有很多年的經驗裏是第一次看到竟然有人會想到用改xml標籤名的方式來表示一個列表的(設計一個複雜一點的element、甚至直接用attribute不好嗎)。(總之因爲強烈地repel)退款這塊數據並沒有很好處理……這裏只是附貼一下結果樣例(2017/04,省略了一些通用項):
      申請退款返回:

      <xml>
          <transaction_id><![CDATA[40007***76642]]></transaction_id>  
          <out_trade_no><![CDATA[1492844-5410-000-002-929]]></out_trade_no>
          <out_refund_no><![CDATA[1493007-4988-0018]]></out_refund_no>
          <refund_id><![CDATA[50000***05548]]></refund_id>  
          <refund_channel><![CDATA[]]></refund_channel>  
          <refund_fee>1</refund_fee>  
          <coupon_refund_fee>0</coupon_refund_fee>  
          <total_fee>2</total_fee>  
          <cash_fee>2</cash_fee>  
          <coupon_refund_count>0</coupon_refund_count>  
          <cash_refund_fee>1</cash_refund_fee> 
      </xml>

      查詢退款返回:

      <xml>
          <cash_fee><![CDATA[2]]></cash_fee>
          <out_refund_no_0><![CDATA[1493007-4988-0018]]></out_refund_no_0>
          <out_trade_no><![CDATA[1492844-5410-000-002-929]]></out_trade_no>
          <refund_account_0><![CDATA[REFUND_SOURCE_UNSETTLED_FUNDS]]></refund_account_0>
          <refund_channel_0><![CDATA[ORIGINAL]]></refund_channel_0>
          <refund_count>1</refund_count>
          <refund_fee>1</refund_fee>
          <refund_fee_0>1</refund_fee_0>
          <refund_id_0><![CDATA[50000***05548]]></refund_id_0>
          <refund_recv_accout_0><![CDATA[支付用戶的零錢]]></refund_recv_accout_0>
          <refund_status_0><![CDATA[SUCCESS]]></refund_status_0>
          <refund_success_time_0><![CDATA[2017-04-24 12:18:26]]></refund_success_time_0>
          <total_fee><![CDATA[2]]></total_fee>
          <transaction_id><![CDATA[40007***76642]]></transaction_id>
      </xml>
  6. 開發文檔閱讀:不是很重要但是真挺讓人抓狂的一點,微信支付開發文檔網站風格設計完全一樣、內容卻像是根據不同支付方式各自維護的,比如請比較:
    安全規範:
    https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
    https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
    統一下單:
    https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
    https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
    百度來的真的很容易走錯啊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章