java服务端微信商户企业付款提现到个人银行卡实现

/**
     * 微信-企业付款到银行卡接口
     * @param partner_trade_no    商户订单号
     * @param enc_bank_no        收款方银行卡号
     * @param enc_true_name        收款方用户名
     * @param bank_code            收款方开户行代码
     * @param amount            付款金额:RMB分(支付总额,不含手续费);每笔按付款金额收取手续费,按金额0.1%收取,最低1元,最高25元;amount字段必填,必须大于0或者小于等于2亿
     * @return
     */
    public static AjaxJson payBank(String partner_trade_no, 
            String enc_bank_no, String enc_true_name, String bank_code, String amount) {
        logger.debug("进入企业付款到银行卡"+partner_trade_no+"||"+enc_bank_no+"||"+enc_true_name+"||"+bank_code+"||"+amount);
        AjaxJson j = new AjaxJson();
        try {
            MyConfig config = new MyConfig();
            WXPay wxpay = new WXPay(config, SignType.MD5);
            Map<String, String> data = new HashMap<String, String>();
            data.put("partner_trade_no", partner_trade_no);
            //收款方银行卡号RSA
            data.put("enc_bank_no", getRsaEncryptStr(enc_bank_no, RSA_PUBLIC_KEY));//RSA加密公钥(PKCS8格式)
            //收款方用户名RSA
            data.put("enc_true_name", getRsaEncryptStr(enc_true_name, RSA_PUBLIC_KEY));//RSA加密公钥(PKCS8格式)
            //收款方开户行
            data.put("bank_code", bank_code);
            data.put("amount", amount);
            //订单备注
            data.put("desc", "用户提现"); 
            Map<String, String> resp = wxpay.payBank(data);
            String msg = resp.get("return_code") + "=" + resp.get("return_msg");
            logger.debug("企业付款到银行卡返回结果:"+msg);
            if(resp.get("result_code") != null || resp.get("err_code_des") != null){
                msg += (";" + resp.get("result_code") + "=" + resp.get("err_code_des"));
            }
            msg += (";payment_no=" + resp.get("payment_no"));
            j.setMsg("提现结果:" + msg);
            logger.info("调用微信-企业付款到银行卡接口结果:{}", resp);
            if("SUCCESS".equals(resp.get("return_code"))
                    && "SUCCESS".equals(resp.get("result_code"))
                    && resp.get("partner_trade_no") != null
                    && resp.get("partner_trade_no").equals(partner_trade_no)){
                j.setSuccess(true);
            } else {
                j.setSuccess(false);
                //注意:当状态为FAIL时,存在业务结果未明确的情况,所以如果状态为FAIL,
                //请务必通过查询接口确认此次付款的结果(关注错误码err_code字段)。
                //如果要继续进行这笔付款,请务必用原商户订单号和原参数来重入此接口。
                if("SUCCESS".equals(resp.get("return_code"))
                        && "FAIL".equals(resp.get("result_code"))){
                    j.setErrorCode("2");
                }
            }
        } catch (Exception e) {
            j.setSuccess(false);
            j.setMsg("微信-企业付款到银行卡处理异常");
            logger.error("调用微信-企业付款到银行卡接口处理异常{}", e);
        }
        return j;
    }

public static final String MMPAYSPTRANS_PAY_BANK_URL_SUFFIX = "/mmpaysptrans/pay_bank";

 /**
     * 企业付款到银行卡
     * @param reqData
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     * @throws Exception
     */
    public Map<String, String> payBank(Map<String, String> reqData) throws Exception {
        String respXml = this.requestWithCert(WXPayConstants.MMPAYSPTRANS_PAY_BANK_URL_SUFFIX, this.fillRequestData3(reqData), config.getHttpConnectTimeoutMs(), this.config.getHttpReadTimeoutMs());
        return this.processResponseXml2(respXml);
    }

 /**
     * 需要证书的请求
     * @param urlSuffix String
     * @param reqData 向wxpay post的请求数据  Map
     * @param connectTimeoutMs 超时时间,单位是毫秒
     * @param readTimeoutMs 超时时间,单位是毫秒
     * @return API返回数据
     * @throws Exception
     */
    public String requestWithCert(String urlSuffix, Map<String, String> reqData,
                                  int connectTimeoutMs, int readTimeoutMs) throws Exception {
        String msgUUID= reqData.get("nonce_str");
        String reqBody = WXPayUtil.mapToXml(reqData);

        String resp = this.wxPayRequest.requestWithCert(urlSuffix, msgUUID, reqBody, connectTimeoutMs, readTimeoutMs, this.autoReport);
        return resp;
    }

/**
     * 可重试的,双向认证的请求
     * @param urlSuffix
     * @param uuid
     * @param data
     * @param connectTimeoutMs
     * @param readTimeoutMs
     * @return
     */
    public String requestWithCert(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean autoReport) throws Exception {
        return this.request(urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, true, autoReport);
    }

  private String request(String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert, boolean autoReport) throws Exception {
        Exception exception = null;
        long elapsedTimeMillis = 0;
        long startTimestampMs = WXPayUtil.getCurrentTimestampMs();
        boolean firstHasDnsErr = false;
        boolean firstHasConnectTimeout = false;
        boolean firstHasReadTimeout = false;
        IWXPayDomain.DomainInfo domainInfo = config.getWXPayDomain().getDomain(config);
        if(domainInfo == null){
            throw new Exception("WXPayConfig.getWXPayDomain().getDomain() is empty or null");
        }
        try {
            String result = requestOnce(domainInfo.domain, urlSuffix, uuid, data, connectTimeoutMs, readTimeoutMs, useCert);
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, null);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout);
            return result;
        }
        catch (UnknownHostException ex) {  // dns 解析错误,或域名不存在
            exception = ex;
            firstHasDnsErr = true;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayUtil.getLogger().warn("UnknownHostException for domainInfo {}", domainInfo);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout
            );
        }
        catch (ConnectTimeoutException ex) {
            exception = ex;
            firstHasConnectTimeout = true;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayUtil.getLogger().warn("connect timeout happened for domainInfo {}", domainInfo);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout
            );
        }
        catch (SocketTimeoutException ex) {
            exception = ex;
            firstHasReadTimeout = true;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayUtil.getLogger().warn("timeout happened for domainInfo {}", domainInfo);
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout);
        }
        catch (Exception ex) {
            exception = ex;
            elapsedTimeMillis = WXPayUtil.getCurrentTimestampMs()-startTimestampMs;
            WXPayReport.getInstance(config).report(
                    uuid,
                    elapsedTimeMillis,
                    domainInfo.domain,
                    domainInfo.primaryDomain,
                    connectTimeoutMs,
                    readTimeoutMs,
                    firstHasDnsErr,
                    firstHasConnectTimeout,
                    firstHasReadTimeout);
        }
        config.getWXPayDomain().report(domainInfo.domain, elapsedTimeMillis, exception);
        throw exception;
    }

   /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }

  /**
     * 不需要appid的请求使用,且不需要sign_type参与签名的
     * @param reqData
     * @return
     * @throws Exception
     */
    public Map<String, String> fillRequestData3(Map<String, String> reqData) throws Exception {
        reqData.put("mch_id", WxPayConfig.MCHID);
        reqData.put("nonce_str", WXPayUtil.generateNonceStr());
        reqData.put("sign", WXPayUtil.generateSignature(reqData, WxPayConfig.KEY, this.signType));
        return reqData;
    }

/**
     * 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
     *
     * @param data 待签名数据
     * @param key API密钥
     * @param signType 签名方式
     * @return 签名
     */
    public static String generateSignature(final Map<String, String> data, String key, SignType signType) throws Exception {
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        if (SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        }
        else if (SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        }
        else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }

  /**
     * 处理 HTTPS API返回数据,转换成Map对象。return_code为SUCCESS时,无需验证签名的。
     * @param xmlStr API返回的XML格式数据
     * @return Map类型数据
     * @throws Exception
     */
    public Map<String, String> processResponseXml2(String xmlStr) throws Exception {
        String RETURN_CODE = "return_code";
        String return_code;
        Map<String, String> respData = WXPayUtil.xmlToMap(xmlStr);
        if (respData.containsKey(RETURN_CODE)) {
            return_code = respData.get(RETURN_CODE);
        }
        else {
            throw new Exception(String.format("No `return_code` in XML: %s", xmlStr));
        }

        if (return_code.equals(WXPayConstants.FAIL)) {
            return respData;
        }
        else if (return_code.equals(WXPayConstants.SUCCESS)) {
            return respData;
        }
        else {
            throw new Exception(String.format("return_code value %s is invalid in XML: %s", return_code, xmlStr));
        }
    }

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值