package com.ynxbd.bcm.config; import com.alibaba.fastjson.JSONObject; import com.bocom.api.DefaultBocomClient; import com.bocom.api.utils.ApiUtils; import com.ynxbd.bcm.bean.pmssMpng.*; import com.ynxbd.common.bean.enums.MerchantEnum; import com.ynxbd.common.bean.pay.Order; import com.ynxbd.common.bean.pay.PayResult; import com.ynxbd.common.helper.common.DateHelper; import com.ynxbd.common.helper.common.ErrorHelper; import com.ynxbd.common.helper.common.JsonHelper; import com.ynxbd.common.helper.common.RepeatKeyHelper; import com.ynxbd.common.result.ResultEnum; import com.ynxbd.common.result.ServiceException; import com.ynxbd.wx.config.WeChatConfig; import com.ynxbd.wx.wxfactory.utils.WxSignHelper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; @Slf4j public class BCMHelper { // 排重 public static final RepeatKeyHelper KEYS = new RepeatKeyHelper(60); // 支付类型JSAPI public static String B2C_JSAPI_WECHAT = "B2C-JSAPI-WECHAT"; public static String VERSION = "V-1.0"; public static String ONLINE = "ONLINE"; // 线上或线下{ ONLINE/OFFLINE } public static String CNY = "CNY"; // 币种 // head交易状态 private static final String RESP_STATUS_P = "P"; // 处理中 private static final String RESP_STATUS_F = "F"; // 失败 private static final String RESP_STATUS_S = "S"; // 成功 // 订单处理状态 private static final String TRAN_STATE_PROCESS = "PROCESS"; // 处理中 private static final String TRAN_STATE_SUCCESS = "SUCCESS"; // 交易成功 private static final String TRAN_STATE_FAILURE = "FAILURE"; // 交易失败 // body交易状态 private static final String ORDER_STATUS_PAIED = "PAIED"; // 交易成功 private static final String ORDER_STATUS_WAITPAY = "WAITPAY"; // 等待支付 private static final String ORDER_STATUS_REFUNDED = "REFUNDED"; // 部分退款 private static final String ORDER_STATUS_REFUNDALL = "REFUNDALL"; // 全部退款 private static final String ORDER_STATUS_CLOSED = "CLOSED"; // 部分退款 private static final String ORDER_STATUS_INITIAL = "INITIAL"; // 初始化 /** * 交行支付调用及通知 * * @param request 请求 * @return 交行支付信息 */ public static PayResult payNotify(HttpServletRequest request) throws ServiceException { try { // 获取请求数据 String respJson = WxSignHelper.getReqData(request); if (respJson == null) { throw new ServiceException("【交行】回调通知参数解析失败"); } PmmsMpngNotifyRequestV1.PmmsMpngNotifyRequestV1Biz bizContent; bizContent = (PmmsMpngNotifyRequestV1.PmmsMpngNotifyRequestV1Biz) ApiUtils.parseNotifyJsonWithBocomSign(respJson, "UTF-8", BCMConfig.API_GW_PUBLIC_KEY, PmmsMpngNotifyRequestV1.PmmsMpngNotifyRequestV1Biz.class); if (bizContent == null || !TRAN_STATE_SUCCESS.equals(bizContent.getTranState())) { throw new ServiceException("【交行】回调通知参数缺失"); } PmmsMpngNotifyRequestV1.PmmsMpngNotifyRequestV1Biz.RequireValues requireValues = bizContent.getRequireValues(); String bankTransNo = requireValues.getThirdPartyTranNo(); // 已处理 去重 if (KEYS.isContainsKey(bankTransNo)) { throw new ServiceException(ResultEnum.PAY_NOTIFY_REPEAT, String.format("【交行】[重复请求]回调通知去重 bankTransNo={%s}", bankTransNo)); } log.info("【交行】回调通知返回 bankTransNo={} resp={}", bankTransNo, JsonHelper.toJsonString(bizContent)); String outTradeNo = bizContent.getMerTranNo(); String bankMerchantNo = requireValues.getBankTranNo(); String totalAmount = bizContent.getTotalAmount(); if (outTradeNo == null || bankTransNo == null || bankMerchantNo == null) { throw new ServiceException(String.format("【交行】回调通知错误 outTradeNo={%s}, bankTransNo={%s}, bankMerchantNo={%s}", outTradeNo, bankTransNo, bankMerchantNo)); } // 保存支付结果 PayResult payInfo = new PayResult(); payInfo.setPatDate(DateHelper.getCurDate()); payInfo.setPatDate(DateHelper.getCurTime()); payInfo.setOutTradeNo(outTradeNo); payInfo.setTransId(bankTransNo); payInfo.setBankMerchantNo(bankMerchantNo); payInfo.setTotalFee(new BigDecimal(totalAmount)); JSONObject respJsonObj = JsonHelper.parseObject(respJson); if (respJsonObj != null) { JSONObject info = respJsonObj.getJSONObject("notify_biz_content"); payInfo.setInfo(JsonHelper.toJsonString(info)); } return payInfo; } catch (Exception e) { if (e instanceof ServiceException) { throw new ServiceException(((ServiceException) e).getResultEnum(), e.getMessage()); } ErrorHelper.println(e); throw new ServiceException(String.format("【交行】支付通知异常:[%s]", e.getMessage())); } } public static JSONObject createOrder(MerchantEnum merchantEnum, String title, String outTradeNo, String totalFee, String notifyType) { try { DefaultBocomClient client = BCMConfig.createClient(); MPNG210003RequestV1 clientRequest = new MPNG210003RequestV1(); clientRequest.setServiceUrl(BCMConfig.API_GW_URL_ADDRESS + "/api/pmssMpng/MPNG210003/v1"); MPNG210003RequestV1.MPNG210003RequestV1Biz bizContent = new MPNG210003RequestV1.MPNG210003RequestV1Biz(); MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqHead reqHead = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqHead(); reqHead.setTransTime(BCMConfig.getTransTime()); // 交易时间 reqHead.setVersion(VERSION); // 版本信息 bizContent.setReqHead(reqHead); MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody reqBody = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody(); reqBody.setMerPtcId(BCMConfig.MER_PTC_ID); // 商户编号 reqBody.setPartnerId(BCMConfig.PARTNER_ID); // 服务商编号 reqBody.setTranScene(B2C_JSAPI_WECHAT); // 交易场景{微 信:B2C-JSAPI-WECHAT 支付宝:B2C-JSAPI-ALIPAY} reqBody.setMerTradeDate(BCMConfig.getMerTradeDate()); // 商户侧交易日期 reqBody.setMerTradeTime(BCMConfig.getMerTradeTime()); // 商户侧交易时间 // 回调地址 reqBody.setNotifyUrl(WeChatConfig.getBaseUrl() + merchantEnum.getNotifyUrl(null, notifyType)); reqBody.setPayMerTranNo(outTradeNo); // 商户订单号 reqBody.setTotalAmount(totalFee); // 订单总金额 reqBody.setTranContent(title); // 交易内容 reqBody.setLocation(ONLINE); reqBody.setCurrency(CNY); List require_fieldsList = new ArrayList<>(); // 行端交易流水号 MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody.RequireFields require_fields = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("bank_tran_no"); require_fieldsList.add(require_fields); // 第三方渠道 require_fields = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("third_party"); require_fieldsList.add(require_fields); // 第三方渠道交易流水号 require_fields = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("third_party_tran_no"); require_fieldsList.add(require_fields); // 微信支付宝详细付款信息 require_fields = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("payment_info"); require_fieldsList.add(require_fields); // 微信支付宝详细付款信息 require_fields = new MPNG210003RequestV1.MPNG210003RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("refund_info"); require_fieldsList.add(require_fields); // 设置额外字段 reqBody.setRequireFields(require_fieldsList); bizContent.setReqBody(reqBody); clientRequest.setBizContent(bizContent); MPNG210003ResponseV1 clientResponse = client.execute(clientRequest, UUID.randomUUID().toString().replace("-", "")); log.info("【交行】下单 outTradeNo={}, resp={}", outTradeNo, JsonHelper.toJsonString(clientResponse)); if (clientResponse == null) { log.info("【交行】下单失败 outTradeNo={}", outTradeNo); return null; } if (!clientResponse.isSuccess()) { log.info("【交行】下单失败 outTradeNo={}, resp={}", outTradeNo, JsonHelper.toJsonString(clientResponse)); return null; } // head MPNG210003ResponseV1.RspHead rspHead = clientResponse.getRspHead(); if (rspHead == null) { log.info("【交行】下单失败 outTradeNo={}, rspHead={}", outTradeNo, JsonHelper.toJsonString(rspHead)); return null; } if (!RESP_STATUS_S.equals(rspHead.getResponseStatus())) { log.info("【交行】下单失败 outTradeNo={}, code={}, message={}", outTradeNo, rspHead.getResponseCode(), rspHead.getResponseMsg()); return null; } // body MPNG210003ResponseV1.RspBody rspBody = clientResponse.getRspBody(); if (rspBody == null) { log.info("【交行】下单失败 outTradeNo={}, rspBody={}", outTradeNo, JsonHelper.toJsonString(rspBody)); return null; } String payUrl = rspBody.getRedirectUrl(); if (ObjectUtils.isEmpty(payUrl)) { log.info("【交行】下单失败 outTradeNo={}, rspBody={}", outTradeNo, JsonHelper.toJsonString(rspBody)); return null; } JSONObject jsonObj = new JSONObject(); jsonObj.put("outTradeNo", outTradeNo); jsonObj.put("url", payUrl); jsonObj.put("type", MerchantEnum.BCM.CODE); return jsonObj; } catch (Exception e) { ErrorHelper.println(e); } return null; } public static Order queryOrder(String outTradeNo) { Order order = new Order(); order.setOutTradeNo(outTradeNo); try { DefaultBocomClient client = BCMConfig.createClient(); MPNG020702RequestV1 clientRequest = new MPNG020702RequestV1(); clientRequest.setServiceUrl(BCMConfig.API_GW_URL_ADDRESS + "/api/pmssMpng/MPNG020702/v1"); MPNG020702RequestV1.MPNG020702RequestV1Biz bizContent = new MPNG020702RequestV1.MPNG020702RequestV1Biz(); MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqHead reqHead = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqHead(); reqHead.setTransTime(BCMConfig.getTransTime()); // 交易时间 reqHead.setVersion(VERSION); bizContent.setReqHead(reqHead); MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody reqBody = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody(); reqBody.setMerTradeDate(BCMConfig.getMerTradeDate()); reqBody.setPayMerTranNo(outTradeNo); reqBody.setTranScene(B2C_JSAPI_WECHAT); // 交易场景 List require_fieldsList = new ArrayList<>(); MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody.RequireFields require_fields = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("bank_tran_no"); require_fieldsList.add(require_fields); //-------------------------------------------- require_fields = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("third_party"); require_fieldsList.add(require_fields); // ---------------------------------------------- require_fields = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("third_party_tran_no"); require_fieldsList.add(require_fields); // ---------------------------------------------- require_fields = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("payment_info"); require_fieldsList.add(require_fields); // ---------------------------------------------- require_fields = new MPNG020702RequestV1.MPNG020702RequestV1Biz.ReqBody.RequireFields(); require_fields.setRequireField("refund_info"); require_fieldsList.add(require_fields); reqBody.setRequireFields(require_fieldsList); reqBody.setMerPtcId(BCMConfig.MER_PTC_ID); reqBody.setPartnerId(BCMConfig.PARTNER_ID); bizContent.setReqBody(reqBody); clientRequest.setBizContent(bizContent); // -------------------------------------------------- MPNG020702ResponseV1 clientResponse = client.execute(clientRequest, UUID.randomUUID().toString().replace("-", "")); log.info("【交行】查询订单 outTradeNo={}, resp={}", outTradeNo, JsonHelper.toJsonString(clientResponse)); if (clientResponse == null) { order.setErrorMsg("【交行】调用查询订单失败"); return order; } if (!clientResponse.isSuccess()) { order.setErrorMsg(clientResponse.getRspMsg()); return order; } // head MPNG020702ResponseV1.RspHead rspHead = clientResponse.getRspHead(); if (rspHead == null) { order.setErrorMsg("【交行】查询订单失败 outTradeNo=" + outTradeNo); return order; } if (!RESP_STATUS_S.equals(rspHead.getResponseStatus())) { order.setErrorCode(rspHead.getResponseCode()); order.setErrorMsg(rspHead.getResponseMsg()); return order; } MPNG020702ResponseV1.RspBody rspBody = clientResponse.getRspBody(); if (rspBody == null) { order.setErrorCode(rspHead.getResponseCode()); order.setErrorMsg(rspHead.getResponseMsg()); return order; } MPNG020702ResponseV1.RspBody.RequireValues requireValues = rspBody.getRequireValues(); if (requireValues != null) { order.setBankTransNo(requireValues.getBankTranNo()); } order.setPayMoney(new BigDecimal(rspBody.getTotalAmount())); // 订单总金额 String tranState = rspBody.getTranState(); // PROCESS 处理中 SUCCESS 交易成功 FAILURE 交易失败 String orderStatus = rspBody.getOrderStatus(); // PAIED交易成功 WAITPAY等待支付 REFUNDED部分退款 REFUNDALL全部退款 CLOSED订单关闭 INITIAL初始化 order.setTradeState(orderStatus); if (TRAN_STATE_SUCCESS.equals(tranState) && ORDER_STATUS_PAIED.equals(orderStatus)) { order.setSuccess(true); // 订单支付成功(判断条件) } else if (ORDER_STATUS_REFUNDED.equals(orderStatus) || ORDER_STATUS_REFUNDALL.equals(orderStatus)) { order.setErrorMsg("订单发生过退款"); if (ORDER_STATUS_REFUNDED.equals(orderStatus)) order.setErrorMsg("订单发生过退款"); if (ORDER_STATUS_REFUNDALL.equals(orderStatus)) order.setErrorMsg("订单已全额退款"); order.setRefund(true); // 订单发生过退款 } else { // 交易错未成功 order.setErrorCode(orderStatus); if (ORDER_STATUS_WAITPAY.equals(orderStatus)) order.setErrorMsg("等待支付"); if (ORDER_STATUS_CLOSED.equals(orderStatus)) order.setErrorMsg("订单关闭"); if (ORDER_STATUS_INITIAL.equals(orderStatus)) order.setErrorMsg("初始化"); } return order; } catch (Exception e) { ErrorHelper.println(e); order.setErrorMsg("【交行】" + e.getMessage()); return order; } } /** * 退款 * * @param outTradeNo 订单号 * @param outRefundNo 退款单号(28位) * @param tradeDate 发生交易的日期 * @param refundMoney 退款金额 * @param desc 退款描述 */ public static Order refund(String outTradeNo, String outRefundNo, String tradeDate, BigDecimal refundMoney, String desc) { outRefundNo = outRefundNo.substring(4); log.info("【交行】退款申请 outTradeNo={}, outRefundNo={}, tradeDate={}, refundMoney={}", outTradeNo, outRefundNo, tradeDate, refundMoney); Order order = new Order(); try { DefaultBocomClient client = BCMConfig.createClient(); MPNG020701RequestV1 clientRequest = new MPNG020701RequestV1(); clientRequest.setServiceUrl(BCMConfig.API_GW_URL_ADDRESS + "/api/pmssMpng/MPNG020701/v1"); MPNG020701RequestV1.MPNG020701RequestV1Biz bizContent = new MPNG020701RequestV1.MPNG020701RequestV1Biz(); MPNG020701RequestV1.MPNG020701RequestV1Biz.ReqHead reqHead = new MPNG020701RequestV1.MPNG020701RequestV1Biz.ReqHead(); reqHead.setTransTime(BCMConfig.getTransTime()); // 交易时间 yyyyMMddHHmmss reqHead.setVersion(VERSION); // req-body MPNG020701RequestV1.MPNG020701RequestV1Biz.ReqBody reqBody = new MPNG020701RequestV1.MPNG020701RequestV1Biz.ReqBody(); reqBody.setTranScene(B2C_JSAPI_WECHAT); reqBody.setCurrency(CNY); reqBody.setMerPtcId(BCMConfig.MER_PTC_ID); reqBody.setPartnerId(BCMConfig.PARTNER_ID); reqBody.setMerRefundDate(DateHelper.dateToStr(new Date(), DateHelper.DateEnum.yyyyMMdd)); reqBody.setMerRefundTime(DateHelper.dateToStr(new Date(), DateHelper.DateEnum.HHmmss)); // -------------- reqBody.setPayMerTranNo(outTradeNo); // 订单号 reqBody.setRefundMerTranNo(outRefundNo); // 退费订单号 reqBody.setMerTradeDate(tradeDate); reqBody.setAmount(refundMoney.setScale(2, RoundingMode.DOWN).toString()); reqBody.setTranContent(desc); bizContent.setReqHead(reqHead); bizContent.setReqBody(reqBody); clientRequest.setBizContent(bizContent); // -------------------------------------------------- log.info("request={}", JsonHelper.toJsonString(clientRequest)); MPNG020701ResponseV1 clientResponse = client.execute(clientRequest, UUID.randomUUID().toString().replace("-", "")); log.info("【交行】退款 outTradeNo={}, resp={}", outTradeNo, JsonHelper.toJsonString(clientResponse)); if (clientResponse == null) { order.setErrorMsg("【交行】调用退款失败"); order.setRefundResult("调用退款失败"); return order; } if (!clientResponse.isSuccess()) { order.setErrorMsg(clientResponse.getRspMsg()); order.setRefundResult(clientResponse.getRspMsg()); return order; } // head MPNG020701ResponseV1.RspHead rspHead = clientResponse.getRspHead(); if (rspHead == null) { order.setErrorMsg("【交行】退款失败 outTradeNo=" + outTradeNo); order.setRefundResult("退款失败"); return order; } if (!RESP_STATUS_S.equals(rspHead.getResponseStatus())) { order.setErrorCode(rspHead.getResponseCode()); order.setErrorMsg(rspHead.getResponseMsg()); order.setRefundResult(rspHead.getResponseMsg()); return order; } MPNG020701ResponseV1.RspBody rspBody = clientResponse.getRspBody(); if (rspBody == null) { order.setErrorCode(rspHead.getResponseCode()); order.setErrorMsg(rspHead.getResponseMsg()); order.setRefundResult(rspHead.getResponseMsg()); return order; } order.setRefundResult("OK"); order.setSuccess(true); // 操作成功标识 return order; } catch (Exception e) { ErrorHelper.println(e); order.setErrorMsg("【交行】" + e.getMessage()); return order; } } /** * 【交行】回调通知成功 */ public static Map resp(ServiceException e) { if (ResultEnum.PAY_NOTIFY_REPEAT.equals(e.getResultEnum())) { return respOk(); } return respError(e.getMessage()); } /** * 【交行】回调通知成功 */ public static Map respOk() { log.info("【交行】回调通知成功"); Map resultMap = new HashMap<>(); resultMap.put("biz_state", "S"); resultMap.put("rsp_code", "0000"); resultMap.put("rsp_msg", "success"); return resultMap; } /** * 【交行】回调通知失败 */ public static Map respError(String message) { log.info("回调通知: {}", message); if (message == null) { message = "error"; } Map resultMap = new HashMap<>(); resultMap.put("biz_state", "F"); resultMap.put("rsp_code", "0000"); resultMap.put("rsp_msg", "error"); return resultMap; } }