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.CodeHelper; 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 javax.servlet.http.HttpServletRequest; 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; } if (MerchantEnum.WX.equals(merchantEnum)) { order = WxPayHelper.refund(outTradeNo, outRefundNo, refundMoney, totalFee, refundDesc); // if (!ObjectUtils.isEmpty(pushInfo) && order.isSuccess()) { // CommonUtil.sendMessage(openid, patientId, pushInfo); // 推送消息 // } } else if (MerchantEnum.ALI.equals(merchantEnum)) { order = AliHelper.refund(outTradeNo, outRefundNo, refundMoney, refundDesc); } else if (MerchantEnum.BCM.equals(merchantEnum)) { 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); } 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 title, BigDecimal totalFee, String authCode, String ip) { Order order = new Order(); String outTradeNo = CodeHelper.getOutTradeNo(merchantEnum); 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 callNo 调用码 * @param order 支付信息 * @param regDate 挂号日期 * @return 是否成功 */ public static Order savePayMicro(String callNo, Order order, String regDate) { 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.toEnum(callNo)) { 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.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: 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; } }