package com.ynxbd.common.service; import com.alibaba.fastjson.JSONObject; import com.ynxbd.ali.helper.AliHelper; import com.ynxbd.bcm.config.BCMHelper; import com.ynxbd.common.action.pay.PEnum; import com.ynxbd.common.bean.enums.MerchantEnum; import com.ynxbd.common.bean.pay.*; import com.ynxbd.common.dao.*; import com.ynxbd.common.dao.his.HisAccountDao; import com.ynxbd.common.helper.common.CopyHelper; import com.ynxbd.common.helper.common.DateHelper; import com.ynxbd.common.result.JsonResult; import com.ynxbd.common.result.Result; import com.ynxbd.common.result.ResultEnum; import com.ynxbd.common.result.ServiceException; import com.ynxbd.common.service.cache.PayCache; import com.ynxbd.wx.wxfactory.WxMedicalHelper; import com.ynxbd.wx.wxfactory.WxPayHelper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; import java.util.Date; import java.util.List; @Slf4j public class PayService { public static class Common { public interface RefundMethod { boolean update(); } public interface UpdateHisStatus { boolean update(); } } public static Result isNoPay() { if (DateHelper.inTimeRangeH_m_s("23:55:00", "23:59:59") || DateHelper.inTimeRangeH_m_s("00:00:00", "00:00:30")) { return Result.error(ResultEnum.SYSTEM_IS_BILLING); } return null; } /** * 手动退费 * * @param verifyDate 比对时间 * @param orderInfo 支付信息 * @param refundUser 退款人 * @param refundDesc 退款描述 * @param refundTable 退款类型 * @return 退费信息 */ public static Order handRefund(Date verifyDate, Date updateTime, Order orderInfo, BigDecimal totalFee, String refundUser, String refundDesc, String refundTable, Integer size, String sys, Common.RefundMethod refundMethod) throws ServiceException { String tradeNo = orderInfo.getTradeNo(); String outTradeNo = orderInfo.getOutTradeNo(); if (ObjectUtils.isEmpty(outTradeNo)) { log.info("[手动退费]参数缺失 sys=[{}] outTradeNo={}, tradeNo={}", sys, outTradeNo, tradeNo); throw new ServiceException(ResultEnum.PARAM_IS_DEFECT); } BigDecimal refundFee = orderInfo.getPayMoney(); if (refundFee == null || refundFee.compareTo(BigDecimal.ZERO) == 0) { log.info("[手动退费]退款金额错误禁止退费 sys=[{}] outTradeNo={}, tradeNo={}", sys, outTradeNo, tradeNo); throw new ServiceException("退款金额错误禁止退费"); } // 支付类型判断 MerchantEnum merchantEnum = MerchantEnum.getMerchantEnumByOutTradeNo(outTradeNo); if (merchantEnum == null) { throw new ServiceException(ResultEnum.PAY_TYPE_ERROR); } if (MerchantEnum.ALI.equals(merchantEnum) && size != 1) { throw new ServiceException(String.format("[支付宝]缴费单数量为[%s],不为1禁止退费,请联系开发商", size)); } if (updateTime == null) { throw new ServiceException(ResultEnum.PARAM_IS_BLANK); } long time = (new Date().getTime() - updateTime.getTime()); int ruleTime = 45 * 60 * 1000; // 45 if (time < ruleTime) { throw new ServiceException("45分钟内禁止退费,剩余时间:" + DateHelper.getMillisecondToMinute(ruleTime - time)); } RefundDao refundDao = new RefundDao(); String refundResult = orderInfo.getRefundResult(); if ("OK".equals(refundResult)) { throw new ServiceException(ResultEnum.REFUND_STATUS_IS_CHANGED); } Order order = queryOrderByOutTradeNo(outTradeNo, null); // 未找到订单信息-并且未出现退费情况 if (!order.isSuccess() && !order.isRefund()) { throw new ServiceException(ResultEnum.PAY_ORDER_NOT_FOUND); } if (MerchantEnum.WX.equals(merchantEnum)) { if (order.isRefund()) { // 发生过退费(全额 | 部分) Bill bill = null; List billList = WxPayHelper.queryRefundList(outTradeNo); String bankTransNo = orderInfo.getBankTransNo(); String outRefundNo; for (Bill item : billList) { outRefundNo = item.getOutRefundNo(); if (outRefundNo.length() < 24) { throw new ServiceException(ResultEnum.REFUND_ORDER_IS_ERROR); } if (!bankTransNo.equals(outRefundNo.substring(0, outRefundNo.length() - 4))) { throw new ServiceException(ResultEnum.REFUND_MERCHANT_HAS_REFUND); } if (outRefundNo.equals(tradeNo)) { bill = item; break; } } if (bill != null) { // 已退费,但是状态不为OK orderInfo.setRefundResult("OK"); if (!refundDao.hasRefund(outTradeNo, tradeNo)) { // 不存在 orderInfo.setRefundUser(refundUser); orderInfo.setRefundTable(refundTable); if (!refundDao.insert(orderInfo, refundDesc, sys)) { log.info("{} [手动退费]退费信息存储失败 sys=[{}], {}", merchantEnum.NAME, sys, orderInfo); } } return orderInfo; } } } else if (MerchantEnum.ALI.equals(merchantEnum)) { // 支付宝 if (order.isRefund()) { // 支付宝发生过退费(全额退费) throw new ServiceException(ResultEnum.REFUND_STATUS_IS_CHANGED); } } else { throw new ServiceException(ResultEnum.PAY_TYPE_ERROR); } String begDate = DateHelper.getMoveDate(verifyDate, -6); String endDate = DateHelper.getMoveDate(verifyDate, 6); if (begDate == null || endDate == null) { log.info("[手动退费]日期转换失败 begDate={}, endDate={}, outTradeNo={}, tradeNo={}", begDate, endDate, outTradeNo, tradeNo); throw new ServiceException(ResultEnum.DATE_FORMAT_ERROR); } JsonResult jsonResult = new HisAccountDao().getTransaction(begDate, endDate, tradeNo, 0, 0); if (jsonResult.isTimeout()) { // 调用超时或异常 throw new ServiceException(jsonResult.getMessage()); } if (jsonResult.success()) { // 查询到费用明细-->已计费成功 log.info("[手动退费]His已记账禁止退费 sys=[{}], outTradeNo={}, tradeNo={}", sys, outTradeNo, tradeNo); throw new ServiceException("His已记账禁止退费"); } if (ObjectUtils.isEmpty(refundResult)) { if (!refundMethod.update()) { // 已发起退款 throw new ServiceException(ResultEnum.REFUND_STATUS_IS_CHANGED); } } PayCache.removeRecipe(tradeNo); Order refundInfo = refund(merchantEnum, outTradeNo, tradeNo, refundFee, totalFee, refundDesc, orderInfo.getUpdateTime(), orderInfo.getOpenid(), orderInfo.getPatientId(), null); if (!refundInfo.isSuccess()) { // 退款失败 throw new ServiceException(refundInfo.getErrorMsg()); } orderInfo.setRefundUser(refundUser); orderInfo.setRefundTable(refundTable); orderInfo.setRefundResult(refundInfo.getRefundResult()); if (!refundDao.insert(orderInfo, refundDesc, sys)) { log.info("{} [手动退费]退费信息存储失败 sys=[{}] {}", merchantEnum.NAME, sys, orderInfo); } return refundInfo; } /** * 退费封装 * * @param merchantEnum 支付方式 * @param outTradeNo 订单号 * @param refundMoney 退费金额 * @param totalFee 【微信】 退费总金额 * @param refundDesc 【微信】 退费描述 * @param openid 【微信】 推送者openid * @param patientId 【微信】 推送者患者id * @param pushInfo 【微信】 推送内容(为空不推送) * @return 退费信息 */ public static Order refund(MerchantEnum merchantEnum, String outTradeNo, String outRefundNo, BigDecimal refundMoney, BigDecimal totalFee, String refundDesc, Date tradeDate, String openid, String patientId, String pushInfo) { Order order = new Order(); if (merchantEnum == null || StringUtils.isEmpty(outTradeNo) || refundMoney == null) { return order; } switch (merchantEnum) { case WX: order = WxPayHelper.refund(outTradeNo, outRefundNo, refundMoney, totalFee, refundDesc); break; case ALI: order = AliHelper.refund(outTradeNo, outRefundNo, refundMoney, refundDesc); break; case BCM: log.info("【交行退款】outTradeNo={}, tradeDate={}", outTradeNo, tradeDate); String tradeDateStr = DateHelper.dateToStr(tradeDate, DateHelper.DateEnum.yyyyMMdd); if (tradeDateStr == null) { order.setErrorMsg("退款日期错误"); return order; } order = BCMHelper.refund(outTradeNo, outRefundNo, tradeDateStr, refundMoney, refundDesc); break; default: break; } return order; } /** * 查询订单信息 */ public static Order queryOrderByOutTradeNo(String outTradeNo, String payWay) { Order order = new Order(); if (outTradeNo == null) { order.setErrorMsg("订单号为空"); return order; } MerchantEnum merchantEnum = MerchantEnum.getMerchantEnumByOutTradeNo(outTradeNo); if (merchantEnum == null) { merchantEnum = MerchantEnum.getMerchantEnumByPayWay(payWay); if (merchantEnum == null) { order.setErrorMsg("支付方式不存在"); return order; } } if (MerchantEnum.WX.equals(merchantEnum)) { return WxPayHelper.queryOrder(outTradeNo); } else if (MerchantEnum.ALI.equals(merchantEnum)) { return AliHelper.queryTransaction(outTradeNo); } else if (MerchantEnum.BCM.equals(merchantEnum)) { return BCMHelper.queryOrder(outTradeNo); } order.setErrorMsg("支付方式不存在"); return order; } /** * [扫码支付] * * @param authCode 编码 * @return 支付类型 */ public static Order payMicro(MerchantEnum merchantEnum, String outTradeNo, String title, BigDecimal totalFee, String authCode, String ip) { Order order = new Order(); if (totalFee == null || merchantEnum == null || StringUtils.isEmpty(authCode)) { order.setOutTradeNo(outTradeNo); order.setErrorMsg("参数缺失"); return order; } if (merchantEnum.equals(MerchantEnum.WX)) { order = WxPayHelper.payMicro(title, outTradeNo, totalFee, authCode, ip); } else if (merchantEnum.equals(MerchantEnum.ALI)) { order = AliHelper.payMicro(title, outTradeNo, totalFee.toString(), authCode); } order.setOutTradeNo(outTradeNo); return order; } /** * [订单信息]存储 * * @param pEnum 支付类型 * @return 是否成功 */ public static boolean saveOrderNo(PEnum pEnum, String outTradeNo, String openid, String info, String authCode) { return new PayResultDao().insert(pEnum.CODE, outTradeNo, openid, info, authCode); } /** * [订单信息]查询用户订单 * * @return 是否存在 */ public static boolean hasUserOrderNo(String outTradeNo, String openid) { return new PayResultDao().queryOrderByOutTradeNoAndOpenid(outTradeNo, openid); } public static Order saveMicroOrderNo(PEnum pEnum, MerchantEnum merchantEnum, String outTradeNo, String authCode, String treatNum, String patientId, String operateUser, String invoiceTransNo, BigDecimal totalFee, String regDate) { Order order = new Order(); PayResult payResult = new PayResultDao().queryOrderByAuthCode(authCode); if (payResult != null) { order.setErrorMsg("付款码已使用过,请刷新重试"); return order; } String openid = "0"; if (!saveOrderNo(pEnum, outTradeNo, openid, null, authCode)) { order.setErrorMsg("订单号信息预存失败"); return order; } order.setOutTradeNo(outTradeNo); order.setAuthCode(authCode); order.setTotalFee(totalFee); order.setPayMoney(totalFee); order.setTreatNum(treatNum); order.setPatientId(patientId); order.setOperateUser(operateUser); order.setInvoiceTransNo(invoiceTransNo); order.setPayWay(merchantEnum.PAY_WAY_MICRO); order.setPayStatus(-1); order.setHisStatus(0); order.setOpenid(openid); boolean isResult = false; switch (pEnum) { case RECIPE: // 处方 Recipe recipe = CopyHelper.fatherToChild(order, Recipe.class); if (recipe == null) { order.setErrorMsg("[处方]赋值失败"); return order; } isResult = new RecipeDao().insert(recipe); break; case REG: // 挂号 Register register = CopyHelper.fatherToChild(order, Register.class); if (register == null) { order.setErrorMsg("[挂号]赋值失败"); return order; } register.setRegDate(regDate); register.setRegType(DateHelper.isToday(regDate) ? "2" : "1"); isResult = new RegisterDao().insert(register); break; case IN_HOSP: // 住院 PayInHosp payInHosp = CopyHelper.fatherToChild(order, PayInHosp.class); if (payInHosp == null) { order.setErrorMsg("[住院]赋值失败"); return order; } isResult = new InHospPayDao().insert(payInHosp); break; default: order.setErrorMsg("调用码无效"); break; } if (!isResult) { order.setErrorMsg("订单信息预存失败"); } order.setSuccess(isResult); return order; } // /** // * [盒子支付]信息存储 // * // * @param pEnum 支付类型 // * @return 是否成功 // */ // public static Order savePayMicro(PEnum pEnum, String outTradeNo, String bankTransNo, String openid) { // Order orderResult = new Order(); // boolean isResult = false; // order.setHisStatus(order.isSuccess() ? 0 : -1); // order.setPayStatus(order.isSuccess() ? 0 : -1); // // order.setPayMoney(order.getTotalFee()); // switch (pEnum) { // case RECIPE: // 处方 // Recipe recipe = CopyHelper.fatherToChild(order, Recipe.class); // if (recipe == null) { // order.setErrorMsg("[处方]赋值失败"); // return order; // } // isResult = new RecipeDao().insert(recipe); // break; // // case REG: // 挂号 // Register register = CopyHelper.fatherToChild(order, Register.class); // if (register == null) { // order.setErrorMsg("[挂号]赋值失败"); // return order; // } // register.setRegDate(regDate); // register.setRegType(DateHelper.isToday(regDate) ? "2" : "1"); // register.setRegDate(regDate); // isResult = new RegisterDao().insert(register); // break; // // case IN_HOSP: // 住院 // PayInHosp payInHosp = CopyHelper.fatherToChild(order, PayInHosp.class); // if (payInHosp == null) { // order.setErrorMsg("[住院]赋值失败"); // return order; // } // isResult = new InHospPayDao().insert(payInHosp); // break; // // default: // orderResult.setErrorMsg("调用码无效"); // break; // } // orderResult.setSuccess(isResult); // return orderResult; // } // /** // * 判断是否为新版订单号 // * // * @param bankTransNo 商户交易流水号 // * @param tradeNo HIS订单号 // * @return 是否为新版订单号 // * @throws ServiceException 业务异常 // */ // public static boolean isNewOrder(String bankTransNo, String tradeNo) throws ServiceException { // if (ObjectUtils.isEmpty(bankTransNo) || ObjectUtils.isEmpty(tradeNo)) { // throw new ServiceException(ResultEnum.REFUND_ORDER_IS_ERROR); // } // int tradeNoLen = tradeNo.length(); // if (tradeNoLen < 24) { // throw new ServiceException(ResultEnum.REFUND_ORDER_IS_ERROR); // } // if ((bankTransNo.length() + 4) != tradeNoLen) { // throw new ServiceException(ResultEnum.REFUND_IS_OLD_ORDER_NUM); // } // return bankTransNo.equals(tradeNo.substring(0, tradeNoLen - 4)); // } /** * 是否退费成功 */ public static Bill queryRefund(MerchantEnum merchantEnum, String outTradeNo, String bankTransNo, String outRefundNo) throws ServiceException { if (MerchantEnum.WX.equals(merchantEnum)) { return WxPayHelper.queryRefund(outTradeNo, outRefundNo); } else if (MerchantEnum.ALI.equals(merchantEnum)) { return AliHelper.queryRefund(outTradeNo, outRefundNo); } return null; } /** * 唤起支付-创建订单 */ public static JSONObject createOrder(MerchantEnum merchantEnum, String openid, String patientId, String totalFee, String outTradeNo, String notifyType, String ip, String title) { if (merchantEnum == null || ObjectUtils.isEmpty(notifyType)) { return null; } if (MerchantEnum.WX.equals(merchantEnum)) { // 微信下单 return WxPayHelper.createOrder(merchantEnum, title, totalFee, outTradeNo, notifyType, ip, openid, patientId); } else if (MerchantEnum.ALI.equals(merchantEnum)) { // 支付宝下单 return AliHelper.createOrder(merchantEnum, title, outTradeNo, totalFee, notifyType, openid); } else if (MerchantEnum.BCM.equals(merchantEnum)) { return BCMHelper.createOrder(merchantEnum, title, outTradeNo, totalFee, notifyType); } return null; } /** * 医保跳转 */ public static JSONObject goMedical(MerchantEnum merchantEnum, PEnum pEnum) { if (MerchantEnum.WX_MEDICAL.equals(merchantEnum)) { // 微信医保(获取授权链接) return WxMedicalHelper.getMdAuthUrl("wx-medical.html", pEnum.CODE); } return null; } /** * 修改[扫码盒子]订单状态 * * @param pEnum 类型 * @param outTradeNo 订单号 * @param bankTransNo 流水号 * @param openid openid */ public static boolean updateMicroOrderState(PEnum pEnum, String outTradeNo, String bankTransNo, String openid) { if (pEnum == null) { PayResult order = new PayResultDao().queryOrderByOutTradeNo(outTradeNo); if (order == null) { return false; } pEnum = PEnum.toEnum(order.getOrderType()); } switch (pEnum) { case RECIPE: // 处方 return new RecipeService().updateMicroOrderState(outTradeNo, bankTransNo, openid); case REG: // 挂号 return new RegService().updateMicroOrderState(outTradeNo, bankTransNo, openid); case IN_HOSP: // 住院预交金 return new InHospService().updateMicroOrderState(outTradeNo, bankTransNo, openid); default: log.info("类型未匹配"); return false; } } }