1. 程式人生 > >Java服務端支付功能模組--(二)微信支付

Java服務端支付功能模組--(二)微信支付

上一篇寫了關於支付寶支付的相關問題,本篇主要介紹微信支付的模組
微信支付可以大體分為5塊:1.網頁支付、2.App支付、3.H5外部瀏覽器支付、4.小程式支付、5.H5微信內部瀏覽器支付。這篇文章主要講解 2、3、4、5這4種支付。
整體難度來說:H5微信內部瀏覽器支付>H5外部瀏覽器支付>小程式支付>App支付>網頁支付。
程式語言為JAVA,採用的框架是SpringBoot+Mybatis.

控制層程式碼:
1.createPayOrder為三端統一調起的介面 只用appType和code區分出到底是呼叫哪個方法
2.因為H5外部瀏覽器支付需要獲取使用者的Ip地址所以主動去獲取了請求的ip地址
3.isPayEarnest只是區分是否定金

@RestController
@RequestMapping("/wechatpay")
public class WeChatPayController {
    @Autowired
    private WeChatPayService weChatPayService;
    /**
     * 發起統一下單
     * @param appType 0為H5端 1為小程式 2為app
     * @param billNo Code是微信通過授權後獲取的
     * @return Result
     */
    @RequestMapping(value = "/createPayOrder"
, method = RequestMethod.POST) public @ResponseBody Result createAppWeChatPayOrder(HttpServletRequest request, int appType, String billNo, String code, int isPayEarnest) { if (appType == 0) { if (code != null) { return weChatPayService.h5CreateInnerPayOrder(billNo,code,isPayEarnest); } // h5
return weChatPayService.h5CreatePayOrder(billNo, getIpAddr(request), isPayEarnest); } else if (appType == 1) { // 小程式 return weChatPayService.wxAppCreatePayOrder(code, billNo, isPayEarnest); } else { // app return weChatPayService.appCreatePayOrder(appType, billNo, isPayEarnest); } } /** * 獲取訪問者IP * * 在一般情況下使用Request.getRemoteAddr()即可,但是經過nginx等反向代理軟體後,這個方法會失效。 * * 本方法先從Header中獲取X-Real-IP,如果不存在再從X-Forwarded-For獲得第一個IP(用,分割), * 如果還不存在則呼叫Request .getRemoteAddr()。 * * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("X-Real-IP"); if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) { return ip; } ip = request.getHeader("X-Forwarded-For"); if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理後會有多個IP值,第一個為真實IP。 int index = ip.indexOf(','); if (index != -1) { return ip.substring(0, index); } else { return ip; } } else { return request.getRemoteAddr(); } } /** * 查詢訂單 * @param appType String * @param wxBillNo String * @param erpBillNo String * @return */ @RequestMapping(value = "/orderquery") public @ResponseBody Result orderQuery(WeChatPayOrderQueryRequest request) { return weChatPayService.orderQuery(request); } }

業務層程式碼:

```
@Service
public class WeChatPayServiceImpl implements WeChatPayService {
    private static Logger LOG = Logger.getLogger(WeChatPayServiceImpl.class);
    //微信支付配置引數
    @Autowired
    private WeChatPayProperties weChatPayProperties;
    private String orderDetail = "XXXX業務:";
    //支付業務
    @Autowired
    private PayService payService;

    @Override
    public Result wxAppCreatePayOrder(String code, String billNo, int isPayEarnest) {
        // 向微信請求獲取openId
        Map<String, Object> openIdMap = getOpenId(initWxAppOpenIdParmMap(code));
        if (openIdMap == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.GET_WECHATPAY_OPENID);
        }
        String openId = (String) openIdMap.get("openid");
        if (openId == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.GET_WECHATPAY_OPENID);
        }
        // String session_key = (String) openIdMap.get("session_key");

        // 獲取訂單資訊
        CarRentOrderPerson dto = payService.findOrder(billNo);
        //為了能多次吊起支付,調起支付訂單號要加上時間戳
        String newbillNo = payService.createNewOrderNo(billNo);
        //儲存吊起支付請求記錄,方便維護(可有可無)
        payService.savePayRequest(newbillNo, dto, PayTypeEnums.WXAPP.getIndex(), SourceTypeEnums.WXAPP.getIndex());
        // 初始化微信小程式統一下單引數
        Map<String, String> params = initWxAppCreatePayBillParams(openId, orderDetail + dto.getModelname(), newbillNo,isPayEarnest == 1 ? dto.getPrepaydeposit().multiply((new BigDecimal(100))): dto.getReceivableamount().multiply((new BigDecimal(100))));
        // 向微信平臺請求
        Result result = sendCreateOrderRequest(params);
        if (result.getCode() == Constants.FAILURE_CODE) {
            return result;
        }
        String prePayid = (String) result.getData();
        // 封裝prePayId到返回引數中,直接返回給前端,前端直接拿著這個就可以調起支付
        return new Result(Constants.SUCCESS_CODE, "", initWxAppCreatePayBillReturnParams(prePayid));
    }

    @Override
    public Result appCreatePayOrder(int appType, String billNo, int isPayEarnest) {
        // 判斷app型別 獲取相應appId
        String appId = weChatPayProperties.getAppAppid();
        if (!(appType == 3 || appType == 2)) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.NO_SUCH_APP_TYPE);
        }
        // 獲取預訂單
        CarRentOrderPerson dto = payService.findOrder(billNo);
        if (dto == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.BILLNO_CANOT_BE_NULL);
        }
        String newbillNo = payService.createNewOrderNo(billNo);
        //3為安卓 2為ios 記錄請求
        if (appType == 3) {
            payService.savePayRequest(newbillNo, dto, PayTypeEnums.WXAPP.getIndex(),
                    SourceTypeEnums.ANDROID.getIndex());
        } else {
            payService.savePayRequest(newbillNo, dto, PayTypeEnums.WXAPP.getIndex(), SourceTypeEnums.IOS.getIndex());
        }
        //建立請求資訊
        Map<String, String> params = initAppCreateOrderParams(appId, orderDetail + dto.getModelname(), newbillNo,
                isPayEarnest == 1 ? dto.getPrepaydeposit().multiply((new BigDecimal(100)))
                        : dto.getReceivableamount().multiply(new BigDecimal(100)));
        // 向微信請求
        Result result = sendCreateOrderRequest(params);
        if (result.getCode() == Constants.FAILURE_CODE) {
            return result;
        }
        String prePayid = (String) result.getData();

        return new Result(Constants.SUCCESS_CODE, "", initAppCreateOrderRetunParams(appId, prePayid));
    }

    @Override
    //H5外部調起支付
    public Result h5CreatePayOrder(String billNo, String ip, int isPayEarnest) {
        String sceenInfo = "{\"h5_info\": \"h5_info\" {\"type\": \"api\",  \"wap_url\": \"\",\"wap_name\": \"\"}}";
        // 獲取預訂單
        CarRentOrderPerson dto = payService.findOrder(billNo);
        if (dto == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.BILLNO_CANOT_BE_NULL);
        }
        String newbillNo = payService.createNewOrderNo(billNo);
        // 儲存請求引數
        payService.savePayRequest(newbillNo, dto, PayTypeEnums.WXAPP.getIndex(), SourceTypeEnums.H5.getIndex());
        Map<String, String> params = initH5CreateOrderParams(sceenInfo, orderDetail + dto.getModelname(), newbillNo, ip,
                isPayEarnest == 1 ? dto.getPrepaydeposit().multiply((new BigDecimal(100)))
                        : dto.getReceivableamount().multiply(new BigDecimal(100)));
        Result result = sendRequestToWxApi(params, weChatPayProperties.CREATE_PAY_ORDER_URL);
        if (result.getCode() == Constants.FAILURE_CODE) {
            return result;
        }
        @SuppressWarnings("unchecked")
        HashMap<String, String> returnHashMap = (HashMap<String, String>) result.getData();
        //prePayid為空請求失敗
        String prePayid = returnHashMap.get("prepay_id");
        if (prePayid == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.WECHATPAY_WXAPP_CREATE_ORDER_FAIL);
        }
        String url = returnHashMap.get("mweb_url");
        if (url == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.WECHATPAY_WXAPP_CREATE_ORDER_FAIL);
        }
        return new Result(Constants.SUCCESS_CODE, "", initH5CreatePayBillReturnParams(prePayid, url));

    }

    @Override
    //H5微信內部瀏覽器調起
    public Result h5CreateInnerPayOrder(String billNo, String code, int isPayEarnest) {
        // 獲取openId
        Map<String, Object> openIdMap = getH5OpenId(initH5OpenIdParmMap(code));
        if (openIdMap == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.GET_WECHATPAY_OPENID);
        }
        String openId = (String) openIdMap.get("openid");
        if (openId == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.GET_WECHATPAY_OPENID);
        }
        // 獲取預訂單
        CarRentOrderPerson dto = payService.findOrder(billNo);
        String newbillNo = payService.createNewOrderNo(billNo);
        payService.savePayRequest(newbillNo, dto, PayTypeEnums.WXAPP.getIndex(), SourceTypeEnums.H5.getIndex());
        // 初始化微信統一下單引數
        Map<String, String> params = initWxAppCreatePayBillParams(openId, orderDetail + dto.getModelname(), newbillNo,
                isPayEarnest == 1 ? dto.getPrepaydeposit().multiply((new BigDecimal(100)))
                        : dto.getReceivableamount().multiply((new BigDecimal(100))));
        // 向微信平臺請求
        Result result = sendCreateOrderRequest(params);
        if (result.getCode() == Constants.FAILURE_CODE) {
            return result;
        }
        String prePayid = (String) result.getData();
        // 封裝prePayId到返回引數中
        return new Result(Constants.SUCCESS_CODE, "", initH5InnerCreatePayBillReturnParams(prePayid));
    }

    @Override
    public Result orderQuery(WeChatPayOrderQueryRequest request) {
        String appid = "";
        if (StringUtils.isBlank(request.getErpBillNo()) && StringUtils.isBlank(request.getWxBillNo())) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.BILLNO_CANOT_BE_NULL);
        }
        if (2 == request.getAppType() || 3 == request.getAppType()) {
            appid = weChatPayProperties.getAppAppid();
        } else if (0 == request.getAppType()) {
            appid = weChatPayProperties.getH5Appid();
        } else if (1 == request.getAppType()) {
            appid = weChatPayProperties.getWxAppAppid();
        } else {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.NO_SUCH_APP_TYPE);
        }
        Map<String, String> params = initOrderQueryParams(appid, request.getWxBillNo(), request.getErpBillNo());
        // 向微信請求
        Result result = sendRequestToWxApi(params, weChatPayProperties.ORDERQUERY_URL);
        if (result.getCode() == Constants.FAILURE_CODE) {
            return result;
        }
        @SuppressWarnings("unchecked")
        HashMap<String, String> returnHashMap = (HashMap<String, String>) result.getData();
        return new Result(Constants.SUCCESS_CODE, "",
                WeChatPayOrderStatus.findNameById(returnHashMap.get("trade_state")));
    }

    private HashMap<String, String> initH5CreatePayBillReturnParams(String prePayid, String url) {
        HashMap<String, String> signMap = new HashMap<String, String>();
        signMap.put("appId", weChatPayProperties.getH5Appid());
        signMap.put("nonceStr", RandomUtils.generateString(31));
        signMap.put("package", "prepay_id=" + prePayid);
        signMap.put("signType", "MD5");
        signMap.put("timeStamp", new Date().getTime() + "");
        signMap.put("mweb_url", url);
        signMap.put("paySign", createSign(signMap));
        return signMap;
    }

    private HashMap<String, String> initH5InnerCreatePayBillReturnParams(String prePayid) {
        HashMap<String, String> signMap = new HashMap<String, String>();
        signMap.put("appId", weChatPayProperties.getH5Appid());
        signMap.put("nonceStr", RandomUtils.generateString(31));
        signMap.put("package", "prepay_id=" + prePayid);
        signMap.put("signType", "MD5");
        signMap.put("timeStamp", new Date().getTime() + "");
        signMap.put("paySign", createSign(signMap));
        return signMap;
    }

    /**
     * 初始化微信小程式統一下單返回引數
     * @param prePayid 預訂單編號
     * @return
     */
    private HashMap<String, String> initWxAppCreatePayBillReturnParams(String prePayid) {
        HashMap<String, String> signMap = new HashMap<String, String>();
        signMap.put("appId", weChatPayProperties.getWxAppAppid());
        signMap.put("nonceStr", RandomUtils.generateString(31));
        signMap.put("package", "prepay_id=" + prePayid);
        signMap.put("signType", "MD5");
        signMap.put("timeStamp", new Date().getTime() + "");
        signMap.put("paySign", createSign(signMap));
        return signMap;
    }

    /**
     * 初始化微信小程式統一下單引數
     * @param openId 商戶openId
     * @param body 商品資訊
     * @param billNo 訂單編號
     * @param totalFee 總價(單位分)
     * @return
     */
    private Map<String, String> initWxAppCreatePayBillParams(String openId, String body, String billNo,
            BigDecimal totalFee) {
        // 生成微信下單請求引數
        HashMap<String, String> params = new HashMap<String, String>();
        Instant now = Instant.now();
        Date nowDate = Date.from(now);
        // 支付有限時間為10分鐘
        now = now.plusSeconds(600);
        Date endDate = Date.from(now);
        params.put("appid", weChatPayProperties.getWxAppAppid());
        params.put("mch_id", weChatPayProperties.getWxAppMchId());
        params.put("notify_url", weChatPayProperties.getNotifyUrl());
        params.put("time_start", DateUtils.dateToString(nowDate, "yyyyMMddHHmmss"));
        params.put("time_expire", DateUtils.dateToString(endDate, "yyyyMMddHHmmss"));
        params.put("nonce_str", RandomUtils.generateString(31));
        params.put("body", body);
        /* + DateUtils.dateToString(nowDate, "yyyyMMddHHmm") */
        params.put("out_trade_no", billNo);
        params.put("total_fee", String.valueOf(totalFee.intValue()));
        // params.put("total_fee", String.valueOf(1));
        // 測試金額
        // params.put("total_fee", String.valueOf(new BigDecimal(100)));
        params.put("spbill_create_ip", "0.0.0.1");
        params.put("trade_type", "JSAPI");
        params.put("openid", openId);
        params.put("sign", createSign(params));
        return params;
    }

    /**
     * 向微信後臺傳送統一下單請求
     * 
     * @param 統一下單請求引數 params
     * @return
     */

    private Result sendCreateOrderRequest(Map<String, String> params) {
        Result result = sendRequestToWxApi(params, weChatPayProperties.CREATE_PAY_ORDER_URL);
        if (result.getCode() == Constants.FAILURE_CODE) {
            return result;
        }
        @SuppressWarnings("unchecked")
        HashMap<String, String> returnHashMap = (HashMap<String, String>) result.getData();

        String prePayid = returnHashMap.get("prepay_id");
        if (prePayid == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.WECHATPAY_WXAPP_CREATE_ORDER_FAIL);
        }
        return new Result(Constants.SUCCESS_CODE, "", prePayid);
    }

    /**
     * 向微信後臺傳送請求
     * @param 引數 params
     * @param 地址 url
     * @return
     */
    private Result sendRequestToWxApi(Map<String, String> params, String url) {
        String sendMsg = XMLUtils.mapToXml(params);
        if (sendMsg == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.WECHATPAY_REQUEST);
        }
        String retString = HttpUtils.postXML(url, sendMsg);
        if (retString == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.WECHATPAY_REQUEST);
        }
        Map<String, String> returnHashMap = XMLUtils.parseMap(retString);
        if (returnHashMap == null) {
            return new Result(Constants.FAILURE_CODE, ErrorMsg.WECHATPAY_REQUEST);
        }
        if (WeChatPayConstants.RETURN_CODE_FAIL.equals(returnHashMap.get("return_code"))) {
            return new Result(Constants.FAILURE_CODE, returnHashMap.get("return_msg"));
        }
        if (WeChatPayConstants.RETURN_CODE_FAIL.equals(returnHashMap.get("result_code"))) {
            return new Result(Constants.FAILURE_CODE, returnHashMap.get("err_code_des"));
        }
        return new Result(Constants.SUCCESS_CODE, "", returnHashMap);
    }

    /**
     * 初始化App統一下單請求引數
     * @return
     */
    private Map<String, String> initAppCreateOrderParams(String appid, String body, String billNo,
            BigDecimal totolFee) {
        HashMap<String, String> params = new HashMap<String, String>();
        Instant now = Instant.now();
        Date nowDate = Date.from(now);
        now = now.plusSeconds(600);
        Date endDate = Date.from(now);
        params.put("appid", appid);
        params.put("mch_id", weChatPayProperties.getMchId());
        params.put("notify_url", weChatPayProperties.getNotifyUrl());
        params.put("time_start", DateUtils.dateToString(nowDate, "yyyyMMddHHmmss"));
        params.put("time_expire", DateUtils.dateToString(endDate, "yyyyMMddHHmmss"));
        params.put("nonce_str", RandomUtils.generateString(31));
        params.put("body", body);
        params.put("out_trade_no", billNo);
        params.put("total_fee", String.valueOf(totolFee.intValue()));
        params.put("spbill_create_ip", "117.28.154.84");
        params.put("trade_type", "APP");
        params.put("sign", createSign(params));
        return params;
    }

    /**
     * 初始化App統一下單請求引數
     * @return
     */
    private Map<String, String> initH5CreateOrderParams(String sceneInfo, String body, String billNo, String ip,
            BigDecimal totolFee) {
        HashMap<String, String> params = new HashMap<String, String>();
        Instant now = Instant.now();
        Date nowDate = Date.from(now);
        now = now.plusSeconds(600);
        Date endDate = Date.from(now);
        params.put("appid", weChatPayProperties.getH5Appid());
        params.put("mch_id", weChatPayProperties.getMchId());
        params.put("notify_url", weChatPayProperties.getNotifyUrl());
        params.put("time_start", DateUtils.dateToString(nowDate, "yyyyMMddHHmmss"));
        params.put("time_expire", DateUtils.dateToString(endDate, "yyyyMMddHHmmss"));
        params.put("nonce_str", RandomUtils.generateString(31));
        params.put("body", body);
        params.put("out_trade_no", billNo);
        params.put("total_fee", String.valueOf(totolFee.intValue()));
        params.put("spbill_create_ip", ip);
        params.put("trade_type", "MWEB");
        params.put("scene_info", sceneInfo);
        params.put("sign", createSign(params));
        return params;
    }

    /**
     * 初始化App統一下單請求引數
     * @return
     */
    private Map<String, String> initAppCreateOrderRetunParams(String appId, String prepayid) {
        HashMap<String, String> signMap = new HashMap<String, String>();
        signMap.put("appid", appId);
        signMap.put("partnerid", weChatPayProperties.getMchId());
        signMap.put("prepayid", prepayid);
        signMap.put("package", "Sign=WXPay");
        signMap.put("noncestr", RandomUtils.generateString(31));
        String timestamp = String.valueOf(new Date().getTime());
        signMap.put("timestamp", timestamp.substring(0, timestamp.length() - 3));
        signMap.put("sign", createSign(signMap));
        return signMap;
    }

    public Map<String, String> initWxAppOpenIdParmMap(String code) {
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("appid", weChatPayProperties.getWxAppAppid());
        params.put("secret", weChatPayProperties.getWxAppAppsecret());
        params.put("js_code", code);
        params.put("grant_type", "authorization_code");
        return params;
    }

    public Map<String, String> initH5OpenIdParmMap(String code) {
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("appid", weChatPayProperties.getH5Appid());
        params.put("secret", weChatPayProperties.getH5Appsecret());
        params.put("code", code);
        params.put("grant_type", "authorization_code");
        return params;
    }

    /**
     * 獲取openID
     * @param params
     * @return
     */
    public Map<String, Object> getOpenId(Map<String, String> params) {
        Map<String, Object> map = null;
        String retStr = HttpUtils.sendPost(weChatPayProperties.GET_OPEN_ID_URL, params);
        if (retStr == null) {
            return null;
        }
        map = stringToHashMap(retStr);
        if (map == null || map.get("openid") == null || map.get("session_key") == null) {
            return null;
        }
        return map;
    }

    /**
     * 獲取H5openID
     * @param params
     * @return
     */
    public Map<String, Object> getH5OpenId(Map<String, String> params) {
        Map<String, Object> map = null;
        String retStr = HttpUtils.sendPost(weChatPayProperties.GET_H5_OPEN_ID_URL, params);
        if (retStr == null) {
            return null;
        }
        map = stringToHashMap(retStr);
        if (map == null || map.get("openid") == null) {
            return null;
        }
        return map;
    }

    private boolean checkSign(Map<String, String> map) {
        String signFromAPIResponse = map.get("sign").toString();
        if (signFromAPIResponse == "" || signFromAPIResponse == null) {
            return false;
        }
        // 清掉返回資料物件裡面的Sign資料(不能把這個資料也加進去進行簽名),然後用簽名演算法進行簽名
        map.remove("sign");
        // 將API返回的資料根據用簽名演算法進行計算新的簽名,用來跟API返回的簽名進行比較
        String signForAPIResponse = createSign(map);
        if (!signForAPIResponse.equals(signFromAPIResponse)) {
            return false;
        }
        return true;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> stringToHashMap(String retStr) {
        ObjectMapper mapper = new ObjectMapper();
        HashMap<String, Object> map = null;
        try {
            map = mapper.readValue(retStr, HashMap.class);
        } catch (JsonParseException e) {
            LOG.info(e.toString());
            return null;
        } catch (JsonMappingException e) {
            LOG.info(e.toString());
            return null;
        } catch (IOException e) {
            LOG.info(e.toString());
            return null;
        }
        return map;
    }

    // 建立簽名
    private String createSign(Map<String, String> params) {
        if (params.isEmpty()) {
            return null;
        }
        StringBuilder sb = new StringBuilder();

        params.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey())).forEachOrdered(e -> {
            sb.append(e.getKey() + "=" + e.getValue() + "&");
        });
        String key = weChatPayProperties.getWxAppAppid().equals(params.get("appid"))
                || weChatPayProperties.getWxAppAppid().equals(params.get("appId")) ? weChatPayProperties.getWxAppKey()
                        : weChatPayProperties.getKey();
        sb.append("key=" + key);
        String md5 = MD5Helper.getMd5(sb.toString());
        return md5.toUpperCase();
    }

    private Map<String, String> initOrderQueryParams(String appid, String wxBillNo, String erpBillNo) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", weChatPayProperties.getWxAppAppid().equals(appid) ? weChatPayProperties.getWxAppMchId()
                : weChatPayProperties.getMchId());
        params.put("nonce_str", RandomUtils.generateString(31));
        if (StringUtils.isNotEmpty(wxBillNo)) {
            params.put("transaction_id", wxBillNo);
        } else {
            // 根據舊單號查詢
            params.put("out_trade_no", payService.getNewOrderNoByOrderNo(erpBillNo));
        }
        params.put("sign", createSign(params));
        return params;
    }



    // 獲取openID
    public Map<String, Object> getOpenId(String code) {
        Map<String, Object> map = null;
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", weChatPayProperties.getWxAppAppid());
        params.put("secret", weChatPayProperties.getWxAppAppsecret());
        params.put("js_code", code);
        params.put("grant_type", "authorization_code");
        String retStr = HttpUtils.sendPost(weChatPayProperties.GET_OPEN_ID_URL, params);
        if (retStr == null) {
            return null;
        }
        map = stringToHashMap(retStr);
        if (map == null || map.get("openid") == null || map.get("session_key") == null) {
            return null;
        }
        return map;
    }
@Configuration
@PropertySource("classpath:pay.properties")
public class WeChatPayProperties {
    @Value("${WeChatPay.wxapp.appid}")
    private String wxAppAppid;

    @Value("${WeChatPay.wxapp.appsecret}")
    private String wxAppAppsecret;

    @Value("${WeChatPay.app.appid}")
    private String appAppid;

    @Value("${WeChatPay.app.appsecret}")
    private String appAppsecret;

    @Value("${WeChatPay.h5.appid}")
    private String h5Appid;

    @Value("${WeChatPay.h5.appsecret}")
    private String h5Appsecret;

    @Value("${WeChatPay.mch_id}")
    private String mchId;

    @Value("${WeChatPay.key}")
    private String key;

    @Value("${WeChatPay.notify_url}")
    private String notifyUrl;

    @Value("${WeChatPay.wxappmch_id}")
    private String wxAppMchId;

    @Value("${WeChatPay.wxappkey}")
    private String wxAppKey;

    public final String CREATE_PAY_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    public final String ORDERQUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";

    public final String GET_OPEN_ID_URL = "https://api.weixin.qq.com/sns/jscode2session";

    public final String GET_H5_OPEN_ID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

}

總體來說,微信支付其實整體流程基本差不多,除了h5外部瀏覽器最後是跳轉頁面,基本都是服務端做好請求引數,客戶端返回引數直接就吊起支付。雖然最後程式碼看起來簡單,但這個微信支付搗鼓了我2個多月才斷斷續續做完,我總結了下主要是以下幾個原因:

 1. 微信公眾平臺,開放平臺,商戶平臺3者的關係特別凌亂。首先像微信小程式的開發是需要在開放平臺處理的;接下來H5的微信內部吊起支付是需要通過繫結公眾號才能實現,需要通過公眾平臺來處理;最令人驚訝的是app支付是通過在開放平臺開通許可權,然後用申請到的商戶號進入商戶平臺處理。跟支付寶只需要在一個開發者中心處理完所有的業務來說,這是騰訊很坑的一點,需要閱讀很多文件,因為沒有人有精力去研究這三個平臺的關聯。
 2. 騰訊缺少線上客服。支付寶除錯遇到問題可以線上向支付寶的技術客服詢問,可以很高效地解決問題,相反像騰訊這種雖然文件齊全,但是隻能發郵件來問問題我認為效率太低了。因為經常會遇到一些可能客服解決只需要·半分鐘的事情,我們卻花了3個小時去檢視騰訊的文件,這是十分低效的。
 3. 微信支付文件齊全但卻缺少程式碼。微信支付幾乎沒有程式碼可以使用,都是開發者通過看他的流程圖然後自己寫程式碼。我認為微信支付既然都有那麼多商戶接入,為什麼不做一些比較好的程式碼demo提高接入介面的效率呢?

在這個開發過程中也遇到一些坑:

  1. App支付必須要釋出到正式環境才能調起。
  2. H5外部客戶端調起支付同樣也必須釋出到正式環境才能調起。
  3. H5外部客戶端支付是在商戶平臺申請開通的,開通後appid是和App支付的appid一致(我的商戶號是通過申請App支付才能生成的),商戶號也是和app支付一致
  4. 各個客戶端調起支付的請求引數是不一樣的,需要逐個確認。
  5. app支付的時間戳是10位的,但是其他支付的時間戳長度沒有限制。
  6. 調起支付的金額一定記得要處理。