package com.ynxbd.wx.wxfactory; import com.alibaba.fastjson.JSONObject; import com.ynxbd.common.bean.enums.MerchantEnum; import com.ynxbd.common.bean.pay.Bill; import com.ynxbd.common.bean.pay.Order; import com.ynxbd.common.helper.cache.TextCache; import com.ynxbd.common.helper.common.*; import com.ynxbd.common.result.ResultEnum; import com.ynxbd.common.result.ServiceException; import com.ynxbd.wx.config.WeChatConfig; import com.ynxbd.wx.wxfactory.bean.WxPayNotify; import com.ynxbd.wx.wxfactory.bean.refund.WxRefundItem; import com.ynxbd.wx.wxfactory.bean.refund.WxRefundQueryRoot; import com.ynxbd.wx.wxfactory.utils.WxSignHelper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import weixin.popular.api.PayMchAPI; import weixin.popular.bean.paymch.*; import weixin.popular.client.LocalHttpClient; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; import static weixin.popular.api.PayMchAPI.payRefundquery; /** * @Author wsq * @Date 2020/8/17 15:26 * @Copyright @ 2020 云南新八达科技有限公司 All rights reserved. */ @Slf4j public class WxPayHelper { // 证书名称 public static final String API_CLIENT_CERT_NAME = "apiclient_cert.p12"; public static final String ALL = "ALL"; // 查询全部订单 // 交易成功返回内容 public static final String REVOKED = "REVOKED"; // 订单已撤销 public static final String PAY_ERROR = "PAYERROR"; // 支付失败 public static final String USER_PAYING = "USERPAYING"; // 用户支付中 public static final String REFUND = "REFUND"; // 发生过退款 // 失败 public static final String FAIL = "FAIL"; // 成功 public static final String SUCCESS = "SUCCESS"; public static final String OK = "OK"; // 排重 public static final RepeatKeyHelper KEYS = new RepeatKeyHelper(60); static { try { String mchId = WeChatConfig.MCH_ID; if (!ObjectUtils.isEmpty(mchId)) { String clazzPath = Objects.requireNonNull(WxPayHelper.class.getClassLoader().getResource("")).getPath(); if (clazzPath == null) { log.info("【微信】未找到证书"); } LocalHttpClient.initMchKeyStore(mchId, clazzPath + API_CLIENT_CERT_NAME); } } catch (Exception e) { ErrorHelper.println(e); } } /** * 微信支付调用及通知 * * @param request 请求 * @return 微信支付信息 */ public static WxPayNotify payNotify(HttpServletRequest request) throws ServiceException { try { // 转换数据对象 Map paramsMap = WxSignHelper.getReqXmlParamsMap(request); log.info("【微信回调】map={}", paramsMap); if (paramsMap == null) { throw new ServiceException("【微信】回调通知下单信息返回错误"); } // 签名验证 if (!WxSignHelper.validateSign(paramsMap, WeChatConfig.MCH_KEY)) { // 验证未通过,通知支付失败 throw new ServiceException("【微信】回调通知签名验证未通过!"); } String json = JsonHelper.toJsonString(paramsMap); if ("".equals(json)) { throw new ServiceException("【微信】回调通知下单信息为空"); } WxPayNotify notifyInfo = JsonHelper.parseObject(json, WxPayNotify.class); log.info("【微信回调】notifyInfo={}", notifyInfo); if (notifyInfo == null) { throw new ServiceException("【微信】回调通知下单信息转换失败"); } String openid = notifyInfo.getOpenid(); String outTradeNo = notifyInfo.getOutTradeNo(); String bankTransNo = notifyInfo.getTransactionId(); BigDecimal totalFee = notifyInfo.getTotalFee();// 金额 String timeEnd = notifyInfo.getTimeEnd(); if (openid == null || outTradeNo == null || bankTransNo == null || totalFee == null || timeEnd == null) { throw new ServiceException(String.format("【微信】下单信息返回错误 outTradeNo={%s}", outTradeNo)); } // 已处理 去重 if (KEYS.isContainsKey(bankTransNo)) { throw new ServiceException(ResultEnum.PAY_NOTIFY_REPEAT, String.format("【微信】[重复请求]下单信息去重 outTradeNo={%s}", outTradeNo)); } String info = WxSignHelper.getMapInfo(paramsMap); Map dateMap = WxSignHelper.getDateMap(timeEnd); notifyInfo.setPayDate(dateMap.get(WxSignHelper.DATE_KEY)); notifyInfo.setPayTime(dateMap.get(WxSignHelper.TIME_KEY)); notifyInfo.setPayInfo(info); notifyInfo.setTotalFee(totalFee.movePointLeft(2)); return notifyInfo; } catch (Exception e) { if (e instanceof ServiceException) { throw e; } ErrorHelper.println(e); throw new ServiceException(String.format("【微信】支付通知异常:[%s]", e.getMessage())); } } /** * 商户创建订单 * * @param merchantEnum 枚举 * @param title 商品标题 * @param totalFee 订单金额 * @param outTradeNo 订单号 * @param notifyType 回调类型 * @param ip ip地址 * @param openid openid * @param patientId 患者id * @return 成功返回 json, 否则返回null */ public static JSONObject createOrder(MerchantEnum merchantEnum, String title, String totalFee, String outTradeNo, String notifyType, String ip, String openid, String patientId) { try { if (ObjectUtils.isEmpty(openid) || ObjectUtils.isEmpty(outTradeNo) || merchantEnum == null || totalFee == null || notifyType == null) { log.info("【微信】下单参数缺失"); return null; } // 金额转换 String cents; try { cents = new BigDecimal(totalFee).movePointRight(2).toString(); // 单位分 } catch (Exception e) { ErrorHelper.println(e); log.info("【微信】统一下单金额转换异常"); return null; } String appId = WeChatConfig.APP_ID; String mchId = WeChatConfig.MCH_ID; String mchKey = WeChatConfig.MCH_KEY; String nonce = UUID.randomUUID().toString().replace("-", ""); Unifiedorder unifiedorder = new Unifiedorder(); unifiedorder.setAppid(appId); unifiedorder.setMch_id(mchId); unifiedorder.setNonce_str(nonce); unifiedorder.setOpenid(openid); unifiedorder.setOut_trade_no(outTradeNo); unifiedorder.setBody(title); unifiedorder.setTotal_fee(cents); // 金额分 unifiedorder.setSpbill_create_ip(ip); unifiedorder.setNotify_url(WeChatConfig.getBaseUrl() + merchantEnum.NOTIFY_URL); unifiedorder.setTrade_type("JSAPI"); // 额外参数 unifiedorder.setAttach(notifyType); log.info(appId + "," + mchId + "," + nonce + "," + title + "," + outTradeNo + "," + totalFee + "," + ip + "," + merchantEnum.NOTIFY_URL); // 微信统一下单 UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey); log.info("【微信】统一下单返回:{}", JsonHelper.toJsonString(unifiedorderResult)); if (unifiedorderResult == null) { log.info("【微信】统一下单失败"); return null; } String result_code = unifiedorderResult.getResult_code(); // 下单是否成功 Boolean sign_status = unifiedorderResult.getSign_status(); if (!SUCCESS.equals(result_code) || sign_status == null || !sign_status) { log.info("【微信】统一下单返回异常信息:sign_status={}, err_code=[ {} ], err_code_des=[ {} ]", sign_status, unifiedorderResult.getErr_code(), unifiedorderResult.getErr_code_des()); return null; } String json = weixin.popular.util.PayUtil.generateMchPayJsRequestJson(unifiedorderResult.getPrepay_id(), appId, mchKey); if (json != null) { return JsonHelper.parseObject(json); } } catch (Exception e) { log.error("【微信】统一下单异常信息:" + e.getMessage()); ErrorHelper.println(e); } return null; } /** * 微信退款 * * @param outTradeNo 订单号 * @param outRefundNo 退款订单号(HIS订单号) * @param refundMoney 本次退款金额 * @param totalFee 订单总金额 * @param desc 退款描述 * @return 退款信息 */ public static Order refund(String outTradeNo, String outRefundNo, BigDecimal refundMoney, BigDecimal totalFee, String desc) { Order order = new Order(); try { int feeCents; int totalFeeCents; feeCents = refundMoney.movePointRight(2).intValue(); // 单位分 totalFeeCents = totalFee.movePointRight(2).intValue(); if (feeCents == 0 || totalFeeCents == 0) { order.setErrorMsg(ResultEnum.REFUND_MONEY_IS_ZERO.message); return order; } log.info("【微信】退款开始 outTradeNo={}, refundMoney={}, totalFee={}", outTradeNo, feeCents, totalFeeCents); SecapiPayRefund refund = new SecapiPayRefund(); refund.setAppid(WeChatConfig.APP_ID); refund.setMch_id(WeChatConfig.MCH_ID); refund.setNonce_str(UUID.randomUUID().toString().replace("-", "")); refund.setSign_type("MD5"); refund.setOut_trade_no(outTradeNo); refund.setOut_refund_no(outRefundNo); refund.setRefund_fee(feeCents); refund.setTotal_fee(totalFeeCents); refund.setRefund_fee_type("CNY"); refund.setRefund_account("REFUND_SOURCE_UNSETTLED_FUNDS"); if (desc.length() > 80) { desc = desc.substring(0, 80); } desc = TextCache.textFilter(desc); refund.setRefund_desc(desc); SecapiPayRefundResult refundResult; try { refundResult = PayMchAPI.secapiPayRefund(refund, WeChatConfig.MCH_KEY); if (refundResult == null) { throw new Exception(ResultEnum.INTERFACE_WX_INVOKE_ERROR.message); } } catch (Exception e) { log.error("【微信】退款接口调用异常 outTradeNo={}", outTradeNo); order.setErrorMsg(ResultEnum.INTERFACE_WX_INVOKE_ERROR.message); return order; } String code = refundResult.getReturn_code(); String message = refundResult.getReturn_msg(); String resultCode = refundResult.getResult_code(); if (!SUCCESS.equals(resultCode)) { String errCode = refundResult.getErr_code(); String errCodeDesc = refundResult.getErr_code_des(); log.info("【微信】退款失败 code={}, message={}, resultCode={}, errCode={}, errCodeDesc={}", code, message, resultCode, errCode, errCodeDesc); order.setErrorMsg(errCodeDesc); order.setRefundResult(errCodeDesc); // 退款失败信息记录 return order; } log.info("【微信】退款返回 code={}, message={}, resultCode={}", code, message, resultCode); order.setRefundResult(message); // 退款信息记录 if (!OK.equals(message)) { // 退款失败 order.setErrorMsg(ResultEnum.INTERFACE_WX_INVOKE_ERROR.message); return order; } order.setSuccess(true); return order; } catch (Exception e) { ErrorHelper.println(e); order.setErrorMsg(ResultEnum.INTERFACE_WX_INVOKE_ERROR.message); String refundResult = order.getRefundResult(); order.setRefundResult(ObjectUtils.isEmpty(refundResult) ? order.getErrorMsg() : refundResult); return order; } } /** * 微信扫码盒子支付 * * @param title 商品标题 * @param outTradeNo 商户订单号 * @param totalFee 总金额 * @param authCode 微信条码 * @param ip ip地址 * @return 执行返回 */ public static Order payMicro(String title, String outTradeNo, BigDecimal totalFee, String authCode, String ip) { Order order = new Order(); if (StringUtils.isEmpty(outTradeNo) || StringUtils.isEmpty(authCode) || ip == null || totalFee == null) { order.setErrorMsg("【微信】[盒子支付] 参数缺失"); return order; } int totalFeeCents; try { totalFeeCents = totalFee.movePointRight(2).intValue(); String appId = WeChatConfig.APP_ID; String mchId = WeChatConfig.MCH_ID; String mchKey = WeChatConfig.MCH_KEY; String nonce = UUID.randomUUID().toString().replace("-", ""); Micropay micropay = new Micropay(); micropay.setAppid(appId); micropay.setMch_id(mchId); micropay.setNonce_str(nonce); // 随机字符串 micropay.setOut_trade_no(outTradeNo); // 商户订单号 micropay.setBody(title); micropay.setAuth_code(authCode); micropay.setTotal_fee(totalFeeCents); // 金额分 micropay.setSpbill_create_ip(ip); // micropay.setDetail("描述"); // 商品描述 MicropayResult micropayResult = PayMchAPI.payMicropay(micropay, mchKey); if (micropayResult == null) { order.setErrorMsg("【微信】盒子扫码支付请求失败"); return order; } log.info("【微信】[盒子支付]支付反馈 {}", JsonHelper.toJsonString(micropayResult)); String openid = micropayResult.getOpenid(); String bankTransNo = micropayResult.getTransaction_id(); // 银行流水号 String resultCode = micropayResult.getResult_code(); String errMsg = micropayResult.getErr_code_des(); String errCode = micropayResult.getErr_code(); order.setOpenid(openid); order.setBankTransNo(bankTransNo); // 调用失败 if (FAIL.equals(micropayResult.getReturn_code())) { // 失败 log.info("【微信】[盒子支付]失败 outTradeNo={}, bankTransNo={}, returnCode={}, returnMsg={}", outTradeNo, bankTransNo, micropayResult.getReturn_code(), micropayResult.getReturn_msg()); order.setErrorCode(micropayResult.getReturn_code()); order.setErrorMsg(micropayResult.getReturn_msg()); return order; } Order wxOrder; if (FAIL.equals(resultCode)) { // 失败 if (USER_PAYING.equals(errCode)) { // 密码支付 log.info("【微信】[盒子支付]密码支付 outTradeNo={}, msg={}", outTradeNo, errMsg); for (int i = 1; i <= 5; i++) { try { Thread.sleep(5000 + ((i - 1) * 1500)); // 总共35s wxOrder = queryOrder(outTradeNo); if (wxOrder.isSuccess()) { return wxOrder; } // 支付失败 if (PAY_ERROR.equals(wxOrder.getTradeState())) { if (i == 1 || i == 2) { Thread.sleep(i == 2 ? 3000 : 9000); // 等够15s } // 撤销订单 Order cancelOrder = cancelOrder(wxOrder.getOutTradeNo(), wxOrder.getBankTransNo()); // 撤销成功 if (cancelOrder.isSuccess()) { wxOrder.setTradeState(REVOKED); wxOrder.setErrorMsg("订单已撤销"); } return wxOrder; } // 其他失败原因 if (!USER_PAYING.equals(wxOrder.getTradeState())) { return wxOrder; } } catch (Exception e) { e.printStackTrace(); } } } else { order.setErrorCode(errCode); order.setErrorMsg(errMsg); log.info("【微信】[盒子支付]失败 outTradeNo={}, bankTransNo={}, errCode={}, errMsg={}", outTradeNo, bankTransNo, errCode, errMsg); return order; } } // 免密支付 Thread.sleep(2000); // 2s wxOrder = queryOrder(outTradeNo); // 长时间未支付 if (USER_PAYING.equals(wxOrder.getTradeState()) || PAY_ERROR.equals(wxOrder.getTradeState())) { // 撤销订单 Order cancelOrder = cancelOrder(wxOrder.getOutTradeNo(), wxOrder.getBankTransNo()); // 撤销成功 if (cancelOrder.isSuccess()) { wxOrder.setTradeState(REVOKED); wxOrder.setErrorMsg("订单已撤销"); } } return wxOrder; } catch (Exception e) { log.info("【微信】[盒子支付]异常 {}", e.getMessage()); order.setErrorMsg("网络异常或当前不支持微信支付"); ErrorHelper.println(e); } return order; } /** * 撤销订单 * * @param outTradeNo 商户订单号 * @param bankTransNo 银行流水号 * @return 撤销 */ public static Order cancelOrder(String outTradeNo, String bankTransNo) { log.info("【微信】撤销订单 outTradeNo={}, bankTransNo={}", outTradeNo, bankTransNo); Order order = new Order(); try { MchReverse mchReverse = new MchReverse(); String appId = WeChatConfig.APP_ID; String mchId = WeChatConfig.MCH_ID; String mchKey = WeChatConfig.MCH_KEY; mchReverse.setAppid(appId); mchReverse.setMch_id(mchId); mchReverse.setOut_trade_no(outTradeNo); if (!ObjectUtils.isEmpty(bankTransNo)) { mchReverse.setTransaction_id(bankTransNo); } mchReverse.setNonce_str(CodeHelper.get32UUID()); mchReverse.setSign_type("MD5"); MchReverseResult wxResult = PayMchAPI.secapiPayReverse(mchReverse, mchKey); if (wxResult == null) { order.setErrorMsg("【微信】撤销订单失败"); return order; } // 调用错误 if (FAIL.equals(wxResult.getReturn_code())) { order.setErrorCode(wxResult.getReturn_code()); order.setErrorMsg(wxResult.getReturn_msg()); return order; } // 业务错误 if (FAIL.equals(wxResult.getResult_code())) { order.setErrorCode(wxResult.getErr_code()); order.setErrorMsg(wxResult.getErr_code_des()); return order; } // 交易成功 if (SUCCESS.equals(wxResult.getResult_code())) { order.setSuccess(true); // 订单支付成功(判断条件) } } catch (Exception e) { ErrorHelper.println(e); order.setErrorMsg("异常信息:" + e.getMessage()); } return order; } /** * 查询退费订单集(含退费时间) * * @param outTradeNo 订单号 * @return 订单信息 */ public static List queryRefundInfoList(String outTradeNo) throws ServiceException { Refundquery refund = new Refundquery(); refund.setAppid(WeChatConfig.APP_ID); refund.setMch_id(WeChatConfig.MCH_ID); refund.setOut_trade_no(outTradeNo); refund.setNonce_str(CodeHelper.get32UUID()); WxRefundQueryRoot wxResult = WxFactory.Base.Refund().refundQuery(refund, WeChatConfig.MCH_KEY); if (wxResult == null) { throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } if (!SUCCESS.equals(wxResult.getReturn_code())) { log.info("[微信]查询退款失败{}", wxResult.getReturn_msg()); throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } List billList = new ArrayList<>(); if (FAIL.equals(wxResult.getResult_code()) && "REFUNDNOTEXIST".equals(wxResult.getErr_code())) { return billList; } if (!SUCCESS.equals(wxResult.getResult_code())) { throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } List refundOrderList = wxResult.getItems(); if (refundOrderList == null || refundOrderList.size() == 0) { return billList; } Bill bill; String refundStatus; for (WxRefundItem item : refundOrderList) { refundStatus = item.getRefund_status(); if (SUCCESS.equals(refundStatus)) { bill = new Bill(); bill.setMoney(new BigDecimal(item.getRefund_fee()).movePointLeft(2)); bill.setRefundId(item.getRefund_id()); bill.setOutRefundNo(item.getOut_refund_no()); bill.setRefundChannel(item.getRefund_channel()); bill.setRefundRecvAccout(item.getRefund_recv_accout()); bill.setRefundSuccessTime(item.getRefund_success_time()); billList.add(bill); } } return billList; } /** * 查询订单 * * @param outTradeNo 订单号 * @return 订单信息 */ public static List queryRefundList(String outTradeNo) throws ServiceException { Refundquery refund = new Refundquery(); refund.setAppid(WeChatConfig.APP_ID); refund.setMch_id(WeChatConfig.MCH_ID); refund.setOut_trade_no(outTradeNo); refund.setNonce_str(CodeHelper.get32UUID()); RefundqueryResult wxResult = payRefundquery(refund, WeChatConfig.MCH_KEY); if (wxResult == null) { throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } if (!SUCCESS.equals(wxResult.getReturn_code())) { log.info("[微信]查询退款失败{}", wxResult.getReturn_msg()); throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } List billList = new ArrayList<>(); if (FAIL.equals(wxResult.getResult_code()) && "REFUNDNOTEXIST".equals(wxResult.getErr_code())) { return billList; } if (!SUCCESS.equals(wxResult.getResult_code())) { throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } List refundOrderList = wxResult.getRefundqueryResultItems(); if (refundOrderList == null || refundOrderList.size() == 0) { return billList; } Bill bill; String refundStatus; for (RefundqueryResultItem item : refundOrderList) { refundStatus = item.getRefund_status(); if (SUCCESS.equals(refundStatus)) { bill = new Bill(); bill.setMoney(new BigDecimal(item.getRefund_fee()).movePointLeft(2)); bill.setRefundId(item.getRefund_id()); bill.setOutRefundNo(item.getOut_refund_no()); bill.setRefundChannel(item.getRefund_channel()); bill.setRefundRecvAccout(item.getRefund_recv_accout()); billList.add(bill); } } return billList; } /** * 查询退费订单 * * @param outTradeNo 订单号 * @return 订单信息 */ public static Bill queryRefund(String outTradeNo, String outRefundNo) throws ServiceException { if (ObjectUtils.isEmpty(outTradeNo) || ObjectUtils.isEmpty(outRefundNo)) { throw new ServiceException(ResultEnum.PARAM_IS_DEFECT); } List billList = queryRefundList(outTradeNo); if (billList.size() == 0) { return null; } for (Bill bill : billList) { if (outRefundNo.equals(bill.getOutRefundNo())) { return bill; } } return null; } /** * 查询订单 * * @param outTradeNo 订单号 * @return 订单信息 */ public static Order queryOrder(String outTradeNo) { Order order = new Order(); order.setOutTradeNo(outTradeNo); try { MchOrderquery mchOrderquery = new MchOrderquery(); String appId = WeChatConfig.APP_ID; String mchId = WeChatConfig.MCH_ID; String mchKey = WeChatConfig.MCH_KEY; mchOrderquery.setAppid(appId); mchOrderquery.setMch_id(mchId); mchOrderquery.setOut_trade_no(outTradeNo); mchOrderquery.setNonce_str(CodeHelper.get32UUID()); MchOrderInfoResult wxResult = PayMchAPI.payOrderquery(mchOrderquery, mchKey); if (wxResult == null) { order.setErrorMsg("【微信】查询订单失败"); return order; } // 调用错误 if (FAIL.equals(wxResult.getReturn_code())) { order.setErrorCode(wxResult.getReturn_code()); order.setErrorMsg(wxResult.getReturn_msg()); return order; } // 业务错误 if (FAIL.equals(wxResult.getResult_code())) { order.setErrorCode(wxResult.getErr_code()); order.setErrorMsg(wxResult.getErr_code_des()); return order; } String tradeState = wxResult.getTrade_state(); order.setTradeState(tradeState); if (SUCCESS.equals(tradeState)) { order.setSuccess(true); // 订单支付成功(判断条件) } else if (REFUND.equals(tradeState)) { order.setErrorMsg(wxResult.getTrade_state_desc()); order.setRefund(true); // 订单发生过退款 } else { // 交易错误 order.setErrorCode(tradeState); order.setErrorMsg(wxResult.getTrade_state_desc()); return order; } // 交易成功 order.setOpenid(wxResult.getOpenid()); order.setBankTransNo(wxResult.getTransaction_id()); Integer totalFee = wxResult.getTotal_fee(); if (totalFee != null) { order.setTotalFee(new BigDecimal(totalFee).movePointLeft(2)); } } catch (Exception e) { ErrorHelper.println(e); order.setErrorMsg("异常信息:" + e.getMessage()); } return order; } /** * 查询订单 * * @param transId 支付单号 * @return 订单信息 */ public static Order queryOrderByTransId(String transId) { Order order = new Order(); order.setBankTransNo(transId); try { MchOrderquery mchOrderquery = new MchOrderquery(); mchOrderquery.setAppid(WeChatConfig.APP_ID); mchOrderquery.setMch_id(WeChatConfig.MCH_ID); mchOrderquery.setTransaction_id(transId); mchOrderquery.setNonce_str(CodeHelper.get32UUID()); MchOrderInfoResult wxResult = PayMchAPI.payOrderquery(mchOrderquery, WeChatConfig.MCH_KEY); if (wxResult == null) { order.setErrorMsg("【微信】查询订单失败"); return order; } // 调用错误 if (FAIL.equals(wxResult.getReturn_code())) { order.setErrorCode(wxResult.getReturn_code()); order.setErrorMsg(wxResult.getReturn_msg()); return order; } // 业务错误 if (FAIL.equals(wxResult.getResult_code())) { order.setErrorCode(wxResult.getErr_code()); order.setErrorMsg(wxResult.getErr_code_des()); return order; } String tradeState = wxResult.getTrade_state(); order.setTradeState(tradeState); if (SUCCESS.equals(tradeState)) { order.setSuccess(true); // 订单支付成功(判断条件) } else if (REFUND.equals(tradeState)) { order.setErrorMsg(wxResult.getTrade_state_desc()); order.setRefund(true); // 订单发生过退款 } else { // 交易错误 order.setErrorCode(tradeState); order.setErrorMsg(wxResult.getTrade_state_desc()); return order; } // 交易成功 order.setOpenid(wxResult.getOpenid()); order.setBankTransNo(wxResult.getTransaction_id()); Integer totalFee = wxResult.getTotal_fee(); if (totalFee != null) { order.setTotalFee(new BigDecimal(totalFee).movePointLeft(2)); } } catch (Exception e) { ErrorHelper.println(e); order.setErrorMsg("异常信息:" + e.getMessage()); } return order; } // // /** // * 通知成功 // * // * @param response 响应流 // */ // public static void notifySuccess(HttpServletResponse response) { // notifyEnd(response, "SUCCESS", ""); // } // // public static void notifyEnd(HttpServletResponse response, String returnCode, String returnMsg) { // try (PrintWriter writer = response.getWriter()) { // String xml = "" // writer.write(xml); // writer.flush(); // } catch (Exception e) { // e.printStackTrace(); // ErrorHelper.println(e); // } // } /** * 查询商户账单 * * @return 订单信息 */ public static List queryBill(String billType, String date, boolean isCompose) throws ServiceException { List billList = new ArrayList<>(); if (!DateHelper.isValidDate(date, DateHelper.DateEnum.yyyy_MM_dd)) { throw new ServiceException(ResultEnum.PARAM_DATE_ERROR); } Date dateTime = DateHelper.strToDate(date, DateHelper.DateEnum.yyyy_MM_dd); if (dateTime == null) { throw new ServiceException(ResultEnum.PARAM_DATE_ERROR); } date = DateHelper.dateToStr(dateTime, DateHelper.DateEnum.yyyyMMdd); if (date == null) { throw new ServiceException(ResultEnum.PARAM_DATE_ERROR); } if (!ALL.equals(billType) && !REFUND.equals(billType) && !SUCCESS.equals(billType)) { throw new ServiceException(ResultEnum.PARAM_IS_INVALID); } MchDownloadbill mchDownloadbill = new MchDownloadbill(); mchDownloadbill.setAppid(WeChatConfig.APP_ID); mchDownloadbill.setMch_id(WeChatConfig.MCH_ID); mchDownloadbill.setNonce_str(CodeHelper.get32UUID()); mchDownloadbill.setBill_date(date); mchDownloadbill.setBill_type(billType); DownloadbillResult downloadbillResult = PayMchAPI.payDownloadbill(mchDownloadbill, WeChatConfig.MCH_KEY); if (downloadbillResult == null) { throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } String code = downloadbillResult.getReturn_code(); String msg = downloadbillResult.getReturn_msg(); if (FAIL.equals(code)) { if ("invalid bill_date".equals(msg)) { throw new ServiceException("日期无效,或超过账单的可查询日期范围[invalid bill_date]"); } log.info("[微信]下载对账单错误[{}][{}]", code, msg); throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR.message + "[" + msg + "]"); } String data = downloadbillResult.getData(); if (data == null) { throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); } String[] dataArr = data.split("\\n"); if (dataArr.length == 0) { return billList; } int index; Bill billItem; String money; String outRefundNo; String[] titleArr = dataArr[0].split(","); if (titleArr.length == 0) { return billList; } String[] item; String tradeState; for (int i = 1; i < (dataArr.length - 2); i++) { item = dataArr[i].split(","); //27 billItem = new Bill(); billItem.setTradeTime(dataHandle(item[0])); billItem.setOutTradeNo(dataHandle(item[6])); billItem.setBankTransNo(dataHandle(item[5])); // billItem.setOpenid(dataHandle(item[7])); // billItem.setPayType(dataHandle(item[8])); tradeState = dataHandle(item[9]); billItem.setTradeState(tradeState); if (WxPayHelper.REFUND.equals(tradeState)) { // 退款 index = findIndex(titleArr, "退款金额"); if (index != -1) { money = dataHandle(item[index]); if (money != null) { billItem.setMoney(new BigDecimal(money).negate()); } } index = findIndex(titleArr, "商户退款单号"); if (index != -1) { outRefundNo = dataHandle(item[index]); if (outRefundNo != null) { billItem.setOutRefundNo(outRefundNo); } } // index = findIndex(titleArr, "退款类型"); // if (index != -1) { // order.setRefundType(dataHandle(item[index])); // } // // index = findIndex(titleArr, "退款状态"); // if (index != -1) { // order.setRefundResult(dataHandle(item[index])); // } } else if (WxPayHelper.SUCCESS.equals(tradeState)) { // 成功 index = findIndex(titleArr, "应结订单金额"); if (index != -1) { money = dataHandle(item[index]); if (money != null) { billItem.setMoney(new BigDecimal(money)); } } } billList.add(billItem); } if (ALL.equals(billType) && isCompose) { List refundList = billList.stream().filter(o -> REFUND.equals(o.getTradeState())).collect(Collectors.toList()); List successList = billList.stream().filter(o -> SUCCESS.equals(o.getTradeState())).collect(Collectors.toList()); String outTradeNo; for (Bill refundItem : refundList) { outTradeNo = refundItem.getOutTradeNo(); for (Bill successItem : successList) { if (outTradeNo.equals(successItem.getOutTradeNo())) { if (successItem.getHisBills() == null) { successItem.setRefundBills(new ArrayList<>()); } refundItem.setBankTransNo(null); refundItem.setOutTradeNo(null); successItem.getRefundBills().add(refundItem); break; } } } return successList; } return billList; } // // /** // * 查询商户账单 // * // * @return 订单信息 // */ // public static List queryBill(String billType, String date) throws ServiceException { // List billList = new ArrayList<>(); // if (!DateHelper.isValidDate(date, DateHelper.DateEnum.yyyy_MM_dd)) { // throw new ServiceException(ResultEnum.PARAM_DATE_ERROR); // } // // Date dateTime = DateHelper.strToDate(date, DateHelper.DateEnum.yyyy_MM_dd); // if (dateTime == null) { // throw new ServiceException(ResultEnum.PARAM_DATE_ERROR); // } // date = DateHelper.dateToStr(dateTime, DateHelper.DateEnum.yyyyMMdd); // if (date == null) { // throw new ServiceException(ResultEnum.PARAM_DATE_ERROR); // } // // if (!ALL.equals(billType) && !REFUND.equals(billType) && !SUCCESS.equals(billType)) { // throw new ServiceException(ResultEnum.PARAM_IS_INVALID); // } // // MchDownloadbill mchDownloadbill = new MchDownloadbill(); // String appId = WeChatConfig.getAppId(); // String mchId = WeChatConfig.getMchId(); // String mchKey = WeChatConfig.getMchKey(); // // mchDownloadbill.setAppid(appId); // mchDownloadbill.setMch_id(mchId); // mchDownloadbill.setNonce_str(CodeHelper.get32UUID()); // mchDownloadbill.setBill_date(date); // mchDownloadbill.setBill_type(billType); // DownloadbillResult downloadbillResult = PayMchAPI.payDownloadbill(mchDownloadbill, mchKey); // // if (downloadbillResult == null) { // throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); // } // // String data = downloadbillResult.getData(); // if (data == null) { // throw new ServiceException(ResultEnum.INTERFACE_WX_INVOKE_ERROR); // } // String[] dataArr = data.split("\\n"); // if (dataArr.length == 0) { // return billList; // } // // int index; // Bill billItem; // String money; // String[] titleArr = dataArr[0].split(","); // if (titleArr.length == 0) { // return billList; // } // // System.out.println(JSONArray.toJSONString(titleArr)); // for (int i = 1; i < (dataArr.length - 2); i++) { // String[] item = dataArr[i].split(","); //27 // System.out.println(JSONArray.toJSONString(item)); // billItem = new Bill(); // billItem.setTradeTime(dataHandle(item[0])); // billItem.setOutTradeNo(dataHandle(item[6])); // billItem.setBankTransNo(dataHandle(item[5])); //// billItem.setOpenid(dataHandle(item[7])); //// billItem.setPayType(dataHandle(item[8])); // String tradeState = dataHandle(item[9]); // billItem.setTradeState(tradeState); // if (WxPayHelper.REFUND.equals(tradeState)) { // 退款 // index = findIndex(titleArr, "退款金额"); // if (index != -1) { // money = dataHandle(item[index]); // if (money != null) { // billItem.setMoney(new BigDecimal(money).negate()); // } // } // //// index = findIndex(titleArr, "退款类型"); //// if (index != -1) { //// order.setRefundType(dataHandle(item[index])); //// } //// //// index = findIndex(titleArr, "退款状态"); //// if (index != -1) { //// order.setRefundResult(dataHandle(item[index])); //// } // // } else if (WxPayHelper.SUCCESS.equals(tradeState)) { // 成功 // index = findIndex(titleArr, "应结订单金额"); // if (index != -1) { // money = dataHandle(item[index]); // if (money != null) { // billItem.setMoney(new BigDecimal(money)); // } // } // } // billList.add(billItem); // } // return billList; // } // /** // * 微信支付调用及通知 // * // * @param request 请求 // * @param response 响应 // * @return 微信支付信息 // */ // public static PayResult oldPayNotify(HttpServletRequest request, HttpServletResponse response) { // PayResult orderInfo = null; // InputStream in = null; // OutputStream out = null; // try { // in = request.getInputStream(); // out = response.getOutputStream(); // // // 获取请求数据 // String wxXmlData = StreamUtils.copyToString(in, StandardCharsets.UTF_8); // // // 转换数据对象 // MchPayNotify wxPayNotify = XMLConverUtil.convertToObject(MchPayNotify.class, wxXmlData); // String openid = wxPayNotify.getOpenid(); // String outTradeNo = wxPayNotify.getOut_trade_no(); // String bankTransNo = wxPayNotify.getTransaction_id(); // Integer totalFee = wxPayNotify.getTotal_fee(); // 金额 // String timeEnd = wxPayNotify.getTime_end(); // String attach = wxPayNotify.getAttach(); // // if (openid == null || outTradeNo == null || timeEnd == null || bankTransNo == null) { // log.info("【微信】下单信息返回错误 outTradeNo={}", outTradeNo); // payNotifyError(out); // return null; // } // // // 已处理 去重 // if (KEYS.isContainsKey(bankTransNo)) { // log.info("【微信】[重复请求]下单信息去重 outTradeNo={}", outTradeNo); // payNotifySuccess(out); // return null; // } // // // 将XML转为MAP,确保所有字段都参与签名验证 // Map wxMapData = XMLConverUtil.convertToMap(wxXmlData); // StringBuilder wxPayInfo = new StringBuilder(); // for (String key : wxMapData.keySet()) { // wxPayInfo.append(key).append("=").append(wxMapData.get(key)).append(";"); // 拼接微信返回的信息 // } // // // @since 2.8.5 // wxPayNotify.buildDynamicField(wxMapData); // // 签名验证 // if (!SignatureUtil.validateSign(wxMapData, WeChatConfig.MCH_KEY)) { // 验证未通过,通知支付失败 // log.info("【微信】签名验证未通过,通知支付失败!"); // payNotifyError(out); // return null; // } // // // 验证通过 // payNotifySuccess(out); // // // 保存支付结果 // orderInfo = new PayResult(); // orderInfo.setOpenid(openid); // orderInfo.setOutTradeNo(outTradeNo); // orderInfo.setTimeEnd(timeEnd); // orderInfo.setTotalFee(new BigDecimal(totalFee).movePointLeft(2)); // orderInfo.setTransId(bankTransNo); // orderInfo.setInfo(wxPayInfo.toString()); // orderInfo.setAttach(attach); // // } catch (Exception e) { // log.info("【微信】支付通知异常:" + e.getMessage()); // payNotifySuccess(out); // ErrorHelper.println(e); // } finally { // try { // if (in != null) in.close(); // // if (out != null) { // if (orderInfo == null) { // payNotifySuccess(out); // } // out.flush(); // out.close(); // } // } catch (Exception e) { // ErrorHelper.println(e); // } // } // return orderInfo; // } /** * 商户创建订单 * * @param merchantEnum 枚举 * @param title 商品标题 * @param totalFee 订单金额 * @param outTradeNo 订单号 * @param notifyType 回调类型 * @param ip ip地址 * @return 成功返回 json, 否则返回null */ public static String createNativeOrder(MerchantEnum merchantEnum, String title, BigDecimal totalFee, String outTradeNo, String notifyType, String ip) { try { if (ObjectUtils.isEmpty(outTradeNo) || merchantEnum == null || totalFee == null || notifyType == null) { log.info("【微信】下单参数缺失"); return null; } // 金额转换 String cents; try { cents = totalFee.movePointRight(2).toString(); // 单位分 } catch (Exception e) { ErrorHelper.println(e); log.info("【微信】统一下单金额转换异常"); return null; } String appId = WeChatConfig.APP_ID; String mchId = WeChatConfig.MCH_ID; String mchKey = WeChatConfig.MCH_KEY; String nonce = UUID.randomUUID().toString().replace("-", ""); Unifiedorder unifiedorder = new Unifiedorder(); unifiedorder.setAppid(appId); unifiedorder.setMch_id(mchId); unifiedorder.setNonce_str(nonce); unifiedorder.setOut_trade_no(outTradeNo); unifiedorder.setBody(title); unifiedorder.setTotal_fee(cents); // 金额分 unifiedorder.setSpbill_create_ip(ip); unifiedorder.setNotify_url(WeChatConfig.getBaseUrl() + merchantEnum.QR_NOTIFY_URL); unifiedorder.setTrade_type("NATIVE"); // 额外参数 unifiedorder.setAttach(notifyType); log.info(appId + "," + mchId + "," + nonce + "," + title + "," + outTradeNo + "," + totalFee + "," + ip + "," + merchantEnum.NOTIFY_URL); // 微信统一下单 UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey); log.info("【微信】统一下单返回:{}", JsonHelper.toJsonString(unifiedorderResult)); if (unifiedorderResult == null) { log.info("【微信】统一下单失败"); return null; } String result_code = unifiedorderResult.getResult_code(); // 下单是否成功 Boolean sign_status = unifiedorderResult.getSign_status(); if (!SUCCESS.equals(result_code) || sign_status == null || !sign_status) { log.info("【微信】统一下单返回异常信息:sign_status={}, err_code=[ {} ], err_code_des=[ {} ]", sign_status, unifiedorderResult.getErr_code(), unifiedorderResult.getErr_code_des()); return null; } String codeUrl = unifiedorderResult.getCode_url(); log.info("codeUrl={}", codeUrl); if (codeUrl != null) { return codeUrl; } } catch (Exception e) { log.error("【微信】统一下单异常信息:" + e.getMessage()); ErrorHelper.println(e); } return null; } public static String dataHandle(String data) { if (ObjectUtils.isEmpty(data)) { return null; } return data.substring(1); } public static int findIndex(String[] titleArr, String title) { if (titleArr == null || titleArr.length == 0) { return -1; } for (int i = 0; i < titleArr.length; i++) { if (title.equals(titleArr[i])) { return i; } } return -1; } }