123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- package thyyxxk.wxservice_server.service;
- import com.alibaba.fastjson.JSONObject;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.http.client.methods.CloseableHttpResponse;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.util.EntityUtils;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.DocumentHelper;
- import org.dom4j.Element;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.converter.HttpMessageConverter;
- import org.springframework.http.converter.StringHttpMessageConverter;
- import org.springframework.stereotype.Service;
- import org.springframework.web.client.RestTemplate;
- import thyyxxk.wxservice_server.config.exception.ExceptionEnum;
- import thyyxxk.wxservice_server.constant.OrderType;
- import thyyxxk.wxservice_server.constant.QuerySource;
- import thyyxxk.wxservice_server.constant.TradeState;
- import thyyxxk.wxservice_server.dao.WxApiDao;
- import thyyxxk.wxservice_server.entity.ResultVo;
- import thyyxxk.wxservice_server.entity.appointment.DoctorInfo;
- import thyyxxk.wxservice_server.entity.appointment.WeChatPayParam;
- import thyyxxk.wxservice_server.entity.wxapi.JsApiSHA1;
- import thyyxxk.wxservice_server.entity.wxapi.GenMzPayQrcodeParam;
- import thyyxxk.wxservice_server.entity.wxapi.WxPayOrder;
- import thyyxxk.wxservice_server.entity.wxapi.WxPyQrcdPrm;
- import thyyxxk.wxservice_server.utils.*;
- import java.nio.charset.StandardCharsets;
- import java.util.*;
- import java.util.concurrent.TimeUnit;
- /**
- * @author dj
- */
- @Slf4j
- @Service
- public class WxApiService {
- private final WxApiDao dao;
- private final SpringRetryService retryService;
- private final SavePayResultService savePayResultService;
- @Autowired
- public WxApiService(WxApiDao dao, SpringRetryService retryService, SavePayResultService savePayResultService) {
- this.dao = dao;
- this.retryService = retryService;
- this.savePayResultService = savePayResultService;
- }
- public ResultVo<JsApiSHA1> getJsapiSHA1Sign(JsApiSHA1 data) {
- data.setAppId(PropertiesUtil.getProperty("appId"));
- data.setTicket(PropertiesUtil.getProperty("ticket"));
- data.setNoncestr(SnowFlakeId.instance().nextId());
- data.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
- String toSign = "jsapi_ticket=" + data.getTicket() + "&noncestr=" + data.getNoncestr() +
- "×tamp=" + data.getTimestamp() + "&url=" + data.getUrl();
- data.setSignature(WxPaySignUtil.sha1Encode(toSign));
- log.info("获取wxJsApi签名:{}", data);
- return ResultVoUtil.success(data);
- }
- public ResultVo<WxPayOrder> createPayOrder(WeChatPayParam param) {
- WxPayOrder existOrder = null;
- if (param.getOrderType() == OrderType.REGISTRATION.getCode()) {
- existOrder = dao.selectSameGhOrder(param.getPatientId(), param.getMzyRequestId());
- } else if (param.getOrderType() == OrderType.OUTPATIENT.getCode()) {
- existOrder = dao.selectSameMzPayOrder(param.getHisOrdNum());
- }
- if (null != existOrder) {
- if (existOrder.getPayStatus() == TradeState.SUCCESS.getCode()) {
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "您已成功支付过一笔相同金额的订单,请勿重复支付。");
- }
- if (StringUtil.notBlank(existOrder.getPaySign()) && StringUtil.notBlank(existOrder.getPrepayId())) {
- if (existOrder.getPayStatus() == TradeState.NEWORDER.getCode() || existOrder.getPayStatus() == TradeState.NOTPAY.getCode()) {
- if (DateUtil.orderValid(existOrder.getCreateDatetime())) {
- return ResultVoUtil.success(existOrder);
- }
- }
- if (null == param.getYjReqNo()) {
- param.setYjReqNo(existOrder.getYjReqNo());
- }
- }
- }
- String appId = PropertiesUtil.getProperty("appId");
- String merchantId = PropertiesUtil.getProperty("mchId");
- String tradeNo = SnowFlakeId.instance().nextId();
- String nonceStr = SnowFlakeId.instance().nextId();
- String totalFee = DecimalTool.moneyYuanToFen(param.getTotalFee());
- String notifyUrl = "http://staticweb.hnthyy.cn/wxserver/wxPayNotify/notify2";
- TreeMap<String, String> map = new TreeMap<>();
- map.put("appid", appId);
- map.put("mch_id", merchantId);
- map.put("device_info", "WEB");
- map.put("body", param.getBody());
- map.put("trade_type", "JSAPI");
- map.put("nonce_str", nonceStr);
- map.put("notify_url", notifyUrl);
- map.put("out_trade_no", tradeNo);
- map.put("total_fee", totalFee);
- map.put("spbill_create_ip", param.getClientIp());
- map.put("openid", param.getOpenId());
- String createOrderSign = WxPaySignUtil.createSign(map);
- String xml = "<xml>" +
- "<appid>" + appId + "</appid>" +
- "<body>" + param.getBody() + "</body>" +
- "<device_info>WEB</device_info>" +
- "<mch_id>" + merchantId + "</mch_id>" +
- "<nonce_str>" + nonceStr + "</nonce_str>" +
- "<notify_url>" + notifyUrl + "</notify_url>" +
- "<openid>" + param.getOpenId() + "</openid>" +
- "<out_trade_no>" + tradeNo + "</out_trade_no>" +
- "<total_fee>" + totalFee + "</total_fee>" +
- "<spbill_create_ip>" + param.getClientIp() + "</spbill_create_ip>" +
- "<trade_type>JSAPI</trade_type>" +
- "<sign>" + createOrderSign + "</sign>" +
- "</xml>";
- String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
- RestTemplate restTemplate = new RestTemplate();
- List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
- for (HttpMessageConverter<?> httpMessageConverter : list) {
- if (httpMessageConverter instanceof StringHttpMessageConverter) {
- ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);
- break;
- }
- }
- String str = restTemplate.postForObject(url, xml, String.class);
- try {
- assert str != null;
- Document document = DocumentHelper.parseText(str);
- Element root = document.getRootElement();
- if ("SUCCESS".equals(root.element("return_code").getStringValue())) {
- TreeMap<String, String> back = new TreeMap<>();
- if (null == root.element("prepay_id")) {
- final String message = root.element("err_code_des").getStringValue();
- log.info("微信统一下单失败:{}", message);
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, message);
- }
- String prepayId = root.element("prepay_id").getStringValue();
- String timeStamp = Long.toString(System.currentTimeMillis());
- back.put("appId", PropertiesUtil.getProperty("appId"));
- back.put("timeStamp", timeStamp);
- back.put("nonceStr", nonceStr);
- back.put("package", "prepay_id=" + prepayId);
- back.put("signType", "MD5");
- String paySign = WxPaySignUtil.createSign(back);
- WxPayOrder order = new WxPayOrder();
- order.setBody(param.getBody());
- order.setOrderType(param.getOrderType());
- order.setOpenId(param.getOpenId());
- order.setTotalFee(param.getTotalFee());
- order.setPatientId(param.getPatientId());
- order.setPatientName(dao.selectPatientName(param.getPatientId()));
- order.setAppId(appId);
- order.setMchId(merchantId);
- order.setPrepayId(prepayId);
- order.setTimeStamp(timeStamp);
- order.setSerialNo(nonceStr);
- order.setTradeNo(tradeNo);
- order.setCreateOrderSign(createOrderSign);
- order.setPaySign(paySign);
- order.setSpbillCreateIp(param.getClientIp());
- order.setCreateDatetime(new Date());
- order.setPayStatus(TradeState.NEWORDER.getCode());
- order.setHisOrdNum(param.getHisOrdNum());
- order.setMzyRequestId(param.getMzyRequestId());
- order.setYjReqNo(param.getYjReqNo());
- order.setInpatientNo(param.getInpatientNo());
- order.setAdmissTimes(param.getAdmissTimes());
- dao.insertNewOrder(order);
- log.info("统一下单成功:{}", order);
- return ResultVoUtil.success(order);
- }
- final String message = root.element("return_msg").getStringValue();
- log.error("微信统一下单失败:{}", message);
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, message);
- } catch (DocumentException e) {
- e.printStackTrace();
- return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, e.getMessage());
- }
- }
- public ResultVo<Object> queryOrderState(String tradeNo, QuerySource source) throws Exception {
- while (TradeVectorUtil.tradeNoBeingQuery(tradeNo)) {
- log.info("订单号:{} 正在查询状态中,进入等待区。", tradeNo);
- TimeUnit.SECONDS.sleep(3);
- }
- TradeVectorUtil.add(tradeNo);
- try {
- WxPayOrder order = dao.selectOrderByTradeNo(tradeNo);
- OrderType orderType = OrderType.get(order.getOrderType());
- int hasSaved = 0;
- if (orderType == OrderType.REGISTRATION) {
- hasSaved = savePayResultService.queryAppointmentSaveStatus(order.getTradeNo(),
- order.getSerialNo(), order.getTotalFee().toPlainString(), order.getPayDatetime());
- } else if (orderType == OrderType.OUTPATIENT) {
- hasSaved = savePayResultService.queryMzPaySaveStatus(order.getHisOrdNum(), order.getTradeNo(),
- order.getSerialNo(), order.getTotalFee().toPlainString(), order.getPayDatetime());
- }
- if (hasSaved == 1) {
- dao.updateSuccessHisStatus(tradeNo);
- if (orderType == OrderType.REGISTRATION) {
- log.info("订单号:{} 的挂号信息已保存,无需再次查询订单状态。", order.getTradeNo());
- return ResultVoUtil.success("保存挂号信息成功。");
- } else {
- log.info("订单号:{} 的门诊缴费信息已保存,无需再次查询订单状态。", order.getTradeNo());
- if (source == QuerySource.INTERFACE) {
- String hisOrdNum = order.getHisOrdNum();
- if (StringUtil.notBlank(hisOrdNum)) {
- String[] hsrdnms = hisOrdNum.split("_");
- Integer isCovidExam = dao.selectSelfCovidExamReceiptCount(hsrdnms[0], hsrdnms[1], hsrdnms[2]);
- if (null != isCovidExam && isCovidExam > 0) {
- order.setBody("新冠肺炎核酸检测");
- return ResultVoUtil.success("showBill", order);
- }
- }
- }
- return ResultVoUtil.success("保存门诊缴费信息成功。");
- }
- }
- JSONObject obj = retryService.queryOrderStateFromTencent(tradeNo, orderType, source);
- String value = obj.getString("trade_state");
- TradeState tradeState = TradeState.get(value);
- if (tradeState.equals(TradeState.SUCCESS)) {
- String successTime = obj.getString("success_time")
- .split("\\+")[0].replace("T", " ");
- dao.updatePayStatusAndPayTime(tradeNo, tradeState.getCode(), successTime);
- switch (orderType) {
- case REGISTRATION:
- return savePayResultService.saveAppointment(order);
- case OUTPATIENT:
- return savePayResultService.saveMzChargeInfo(order, source, successTime);
- case INPATIENT_PRE_PAY:
- return savePayResultService.saveZyYjjInfo(order);
- case SELF_HELP_MACHINE:
- case INSPECTIONS:
- return ResultVoUtil.success("订单已支付。");
- default:
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "未识别到的订单类型,请联系服务中心。");
- }
- }
- dao.updatePayStatusAndQueryTimes(tradeNo, tradeState.getCode());
- return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, tradeState.getLabel());
- } catch (Exception e) {
- e.printStackTrace();
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "服务器错误,请联系管理员处理。【订单号:" + tradeNo + "】");
- } finally {
- TradeVectorUtil.remove(tradeNo);
- }
- }
- public ResultVo<String> generateMzGuideBillPayQrcode(GenMzPayQrcodeParam param) throws Exception {
- String outTradeNo = SnowFlakeId.instance().nextId();
- JSONObject body = new JSONObject();
- body.put("appid", PropertiesUtil.getProperty("appId"));
- body.put("mchid", PropertiesUtil.getProperty("mchId"));
- body.put("description", "湖南泰和医院-门诊缴费");
- body.put("attach", "湖南泰和医院-门诊缴费");
- body.put("out_trade_no", outTradeNo);
- body.put("notify_url", PropertiesUtil.getProperty("notifyUrl"));
- JSONObject cny = new JSONObject();
- cny.put("total", param.getTotalAmt());
- cny.put("currency", "CNY");
- body.put("amount", cny);
- String reqdata = JSONObject.toJSONString(body);
- log.info("请求门诊指引单二维码:{}", reqdata);
- HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
- StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
- entity.setContentEncoding("UTF-8");
- entity.setContentType("application/json");
- httpPost.setEntity(entity);
- httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
- httpPost.addHeader("Accept", "application/json");
- //完成签名并执行请求
- CloseableHttpClient httpClient = WxHttpUtil.getClosableHttpClient();
- CloseableHttpResponse response = httpClient.execute(httpPost);
- int statusCode = response.getStatusLine().getStatusCode();
- String ret = EntityUtils.toString(response.getEntity());
- httpClient.close();
- if (statusCode == ExceptionEnum.SUCCESS.getCode()) {
- long timesStamp = System.currentTimeMillis() / 1000;
- WxPayOrder order = new WxPayOrder();
- order.setAppId(PropertiesUtil.getProperty("appId"));
- order.setBody("门诊缴费");
- order.setOpenId("");
- order.setTotalFee(DecimalTool.moneyFenToYuan(param.getTotalAmt()));
- order.setPatientId(param.getPatientId());
- order.setPatientName(dao.selectPatientName(param.getPatientId()));
- order.setMchId(PropertiesUtil.getProperty("mchId"));
- order.setTimeStamp(String.valueOf(timesStamp));
- order.setTradeNo(outTradeNo);
- order.setCreateDatetime(new Date());
- order.setPayStatus(TradeState.NOTPAY.getCode());
- order.setSerialNo(SnowFlakeId.instance().nextId());
- order.setOrderType(OrderType.OUTPATIENT.getCode());
- order.setHisOrdNum(param.getHisOrdNum());
- dao.insertNewOrder(order);
- JSONObject retObj = JSONObject.parseObject(ret);
- return ResultVoUtil.success(retObj.getString("code_url"));
- }
- log.error("请求门诊指引单二维码失败:{}", ret);
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请求微信支付二维码失败,请联系管理员。");
- }
- public ResultVo<Map<String, String>> getWxPayQrcode(WxPyQrcdPrm prm) throws Exception {
- String patName = dao.selectPatientName(prm.getPatientId());
- if (null == patName) {
- return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有找到患者信息,请检查patientId是否正确!");
- }
- WxPayOrder order = new WxPayOrder();
- if (prm.getOrderType() == OrderType.INPATIENT_PRE_PAY.getCode()) {
- if (StringUtil.isBlank(prm.getInpatientNo())) {
- return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "费用类型为住院预交金时,住院号不能为空!");
- }
- if (null == prm.getAdmissTimes()) {
- return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "费用类型为住院预交金时,住院次数不能为空!");
- }
- order.setInpatientNo(prm.getInpatientNo());
- order.setAdmissTimes(prm.getAdmissTimes());
- }
- String outTradeNo = SnowFlakeId.instance().nextId();
- JSONObject body = new JSONObject();
- body.put("appid", PropertiesUtil.getProperty("appId"));
- body.put("mchid", PropertiesUtil.getProperty("mchId"));
- body.put("description", prm.getDescription());
- body.put("attach", prm.getDescription());
- body.put("out_trade_no", outTradeNo);
- body.put("notify_url", PropertiesUtil.getProperty("notifyUrl"));
- JSONObject cny = new JSONObject();
- cny.put("total", prm.getTotalAmt());
- cny.put("currency", "CNY");
- body.put("amount", cny);
- String reqdata = JSONObject.toJSONString(body);
- HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
- StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
- entity.setContentEncoding("UTF-8");
- entity.setContentType("application/json");
- httpPost.setEntity(entity);
- httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
- httpPost.addHeader("Accept", "application/json");
- CloseableHttpClient httpClient = WxHttpUtil.getClosableHttpClient();
- CloseableHttpResponse response = httpClient.execute(httpPost);
- int statusCode = response.getStatusLine().getStatusCode();
- String ret = EntityUtils.toString(response.getEntity());
- httpClient.close();
- log.info("请求微信支付二维码:{},结果:{}", prm, ret);
- if (statusCode == ExceptionEnum.SUCCESS.getCode()) {
- long timesStamp = System.currentTimeMillis() / 1000;
- String orderTypeName = OrderType.get(prm.getOrderType()).getLabel();
- order.setAppId(PropertiesUtil.getProperty("appId"));
- order.setBody(orderTypeName);
- order.setOpenId("");
- order.setTotalFee(DecimalTool.moneyFenToYuan(prm.getTotalAmt()));
- order.setPatientId(prm.getPatientId());
- order.setPatientName(patName);
- order.setMchId(PropertiesUtil.getProperty("mchId"));
- order.setTimeStamp(String.valueOf(timesStamp));
- order.setTradeNo(outTradeNo);
- order.setCreateDatetime(new Date());
- order.setPayStatus(TradeState.NOTPAY.getCode());
- order.setSerialNo(SnowFlakeId.instance().nextId());
- order.setOrderType(prm.getOrderType());
- dao.insertNewOrder(order);
- JSONObject retObj = JSONObject.parseObject(ret);
- Map<String, String> map = new HashMap<>();
- map.put("qrcodeUrl", retObj.getString("code_url"));
- map.put("tradeNo", order.getTradeNo());
- map.put("serialNo", order.getSerialNo());
- return ResultVoUtil.success(map);
- }
- return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请求微信支付二维码失败,请联系管理员。");
- }
- public ResultVo<String> closeWxOrder(WxPayOrder order) throws Exception {
- Integer payStatus = dao.selectOrderStatus(order.getTradeNo());
- if (null == payStatus) {
- return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "订单不存在,请检查订单号是否正确。");
- }
- if (payStatus == TradeState.CLOSED.getCode()) {
- return ResultVoUtil.success("订单已是关闭状态,请勿重复关闭订单。");
- }
- String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + order.getTradeNo() + "/close";
- JSONObject body = new JSONObject();
- body.put("mchid", PropertiesUtil.getProperty("mchId"));
- String reqdata = JSONObject.toJSONString(body);
- HttpPost httpPost = new HttpPost(url);
- StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
- entity.setContentEncoding("UTF-8");
- entity.setContentType("application/json");
- httpPost.setEntity(entity);
- httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
- httpPost.addHeader("Accept", "application/json");
- CloseableHttpClient httpClient = WxHttpUtil.getClosableHttpClient();
- CloseableHttpResponse response = httpClient.execute(httpPost);
- int statusCode = response.getStatusLine().getStatusCode();
- log.info("关闭微信订单:{},结果:{}", order.getTradeNo(), statusCode);
- if (statusCode == 204) {
- httpClient.close();
- dao.updatePayStatusOnly(order.getTradeNo(), TradeState.CLOSED.getCode());
- return ResultVoUtil.success("关闭订单成功。");
- }
- if (null != response.getEntity()) {
- String ret = EntityUtils.toString(response.getEntity());
- return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, ret);
- }
- return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "关闭订单失败。");
- }
- public ResultVo<DoctorInfo> getDoctorInfo(String doctorCode) {
- DoctorInfo doctorInfo = dao.selectDoctorInfo(doctorCode);
- if (null == doctorInfo) {
- return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "未找到医生信息!");
- }
- return ResultVoUtil.success(doctorInfo);
- }
- }
|