WxApiService.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package thyyxxk.wxservice_server.service;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.wechat.pay.java.core.util.GsonUtil;
  4. import com.wechat.pay.java.service.payments.jsapi.model.*;
  5. import com.wechat.pay.java.service.payments.model.Transaction;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9. import thyyxxk.wxservice_server.config.exception.ExceptionEnum;
  10. import thyyxxk.wxservice_server.constant.OrderType;
  11. import thyyxxk.wxservice_server.constant.medins.Insutype;
  12. import thyyxxk.wxservice_server.constant.medins.MedType;
  13. import thyyxxk.wxservice_server.dao.WxApiDao;
  14. import thyyxxk.wxservice_server.entity.ResultVo;
  15. import thyyxxk.wxservice_server.entity.appointment.DoctorInfo;
  16. import thyyxxk.wxservice_server.entity.paymzfee.MedinsPresettle;
  17. import thyyxxk.wxservice_server.entity.wxapi.JsApiSHA1;
  18. import thyyxxk.wxservice_server.entity.wxapi.GenMzPayQrcodeParam;
  19. import thyyxxk.wxservice_server.entity.wxapi.WxPayOrder;
  20. import thyyxxk.wxservice_server.entity.wxapi.WxPyQrcdPrm;
  21. import thyyxxk.wxservice_server.factory.wechatpay.WeChatPayService;
  22. import thyyxxk.wxservice_server.factory.wechatpay.model.NativePayResponse;
  23. import thyyxxk.wxservice_server.factory.wechatpay.model.WechatPayGlobalRequest;
  24. import thyyxxk.wxservice_server.utils.*;
  25. import java.math.BigDecimal;
  26. import java.util.*;
  27. import java.util.concurrent.TimeUnit;
  28. /**
  29. * @author dj
  30. */
  31. @Slf4j
  32. @Service
  33. public class WxApiService {
  34. private final WxApiDao dao;
  35. private final SavePayResultService savePayResultService;
  36. @Autowired
  37. public WxApiService(WxApiDao dao, SavePayResultService savePayResultService) {
  38. this.dao = dao;
  39. this.savePayResultService = savePayResultService;
  40. }
  41. public ResultVo<JsApiSHA1> getJsapiSHA1Sign(JsApiSHA1 data) throws Exception {
  42. data.setAppId(PropertiesUtil.getLocalProperty("appId"));
  43. data.setTicket(PropertiesUtil.getLocalProperty("ticket"));
  44. data.setNoncestr(SnowFlakeId.instance().nextId());
  45. data.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
  46. String toSign = "jsapi_ticket=" + data.getTicket() + "&noncestr=" + data.getNoncestr() +
  47. "&timestamp=" + data.getTimestamp() + "&url=" + data.getUrl();
  48. data.setSignature(WxPaySignUtil.sha1Encode(toSign));
  49. log.info("获取wxJsApi签名:{}", data);
  50. return ResultVoUtil.success(data);
  51. }
  52. public ResultVo<WxPayOrder> createPayOrder(WxPayOrder order) {
  53. WxPayOrder existOrder = getExistOrder(order);
  54. if (null != existOrder) {
  55. if (existOrder.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
  56. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR,
  57. "您已支付过一笔相同金额的订单,请勿重复支付。");
  58. }
  59. if (StringUtil.notBlank(existOrder.getPrepayId()) &&
  60. existOrder.getTradeState() == Transaction.TradeStateEnum.NOTPAY &&
  61. DateUtil.orderValid(existOrder.getCreateDatetime())) {
  62. return ResultVoUtil.success(existOrder);
  63. }
  64. if (null == order.getYjReqNo()) {
  65. order.setYjReqNo(existOrder.getYjReqNo());
  66. }
  67. }
  68. int payAmt = DecimalTool.moneyYuanToFen(order.getCashpayAmt());
  69. order.setOpenid(TokenUtil.getInstance().getUserOpenid());
  70. order.setTradeNo(SnowFlakeId.instance().nextId());
  71. order.setMchId(PropertiesUtil.getLocalProperty("mchId"));
  72. if (payAmt == 0) {
  73. order.setPrepayId("PREPARED");
  74. order.setTimeStamp(String.valueOf(System.currentTimeMillis()));
  75. order.setSerialNo(SnowFlakeId.instance().nextId());
  76. } else {
  77. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  78. .openid(order.getOpenid()).description(order.getBody())
  79. .outTradeNo(order.getTradeNo()).totalAmount(payAmt).build();
  80. PrepayWithRequestPaymentResponse response = new WeChatPayService().jsApiPrepay(request);
  81. order.setAppId(response.getAppId());
  82. order.setPrepayId(response.getPackageVal());
  83. order.setTimeStamp(response.getTimeStamp());
  84. order.setSerialNo(response.getNonceStr());
  85. order.setSignType(response.getSignType());
  86. order.setPaySign(response.getPaySign());
  87. }
  88. if (null == order.getFundpayAmt()) {
  89. order.setFundpayAmt(BigDecimal.ZERO);
  90. }
  91. if (null == order.getAcctpayAmt()) {
  92. order.setAcctpayAmt(BigDecimal.ZERO);
  93. }
  94. if (null == order.getCouponAmt()) {
  95. order.setCouponAmt(BigDecimal.ZERO);
  96. }
  97. BigDecimal discount = order.getFundpayAmt().add(order.getAcctpayAmt())
  98. .add(order.getCouponAmt());
  99. BigDecimal cashpayAmt = order.getTotalFee().subtract(discount);
  100. if (cashpayAmt.compareTo(BigDecimal.ZERO) < 0) {
  101. cashpayAmt = BigDecimal.ZERO;
  102. }
  103. order.setCashpayAmt(cashpayAmt);
  104. order.setPatientName(dao.selectPatientName(order.getPatientId()));
  105. order.setCreateDatetime(new Date());
  106. order.setTradeState(Transaction.TradeStateEnum.NOTPAY);
  107. dao.insertNewOrder(order);
  108. return ResultVoUtil.success(order);
  109. }
  110. private WxPayOrder getExistOrder(WxPayOrder order) {
  111. if (order.getOrderType() == OrderType.CLINIC_REGISTER.getCode() ||
  112. order.getOrderType() == OrderType.REGISTRATION.getCode()) {
  113. return dao.selectSameGhOrder(order.getPatientId(), order.getMzyRequestId(),
  114. order.getCashpayAmt());
  115. } else if (order.getOrderType() == OrderType.OUTPATIENT.getCode()) {
  116. return dao.selectSameMzPayOrder(order.getHisOrdNum(), order.getCashpayAmt());
  117. }
  118. return null;
  119. }
  120. public ResultVo<JSONObject> queryOrderStateOnly(String tradeNo) {
  121. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  122. .outTradeNo(tradeNo).build();
  123. Transaction transaction = new WeChatPayService().queryOrderState(request);
  124. JSONObject response = JSONObject.parseObject(GsonUtil.toJson(transaction));
  125. return ResultVoUtil.success(response);
  126. }
  127. public String queryOrderState(String tradeNo) {
  128. try {
  129. while (TradeVectorUtil.tradeNoBeingQuery(tradeNo) || TradeVectorUtil.tradeNoBeingRefund(tradeNo)) {
  130. log.info("订单号:{} 正在查询状态中,进入等待区。", tradeNo);
  131. TimeUnit.SECONDS.sleep(3);
  132. }
  133. TradeVectorUtil.addBeingQuery(tradeNo);
  134. WxPayOrder order = dao.selectOrderByTradeNo(tradeNo);
  135. OrderType orderType = OrderType.get(order.getOrderType());
  136. int hasSaved = 0;
  137. if (orderType == OrderType.REGISTRATION || orderType == OrderType.CLINIC_REGISTER) {
  138. hasSaved = dao.alreadyPaidRegisterFee(order.getTradeNo());
  139. } else if (orderType == OrderType.OUTPATIENT) {
  140. hasSaved = dao.alreadyPayed(order.getTradeNo());
  141. }
  142. if (hasSaved == 1) {
  143. dao.updateSuccessHisStatus(tradeNo);
  144. if (orderType == OrderType.REGISTRATION) {
  145. log.info("订单号:{} 的挂号信息已保存,无需再次查询订单状态。", order.getTradeNo());
  146. return DateUtil.formatDatetime(order.getPayDatetime());
  147. }
  148. log.info("订单号:{} 的门诊缴费信息已保存,无需再次查询订单状态。", order.getTradeNo());
  149. return DateUtil.formatDatetime(order.getPayDatetime());
  150. }
  151. Transaction transaction;
  152. if (order.getCashpayAmt().compareTo(BigDecimal.ZERO) == 0) {
  153. transaction = new Transaction();
  154. transaction.setTradeState(Transaction.TradeStateEnum.SUCCESS);
  155. transaction.setSuccessTime(DateUtil.formatDatetime(new Date()));
  156. } else {
  157. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  158. .outTradeNo(tradeNo).build();
  159. transaction = new WeChatPayService().queryOrderState(request);
  160. }
  161. Transaction.TradeStateEnum tradeState = transaction.getTradeState();
  162. if (tradeState == Transaction.TradeStateEnum.SUCCESS) {
  163. String successTime = transaction.getSuccessTime()
  164. .split("\\+")[0].replace("T", " ");
  165. dao.updatePayStatusAndPayTime(tradeNo, tradeState, successTime);
  166. switch (orderType) {
  167. case CLINIC_REGISTER:
  168. return savePayResultService.saveClinicRegister(order, successTime);
  169. case REGISTRATION:
  170. return savePayResultService.saveAppointment(order, successTime);
  171. case OUTPATIENT:
  172. return savePayResultService.saveMzChargeInfo(order, successTime);
  173. case INPATIENT_PRE_PAY:
  174. return savePayResultService.saveZyYjjInfo(order, successTime);
  175. case SELF_HELP_MACHINE:
  176. case INSPECTIONS:
  177. return "订单已支付。";
  178. default:
  179. return "ERROR:未识别到的订单类型,请联系服务中心。";
  180. }
  181. }
  182. dao.updatePayStatusAndQueryTimes(tradeNo, tradeState);
  183. return "ERROR:" + transaction.getTradeStateDesc();
  184. } catch (Exception e) {
  185. log.error("查询订单状态出错:", e);
  186. return "ERROR:服务器错误,请联系管理员处理。【订单号:" + tradeNo + "】";
  187. } finally {
  188. TradeVectorUtil.removeBeingQuery(tradeNo);
  189. }
  190. }
  191. public ResultVo<String> generateMzGuideBillPayQrcode(GenMzPayQrcodeParam param) {
  192. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  193. .totalAmount(param.getTotalAmt()).description("湖南泰和医院-门诊缴费").build();
  194. NativePayResponse response = new WeChatPayService().nativePrepay(request);
  195. long timesStamp = System.currentTimeMillis() / 1000;
  196. WxPayOrder order = new WxPayOrder();
  197. order.setAppId(PropertiesUtil.getLocalProperty("appId"));
  198. order.setBody("门诊缴费");
  199. order.setOpenid("");
  200. order.setPatientId(param.getPatientId());
  201. order.setPatientName(dao.selectPatientName(param.getPatientId()));
  202. order.setMchId(PropertiesUtil.getLocalProperty("mchId"));
  203. order.setTimeStamp(String.valueOf(timesStamp));
  204. order.setTradeNo(response.getTradeNo());
  205. order.setCreateDatetime(new Date());
  206. order.setTradeState(Transaction.TradeStateEnum.NOTPAY);
  207. order.setSerialNo(SnowFlakeId.instance().nextId());
  208. order.setOrderType(OrderType.OUTPATIENT.getCode());
  209. order.setHisOrdNum(param.getHisOrdNum());
  210. // MedinsPresettle presettle = getFundPayAmt(param.getHisOrdNum());
  211. order.setFundpayAmt(BigDecimal.ZERO);
  212. order.setAcctpayAmt(BigDecimal.ZERO);
  213. order.setCashpayAmt(DecimalTool.moneyFenToYuan(param.getTotalAmt()));
  214. order.setCouponAmt(BigDecimal.ZERO);
  215. order.setTotalFee(order.getCashpayAmt());
  216. dao.insertNewOrder(order);
  217. return ResultVoUtil.success(response.getCodeUrl());
  218. }
  219. public ResultVo<Map<String, String>> getWxPayQrcode(WxPyQrcdPrm param) {
  220. String patName = dao.selectPatientName(param.getPatientId());
  221. if (null == patName) {
  222. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有找到患者信息,请检查patientId是否正确!");
  223. }
  224. WxPayOrder order = new WxPayOrder();
  225. if (param.getOrderType() == OrderType.INPATIENT_PRE_PAY.getCode()) {
  226. if (StringUtil.isBlank(param.getInpatientNo())) {
  227. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "费用类型为住院预交金时,住院号不能为空!");
  228. }
  229. if (null == param.getAdmissTimes()) {
  230. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "费用类型为住院预交金时,住院次数不能为空!");
  231. }
  232. order.setInpatientNo(param.getInpatientNo());
  233. order.setAdmissTimes(param.getAdmissTimes());
  234. }
  235. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  236. .totalAmount(param.getTotalAmt()).description(param.getDescription()).build();
  237. NativePayResponse response = new WeChatPayService().nativePrepay(request);
  238. long timesStamp = System.currentTimeMillis() / 1000;
  239. String orderTypeName = OrderType.get(param.getOrderType()).getLabel();
  240. order.setAppId(PropertiesUtil.getLocalProperty("appId"));
  241. order.setBody(orderTypeName);
  242. order.setOpenid("");
  243. order.setTotalFee(DecimalTool.moneyFenToYuan(param.getTotalAmt()));
  244. order.setPatientId(param.getPatientId());
  245. order.setPatientName(patName);
  246. order.setMchId(PropertiesUtil.getLocalProperty("mchId"));
  247. order.setTimeStamp(String.valueOf(timesStamp));
  248. order.setTradeNo(response.getTradeNo());
  249. order.setCreateDatetime(new Date());
  250. order.setTradeState(Transaction.TradeStateEnum.NOTPAY);
  251. order.setSerialNo(SnowFlakeId.instance().nextId());
  252. order.setOrderType(param.getOrderType());
  253. order.setFundpayAmt(BigDecimal.ZERO);
  254. order.setAcctpayAmt(BigDecimal.ZERO);
  255. order.setCouponAmt(BigDecimal.ZERO);
  256. order.setCashpayAmt(order.getTotalFee());
  257. dao.insertNewOrder(order);
  258. Map<String, String> map = new HashMap<>();
  259. map.put("qrcodeUrl", response.getCodeUrl());
  260. map.put("tradeNo", response.getTradeNo());
  261. map.put("serialNo", order.getSerialNo());
  262. return ResultVoUtil.success(map);
  263. }
  264. private MedinsPresettle getFundPayAmt(String hisOrdNum) {
  265. String[] hisOrdNumSplitArray = hisOrdNum.split("_");
  266. String patNo = hisOrdNumSplitArray[0];
  267. int times = Integer.parseInt(hisOrdNumSplitArray[1]);
  268. MedinsPresettle presettle = dao.selectSettleinfo(patNo, times);
  269. if (null == presettle) {
  270. presettle = dao.selectMedinsPresettle2(patNo, times);
  271. }
  272. if (null == presettle) {
  273. presettle = new MedinsPresettle();
  274. presettle.setFundPaySumamt(BigDecimal.ZERO);
  275. presettle.setAcctPay(BigDecimal.ZERO);
  276. }
  277. return presettle;
  278. }
  279. public String closeWxOrder(WxPayOrder order) {
  280. String tradeNo = order.getTradeNo();
  281. if (StringUtil.isBlank(tradeNo)) {
  282. return "参数错误:[tradeNo]不能为空!";
  283. }
  284. Transaction.TradeStateEnum tradeState = dao.selectOrderStatus(tradeNo);
  285. if (null == tradeState) {
  286. return "订单不存在。";
  287. }
  288. if (tradeState == Transaction.TradeStateEnum.SUCCESS) {
  289. return "订单已支付,无法关闭。";
  290. }
  291. if (tradeState == Transaction.TradeStateEnum.CLOSED) {
  292. return "SUCCESS";
  293. }
  294. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  295. .outTradeNo(tradeNo).build();
  296. new WeChatPayService().closeOrder(request);
  297. dao.updatePayStatusOnly(tradeNo, Transaction.TradeStateEnum.CLOSED);
  298. return "SUCCESS";
  299. }
  300. public void closeUnpaidWxOrder(String hisOrdNum) {
  301. List<String> tradeNos = dao.selectTradeNoByHisOrdNum(hisOrdNum);
  302. for (String tradeNo : tradeNos) {
  303. WechatPayGlobalRequest request = new WechatPayGlobalRequest.Builder()
  304. .outTradeNo(tradeNo).build();
  305. new WeChatPayService().closeOrder(request);
  306. dao.updatePayStatusOnly(tradeNo, Transaction.TradeStateEnum.CLOSED);
  307. }
  308. }
  309. public ResultVo<DoctorInfo> getDoctorInfo(String doctorCode) {
  310. DoctorInfo doctorInfo = dao.selectDoctorInfo(doctorCode);
  311. if (null == doctorInfo) {
  312. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "未找到医生信息!");
  313. }
  314. return ResultVoUtil.success(doctorInfo);
  315. }
  316. public String saveInpatientPrepay(String tradeNo) {
  317. WxPayOrder order = dao.selectOrderByTradeNo(tradeNo);
  318. if (null == order) {
  319. return "ERROR:没有找到订单号【" + tradeNo + "】对应的订单。";
  320. }
  321. if (StringUtil.isBlank(order.getInpatientNo()) || null == order.getAdmissTimes()) {
  322. return "ERROR:预交金订单的住院号和住院次数不能为空。";
  323. }
  324. return savePayResultService.saveZyYjjInfo(order, DateUtil.formatDatetime(order.getPayDatetime()));
  325. }
  326. }