You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
412 lines
16 KiB
412 lines
16 KiB
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<Bill> 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 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.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;
|
|
}
|
|
|
|
|
|
}
|
|
|