diff --git a/src/main/java/com/ynxbd/ali/config/AliMIConfig.java b/src/main/java/com/ynxbd/ali/config/AliMIConfig.java index 3ee5e3f..c496686 100644 --- a/src/main/java/com/ynxbd/ali/config/AliMIConfig.java +++ b/src/main/java/com/ynxbd/ali/config/AliMIConfig.java @@ -19,7 +19,7 @@ public class AliMIConfig { public static boolean IS_DEV; public static boolean IS_AUTO_REFUND; - + public static String DEV_OPENID; static { ProperHelper config = new ProperHelper().read("medical-ali.properties"); @@ -28,6 +28,7 @@ public class AliMIConfig { IS_DEV = config.getBoolean("mi_ali.is_dev", true); IS_AUTO_REFUND = config.getBoolean("mi_ali.is_auto_refund", false); + DEV_OPENID = config.getString("mi_ali.dev_openid", null); ORG_NO = config.getString("mi_ali.org_no"); ORG_APP_ID = config.getString("mi_ali.org_app_id"); diff --git a/src/main/java/com/ynxbd/ali/helper/AliHelper.java b/src/main/java/com/ynxbd/ali/helper/AliHelper.java index cd87d4b..6d20c09 100644 --- a/src/main/java/com/ynxbd/ali/helper/AliHelper.java +++ b/src/main/java/com/ynxbd/ali/helper/AliHelper.java @@ -445,14 +445,18 @@ public class AliHelper { * 交易通知去重 * * @param outTradeNo 商户订单号,64个字符以内,可包含字母、数字、下划线,需保证在商户端不重复 + * @param isMIPay 是否为医保支付 */ - public static Order payNotify(String outTradeNo) { + public static Order paidNotify(String outTradeNo, boolean isMIPay) { Order order = new Order(); // 已处理 去重 if (KEYS.isContainsKey(outTradeNo)) { order.setErrorMsg("【支付宝】[重复请求]下单信息去重 outTradeNo=" + outTradeNo); return order; } + if (isMIPay) { + return AliMedicalHelper.queryTrade(outTradeNo, null); + } return queryTransaction(outTradeNo); } diff --git a/src/main/java/com/ynxbd/ali/helper/AliMedicalHelper.java b/src/main/java/com/ynxbd/ali/helper/AliMedicalHelper.java index ea7e30a..49d2edb 100644 --- a/src/main/java/com/ynxbd/ali/helper/AliMedicalHelper.java +++ b/src/main/java/com/ynxbd/ali/helper/AliMedicalHelper.java @@ -6,14 +6,18 @@ import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.domain.AlipayCommerceMedicalAuthinfoAuthQueryModel; +import com.alipay.api.domain.AlipayTradeQueryModel; import com.alipay.api.domain.AuthExtendParams; import com.alipay.api.domain.MedicalNationalPayAuthInfo; import com.alipay.api.request.AlipayCommerceMedicalAuthinfoAuthQueryRequest; import com.alipay.api.request.AlipayTradeAppPayRequest; +import com.alipay.api.request.AlipayTradeQueryRequest; import com.alipay.api.response.AlipayCommerceMedicalAuthinfoAuthQueryResponse; import com.alipay.api.response.AlipayTradeAppPayResponse; +import com.alipay.api.response.AlipayTradeQueryResponse; import com.ynxbd.ali.config.AliConfig; import com.ynxbd.ali.config.AliMIConfig; +import com.ynxbd.common.bean.pay.Order; import com.ynxbd.common.helper.common.DateHelper; import com.ynxbd.common.helper.common.ErrorHelper; import com.ynxbd.common.helper.common.JsonHelper; @@ -22,9 +26,8 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.net.URLDecoder; +import java.util.*; @Slf4j public class AliMedicalHelper { @@ -39,7 +42,7 @@ public class AliMedicalHelper { * @param callUrl 回调地址 * @return */ - public static MedicalNationalPayAuthInfo queryMIAuth(String openId, String accessToken, String patientName, String patientCardNo, String callUrl) { + public static MedicalNationalPayAuthInfo queryMIAuth(String openId, String accessToken, String patientName, String patientCardNo, String reqBizNo, String callUrl) { try { // 初始化SDK AlipayClient alipayClient = new DefaultAlipayClient(AliConfig.getAlipayConfig()); @@ -57,7 +60,7 @@ public class AliMedicalHelper { } // 设置商户请求流水号 - model.setReqBizNo(UUID.randomUUID().toString()); + model.setReqBizNo(ObjectUtils.isEmpty(reqBizNo) ? UUID.randomUUID().toString() : reqBizNo); // 设置机构编码-固定为NATHSA model.setInsCode("NATHSA"); // 设置线上业务类型编码 @@ -94,7 +97,6 @@ public class AliMedicalHelper { // 设置auth_token,同access_token(用户访问令牌) request.putOtherTextParam("auth_token", accessToken); - log.info("[支付宝]国家医保局线上业务身份核验信息授权查询接口-入参{}", JsonHelper.toJsonString(request)); AlipayCommerceMedicalAuthinfoAuthQueryResponse response = alipayClient.execute(request); log.info("[支付宝]国家医保局线上业务身份核验信息授权查询接口-调用{} {}", response.isSuccess() ? "[成功]" : "[失败]", response.getBody()); @@ -119,22 +121,24 @@ public class AliMedicalHelper { /** * 5.2.2.4 APP支付接口alipay.trade.app.pay * - * @param orderMIEnum 医保订单类型 - * @param openid openid - * @param outTradeNo 商户订单号 - * @param payOrdId 医保订单id - * @param payAuthNo 医保授权 - * @param totalAmount 总金额 - * @param cashFee 现金部分 - * @param subject 订单标题 - * @param notifyUrl 携带了业务类型,用于区分挂号和处方缴费 - * @param notifyType 业务类型 - * @param familyType 保卡类型"1":”亲情支付“,"0":"本人支付",为空默认本人支付。 - * @param familyName 家属姓名(这里不使用) - * @param familyCardNo 医保电子凭证授权码 获取线上医保业务授权接口返回medicalCardId参数。"6638aabe-7b4d-4d10-bdf0-b44804da3dab" + * @param orderMIEnum 医保订单类型 + * @param openid openid + * @param outTradeNo 商户订单号 + * @param payOrdId 医保订单id + * @param payAuthNo 医保授权 + * @param totalAmount 总金额 + * @param cashFee 现金部分 + * @param subject 订单标题 + * @param notifyUrl 携带了业务类型,用于区分挂号和处方缴费 + * @param notifyType 业务类型 + * @param familyType 保卡类型"1":”亲情支付“,"0":"本人支付",为空默认本人支付。 + * @param familyName 家属姓名 + * @param familyCardNo 家属卡号 + * @param medicalCardInstId 医保电子凭证机构编号,默认位NATHSA + * @param medicalCardId 医保电子凭证授权码 获取线上医保业务授权接口返回medicalCardId参数。"6638aabe-7b4d-4d10-bdf0-b44804da3dab" * @return map */ - public static Map createOrder(OrderMIEnum orderMIEnum, String openid, String outTradeNo, String payOrdId, String payAuthNo, BigDecimal totalAmount, BigDecimal cashFee, String subject, String notifyUrl, String notifyType, String familyType, String familyName, String familyCardNo) { + public static Map createOrder(OrderMIEnum orderMIEnum, String openid, String outTradeNo, String payOrdId, String payAuthNo, BigDecimal totalAmount, BigDecimal cashFee, String subject, String notifyUrl, String notifyType, String familyType, String familyName, String familyCardNo, String medicalCardInstId, String medicalCardId) { try { AlipayClient alipayClient = new DefaultAlipayClient(AliConfig.getAlipayConfig()); AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); @@ -143,18 +147,19 @@ public class AliMedicalHelper { JSONObject bizContent = new JSONObject(); bizContent.put("out_trade_no", outTradeNo); // 订单总金额 - bizContent.put("total_amount", totalAmount); + bizContent.put("total_amount", totalAmount.toString()); // 订单标题 bizContent.put("subject", subject); - bizContent.put("product_code", "JSAPI_PAY"); // JSAPI_PAY - //亲情账户增加 start + bizContent.put("product_code", "QUICK_MSECURITY_PAY"); // JSAPI_PAY|QUICK_MSECURITY_PAY + // 亲情账户增加 start bizContent.put("query_options", new String[]{"medical_insurance_info"}); - //亲情账户增加 end + // 亲情账户增加 end JSONObject extendParams = new JSONObject(); extendParams.put("sys_service_provider_id", AliConfig.ISV_PARTNER_ID); extendParams.put("IS_INSURANCE_PAY", "T"); // 是否使用医保支付 - extendParams.put("medical_card_inst_id", ObjectUtils.isEmpty(familyType) ? "0" : familyType); // 医保电子凭证机构号 获取线上医保业务授权接口返回medicalCardInstId参数。医保卡类型"1":”亲情支付“,"0":"本人支付",为空默认本人支付。 - extendParams.put("medical_card_id", familyCardNo); // 医保电子凭证授权码 获取线上医保业务授权接口返回medicalCardId参数。"6638aabe-7b4d-4d10-bdf0-b44804da3dab" + extendParams.put("medical_card_inst_id", ObjectUtils.isEmpty(medicalCardInstId) ? "NATHSA" : medicalCardInstId); //医保电子凭证机构号 获取线上医保业务授权接口返回medicalCardInstId参数。 + extendParams.put("medical_card_id", medicalCardId); //医保电子凭证授权码 获取线上医保业务授权接口返回medicalCardId参数。 + JSONObject medicalRequestContent = new JSONObject(); // 医保参数透传体,没有需传空对象串 extendParams.put("medical_request_content", JSONObject.toJSONString(medicalRequestContent)); @@ -176,28 +181,30 @@ public class AliMedicalHelper { extendParams.put("medical_request_ext", JSONObject.toJSONString(medicalRequestExt)); bizContent.put("extend_params", extendParams); - //如需补充资产,如院内预交金等 begin - JSONArray merchantCardZkt = new JSONArray(); - JSONObject zkt = new JSONObject(); - zkt.put("amount", cashFee); // 现金部分 - zkt.put("templateId", "2023062500391002246600206403"); - zkt.put("forceUse", "Y"); // 固定为Y - merchantCardZkt.add(zkt); - JSONObject presetPayTool = new JSONObject(); - presetPayTool.put("MERCHANTCARD_ZKT", merchantCardZkt); - extendParams.put("preset_pay_tool", JSONObject.toJSONString(presetPayTool)); - extendParams.put("scene", "PLATFORM_DRUG_STORE"); //场景(有三方购药渠道场景使用) + // 如需补充资产,如院内预交金等 begin + if (cashFee.compareTo(BigDecimal.ZERO) == 0) { // 0元现金支付 + JSONArray merchantCardZkt = new JSONArray(); + JSONObject zkt = new JSONObject(); + zkt.put("amount", totalAmount); // 总金额 + zkt.put("forceUse", "Y"); // 固定为Y + zkt.put("templateId", "2023062500391002246600206403"); + merchantCardZkt.add(zkt); + JSONObject presetPayTool = new JSONObject(); + presetPayTool.put("MERCHANTCARD_ZKT", merchantCardZkt); + extendParams.put("preset_pay_tool", JSONObject.toJSONString(presetPayTool)); + extendParams.put("scene", "PLATFORM_DRUG_STORE"); //场景(有三方购药渠道场景使用) + } // 如需补充资产,如院内预交金等 end - request.setBizContent(JSONObject.toJSONString(bizContent)); log.info("[支付宝]医保下单-入参 {}", JSONObject.toJSONString(request)); AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); - log.info("[支付宝]医保下单-返回 {}", response.getBody()); + log.info("[支付宝]医保下单-返回 orderStr={}", response.getBody()); + if (response.isSuccess()) { Map map = new HashMap<>(); - map.put("tradeNo", response.getTradeNo()); - map.put("outTradeNo", response.getOutTradeNo()); + map.put("orderStr", response.getBody()); + map.put("outTradeNo", outTradeNo); return map; } } catch (AlipayApiException e) { @@ -206,31 +213,54 @@ public class AliMedicalHelper { return null; } + + + // 5.2.2.5 线下交易查询alipay.trade.query - public static String queryTrade(String outTradeNo, String tradeNo) { + public static Order queryTrade(String outTradeNo, String tradeNo) { + Order order = new Order(); try { AlipayClient alipayClient = new DefaultAlipayClient(AliConfig.getAlipayConfig()); - AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); - JSONObject bizContent = new JSONObject(); - bizContent.put("trade_no", tradeNo); - bizContent.put("out_trade_no", outTradeNo); - bizContent.put("query_options", new String[]{"fund_bill_list", "medical_insurance_info"}); //查询本次交易用户付款的支付渠道和亲情账户关系 - - request.setBizContent(JSONObject.toJSONString(bizContent)); + // 构造请求参数以调用接口 + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + AlipayTradeQueryModel model = new AlipayTradeQueryModel(); + // 设置订单支付时传入的商户订单号 + model.setOutTradeNo(outTradeNo); + // 设置支付宝交易号 + model.setTradeNo(tradeNo); + + // 设置查询选项 + List queryOptions = new ArrayList<>(); + queryOptions.add("trade_settle_info"); + queryOptions.add("fund_bill_list"); + queryOptions.add("medical_insurance_info"); + model.setQueryOptions(queryOptions); + request.setBizModel(model); - AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); - log.info("[支付宝]线下交易查询-返回 {}", response.getBody()); + log.info("[支付宝][医保]线下交易查询-入参 {}", JSONObject.toJSONString(request)); + AlipayTradeQueryResponse response = alipayClient.execute(request); + log.info("[支付宝][医保]线下交易查询-返回 {}", JsonHelper.toJsonString(response)); if (response.isSuccess()) { - return response.getBody(); + if ("TRADE_SUCCESS".equals(response.getTradeStatus())) { + order.setSuccess(true); + } + order.setInfo(response.getBody()); + order.setTradeState(response.getTradeStatus()); + order.setTradeNo(response.getTradeNo()); + order.setBankTransNo(response.getTradeNo()); + order.setOutTradeNo(response.getOutTradeNo()); + order.setTotalFee(new BigDecimal(response.getTotalAmount())); + } else { + order.setErrorMsg(response.getMsg()); } } catch (AlipayApiException e) { ErrorHelper.println(e); } - return null; + return order; } // 5.2.2.6 交易退款接口alipay.trade.refund.apply - public static String refundTrade(String outRefundNo, BigDecimal refundAmount, String refundReason, String outTradeNo, String tradeNo) { + public static String refundTrade(String outTradeNo, String outRefundNo, String tradeNo, BigDecimal refundAmount, String refundReason) { try { AlipayClient alipayClient = new DefaultAlipayClient(AliConfig.getAlipayConfig()); AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); @@ -250,8 +280,9 @@ public class AliMedicalHelper { request.setBizContent(JSONObject.toJSONString(bizContent)); + log.info("[支付宝][医保]交易退款接口-入参 {}", JSONObject.toJSONString(request)); AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); - log.info("[支付宝]交易退款接口-返回 {}", response.getBody()); + log.info("[支付宝][医保]交易退款接口-返回 {}", response.getBody()); if (response.isSuccess()) { return response.getBody(); } @@ -286,4 +317,32 @@ public class AliMedicalHelper { return null; } + + // 数据转换 + private static String formatOrderInfo(String data) { + try { + if (ObjectUtils.isEmpty(data)) { + return null; + } + String decode = URLDecoder.decode(data, "UTF-8"); + if (ObjectUtils.isEmpty(decode)) { + return null; + } + String[] pairs = decode.split("&"); + Map map = new HashMap<>(); + for (String pair : pairs) { + if (ObjectUtils.isEmpty(pair)) { + continue; + } + String[] keyValue = pair.split("="); + if (keyValue.length == 2) { + map.put(keyValue[0], keyValue[1]); + } + } + return null; + } catch (Exception e) { + ErrorHelper.println(e); + return null; + } + } } diff --git a/src/main/java/com/ynxbd/common/action/pay/AliPayAction.java b/src/main/java/com/ynxbd/common/action/pay/AliPayAction.java index 47863fd..26cc591 100644 --- a/src/main/java/com/ynxbd/common/action/pay/AliPayAction.java +++ b/src/main/java/com/ynxbd/common/action/pay/AliPayAction.java @@ -43,7 +43,7 @@ public class AliPayAction extends BaseAction { return Result.respStr(); } - Order order = AliHelper.payNotify(outTradeNo); + Order order = AliHelper.paidNotify(outTradeNo, false); if (!order.isSuccess()) { log.info(order.getErrorMsg()); return Result.error(order.getErrorMsg()); @@ -70,8 +70,8 @@ public class AliPayAction extends BaseAction { break; case IN_HOSP: - boolean isInsert = new InHospService().inHospNotify(MerchantEnum.ALI, openid, totalFee, outTradeNo, bankTransNo, payDate, payTime, payInfo); - log.info("【微信】[住院预交金] 预存{}", (isInsert ? "成功" : "失败")); + boolean isOK = new InHospService().inHospNotify(MerchantEnum.ALI, openid, totalFee, outTradeNo, bankTransNo, payDate, payTime, payInfo); + log.info("【微信】[住院预交金] 预存{}", (isOK ? "成功" : "失败")); break; default: @@ -88,12 +88,13 @@ public class AliPayAction extends BaseAction { */ @Action("medical_notify") public Result medical_notify(String outTradeNo, String notifyType) { + // [支付宝][医保][收到通知] outTradeNo=ALI_1dedf988982a4142c20ee839d499, notifyType=recipe log.info("[支付宝][医保][收到通知] outTradeNo={}, notifyType={}", outTradeNo, notifyType); if (outTradeNo == null || notifyType == null) { return Result.respStr(); } - Order order = AliHelper.payNotify(outTradeNo); + Order order = AliHelper.paidNotify(outTradeNo, true); if (!order.isSuccess()) { log.info(order.getErrorMsg()); return Result.error(order.getErrorMsg()); @@ -107,9 +108,6 @@ public class AliPayAction extends BaseAction { String payDate = DateHelper.getCurDate(); String payTime = DateHelper.getCurTime(); - String body = AliMedicalHelper.queryTrade(outTradeNo, bankTransNo); - log.info("[支付宝][医保][查询订单] body={}", body); - new MedicalService().commonNotify(MerchantEnum.ALI_MEDICAL, notifyType, outTradeNo, totalFee, bankTransNo, payDate, payTime, openid, payInfo); return Result.respStr(); } @@ -120,9 +118,9 @@ public class AliPayAction extends BaseAction { * @return 是否成功 */ @Action("queryMIAuth") - public Result queryMIAuth(String openid, String accessToken, String patientName, String patientCardNo, String callUrl) { - log.info("[支付宝][医保]授权查询 openid={}, accessToken={}, patientName={}, patientCardNo={}, callUrl={}", openid, accessToken, patientName, patientCardNo, callUrl); - MedicalNationalPayAuthInfo dataInfo = AliMedicalHelper.queryMIAuth(openid, accessToken, patientName, patientCardNo, callUrl); + public Result queryMIAuth(String openid, String accessToken, String patientName, String patientCardNo, String reqBizNo, String callUrl) { + log.info("[支付宝][医保]授权查询 openid={}, accessToken={}, patientName={}, patientCardNo={}, reqBizNo={}, callUrl={}", openid, accessToken, patientName, patientCardNo, reqBizNo, callUrl); + MedicalNationalPayAuthInfo dataInfo = AliMedicalHelper.queryMIAuth(openid, accessToken, patientName, patientCardNo, reqBizNo, callUrl); log.info("[支付宝][医保]授权查询 {}", JsonHelper.toJsonString(dataInfo)); if (dataInfo == null) { return Result.error("授权查询失败"); diff --git a/src/main/java/com/ynxbd/common/action/pay/MedicalAction.java b/src/main/java/com/ynxbd/common/action/pay/MedicalAction.java index 1ddd9bd..b3882b2 100644 --- a/src/main/java/com/ynxbd/common/action/pay/MedicalAction.java +++ b/src/main/java/com/ynxbd/common/action/pay/MedicalAction.java @@ -1,11 +1,14 @@ package com.ynxbd.common.action.pay; import com.alibaba.fastjson.JSONObject; +import com.ynxbd.ali.config.AliMIConfig; +import com.ynxbd.ali.helper.AliMedicalHelper; import com.ynxbd.common.action.base.BaseAction; import com.ynxbd.common.bean.enums.MerchantEnum; import com.ynxbd.common.bean.pay.Order; import com.ynxbd.common.bean.pay.Recipe; import com.ynxbd.common.bean.pay.Register; +import com.ynxbd.common.config.interceptor.AesDecode; import com.ynxbd.common.dao.RecipeDao; import com.ynxbd.common.dao.his.HisMIDao; import com.ynxbd.common.helper.common.AesMicroHelper; @@ -28,7 +31,9 @@ import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Namespace; import java.math.BigDecimal; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * @Author wsq @@ -71,7 +76,7 @@ public class MedicalAction extends BaseAction { case RECIPE: String treatNum = getString("treatNum"); - String recipeJson = getString("recipeJson"); + String recipeJson = getString("recipeJson"); // 处方JSON集 [{ recipeId: item.id, recipeFee: item.amount }] if (treatNum == null || recipeJson == null) { return Result.error(ResultEnum.PARAM_IS_DEFECT); } @@ -176,9 +181,9 @@ public class MedicalAction extends BaseAction { */ @Action("order") public Result order(String callNo, String payCode, String openid, String patientId, String cardNo, String realName, String treatNum, String payOrdId, String payAuthNo, String mdTrtId, String chrgBchno, String mdUserId, - BigDecimal totalFee, BigDecimal acctFee, BigDecimal hifpFee, BigDecimal insuranceFee, BigDecimal cashFee, String familyType, String familyName, String familyCardNo) { + BigDecimal totalFee, BigDecimal acctFee, BigDecimal hifpFee, BigDecimal insuranceFee, BigDecimal cashFee, String familyType, String familyName, String familyCardNo, String medicalCardInstId, String medicalCardId) { try { - log.info("[医保]下单 callNo={}, payCode={}, openid={}, patientId={}, totalFee={}", callNo, payCode, openid, patientId, totalFee); + log.info("[医保]下单入参 callNo={}, payCode={}, openid={}, patientId={}, totalFee={}", callNo, payCode, openid, patientId, totalFee); if (callNo == null || payCode == null || openid == null || patientId == null || cardNo == null || totalFee == null) { return Result.error(ResultEnum.PARAM_IS_DEFECT); } @@ -188,8 +193,8 @@ public class MedicalAction extends BaseAction { Register reg = new Register().getRegParams(new RequestParams(request), true); return new MedicalService().createRegOrder(payCode, openid, cardNo, realName, ip, payOrdId, payAuthNo, mdTrtId, chrgBchno, mdUserId, - totalFee, acctFee, hifpFee, insuranceFee, cashFee, - callNo, reg, familyType, familyName, familyCardNo); + totalFee, acctFee, hifpFee, insuranceFee, cashFee, callNo, reg, + familyType, familyName, familyCardNo, medicalCardInstId, medicalCardId); case RECIPE: String recipeJson = getString("recipeJson"); @@ -200,7 +205,7 @@ public class MedicalAction extends BaseAction { return new MedicalService().createRxOrder(payCode, openid, patientId, cardNo, realName, ip, payOrdId, payAuthNo, mdTrtId, chrgBchno, mdUserId, totalFee, acctFee, hifpFee, insuranceFee, cashFee, - callNo, recipeJson, treatNum, payWay, familyType, familyName, familyCardNo); + callNo, recipeJson, treatNum, payWay, familyType, familyName, familyCardNo, medicalCardInstId, medicalCardId); default: return Result.error("调用方式不存在"); @@ -214,71 +219,77 @@ public class MedicalAction extends BaseAction { /** * HIS医保退费(线上) */ - @Action("refundMedical") - public Result refundMedical(String callNo, String openid, String qrCode, String outTradeNo, String tradeNo) { - try { - tradeNo = Base64Helper.decodeEn(tradeNo); - log.info("[医保退费] callNo={}, openid={}, qrCode={}, outTradeNo={}, tradeNo={}", callNo, openid, qrCode, outTradeNo, tradeNo); - if (openid == null || qrCode == null || outTradeNo == null || tradeNo == null) { - return Result.error(ResultEnum.PARAM_IS_DEFECT); - } - MedicalUserInfo userInfo = WxMedicalHelper.getUserInfo(openid, qrCode, null, null); - RecipeDao recipeDao = new RecipeDao(); - Recipe recipe = recipeDao.selectByTradeNo(tradeNo); - if (recipe == null) { - return Result.error(ResultEnum.DATA_NOT_FOUND); - } - Integer payStatus = recipe.getPayStatus(); - Integer hisStatus = recipe.getHisStatus(); - String chrgBchno = recipe.getChrgBchno(); - String bankTransNo = recipe.getBankTransNo(); - String mdUserId = recipe.getMdUserId(); - - BigDecimal cashFee = recipe.getPayMoney(); - BigDecimal acctFee = recipe.getAcctFee(); - BigDecimal hifpFee = recipe.getHifpFee(); - - if (payStatus == null || hisStatus == null || chrgBchno == null || bankTransNo == null || mdUserId == null || cashFee == null || acctFee == null || hifpFee == null) { - log.info("[医保]退费参数缺失 outTradeNo={}, bankTransNo={}, payStatus={}, hisStatus={}, chrgBchno={}, cashFee={}, acctFee={}, hifpFee={}", outTradeNo, bankTransNo, payStatus, hisStatus, chrgBchno, cashFee, acctFee, hifpFee); - return Result.error(ResultEnum.PAY_ORDER_PARAMS_IS_DEFECT); - } + @Action("refundMI") + public Result refundMI(String payCode, String callNo, String openid, String qrCode, String outTradeNo, @AesDecode String tradeNo) { +// tradeNo = Base64Helper.decodeEn(tradeNo); + log.info("[医保退费] callNo={}, openid={}, qrCode={}, outTradeNo={}, tradeNo={}", callNo, openid, qrCode, outTradeNo, tradeNo); + if (openid == null || qrCode == null || outTradeNo == null || tradeNo == null) { + return Result.error(ResultEnum.PARAM_IS_DEFECT); + } - BigDecimal insuranceFee = acctFee.add(hifpFee); - if (payStatus != 0) { // 都已成功禁止退费 - return Result.error(ResultEnum.PAY_ORDER_NO_PAY); - } + MerchantEnum merchantEnum = MerchantEnum.findEnumByCode(payCode); + if (merchantEnum == null) { // 支付方式异常 + return Result.error(ResultEnum.PAY_TYPE_ERROR); + } + + RecipeDao recipeDao = new RecipeDao(); + Recipe recipe = recipeDao.selectByTradeNo(tradeNo); + if (recipe == null) { + return Result.error(ResultEnum.DATA_NOT_FOUND); + } + + Boolean isDevUser = MedicalService.isDevUser(recipe.getOpenid()); + if (isDevUser == null || !isDevUser) { + return Result.error("不是测试用户"); + } + + Integer payStatus = recipe.getPayStatus(); + Integer hisStatus = recipe.getHisStatus(); + String chrgBchno = recipe.getChrgBchno(); + String bankTransNo = recipe.getBankTransNo(); + String mdUserId = recipe.getMdUserId(); + + BigDecimal cashFee = recipe.getPayMoney(); + BigDecimal acctFee = recipe.getAcctFee(); + BigDecimal hifpFee = recipe.getHifpFee(); + + if (payStatus == null || hisStatus == null || chrgBchno == null || bankTransNo == null || mdUserId == null || cashFee == null || acctFee == null || hifpFee == null) { + log.info("[医保]退费参数缺失 outTradeNo={}, bankTransNo={}, payStatus={}, hisStatus={}, chrgBchno={}, cashFee={}, acctFee={}, hifpFee={}", outTradeNo, bankTransNo, payStatus, hisStatus, chrgBchno, cashFee, acctFee, hifpFee); + return Result.error(ResultEnum.PAY_ORDER_PARAMS_IS_DEFECT); + } + + BigDecimal insuranceFee = acctFee.add(hifpFee); + if (payStatus != 0) { // 都已成功禁止退费 + return Result.error(ResultEnum.PAY_ORDER_NO_PAY); + } // if (hisStatus == 0) { // 都已成功禁止退费 // return Result.error(ResultEnum.PAY_ORDER_NO_PAY); // } - - Result result = new MedicalService().refund(userInfo.getPayAuthNo(), chrgBchno.substring(1), outTradeNo, bankTransNo, tradeNo, mdUserId, cashFee, insuranceFee); - - String message = result.isOK() ? WxPayHelper.OK : result.getMessage(); - if (!recipeDao.updateRefundByTradeNo(tradeNo, message)) { - log.info("[医保]修改退费信息失败 [{}] outTradeNo={}, tradeNo={}", message, outTradeNo, tradeNo); - } - if (result.isOK()) { - return Result.success(); - } - return Result.error(); - } catch (ServiceException e) { - return Result.error(e); + Result result = new MedicalService().refund(merchantEnum, openid, qrCode, chrgBchno.substring(1), outTradeNo, bankTransNo, tradeNo, mdUserId, cashFee, insuranceFee); + String message = result.isOK() ? WxPayHelper.OK : result.getMessage(); + if (!recipeDao.updateRefundByTradeNo(tradeNo, message)) { + log.info("[医保]修改退费信息失败 [{}] outTradeNo={}, tradeNo={}", message, outTradeNo, tradeNo); } + if (result.isOK()) { + return Result.success(); + } + return result; + } /** * 获取需要退费的订单 */ - @Action("getRefundList") - public Result getRefundList(String openid, String begDate, String endDate) { - log.info("查询需退费订单 begDate={}, endDate={}", begDate, endDate); + @Action("getRefundMIOrders") + public Result getRefundMIOrders(String openid, String begDate, String endDate) { + log.info("[医保]查询需退费订单 openid={}, begDate={}, endDate={}", openid, begDate, endDate); RecipeDao recipeDao = new RecipeDao(); List orders = recipeDao.selectRefundList(openid, begDate, endDate); for (Order order : orders) { if (order.getTradeNo() != null) { - order.setTradeNo(Base64Helper.encode(order.getTradeNo(), true)); + order.setTradeNo(AesWxHelper.encode(order.getTradeNo())); } } return Result.success(orders); @@ -305,13 +316,36 @@ public class MedicalAction extends BaseAction { * @param medTransId 医保交易流水号 */ @Action("queryOrder") - public Result queryOrder(String outTradeNo, String medTransId) { - log.info("[医保]查询订单 outTradeNo={}, medTransId={}", outTradeNo, medTransId); - MedicalOrder order = WxMedicalHelper.queryOrder(WxCacheHelper.getAccessToken(), outTradeNo, medTransId); - if (order.isOk()) { - return Result.success(order); + public Result queryOrder(String payCode, String outTradeNo, String medTransId, String callNo) { + log.info("[医保]查询订单 payCode={}, outTradeNo={}, medTransId={}, callNo={}", payCode, outTradeNo, medTransId, callNo); + MerchantEnum merchantEnum = MerchantEnum.findEnumByCode(payCode, MerchantEnum.WX_MEDICAL); + if (merchantEnum == null) { + return Result.error(ResultEnum.PAY_TYPE_ERROR); + } + + Map orderObj = new HashMap<>(); + if (!ObjectUtils.isEmpty(callNo)) { // 传递业务类型,返回费用信息 + orderObj = new MedicalService().queryOrderFee(callNo, outTradeNo, medTransId); + } + + if (merchantEnum.equals(MerchantEnum.WX_MEDICAL)) { + MIOrder order = WxMedicalHelper.queryOrder(WxCacheHelper.getAccessToken(), outTradeNo, medTransId); + order.setOrderObj(orderObj); + if (order.isOk()) { + return Result.success(order); + } + return Result.error(order.getMessage()); + + } else if (merchantEnum.equals(MerchantEnum.ALI_MEDICAL)) { + Order order = AliMedicalHelper.queryTrade(outTradeNo, medTransId); + order.setOrderObj(orderObj); + if (order.isSuccess()) { + return Result.success(order); + } + return Result.error(order.getErrorMsg()); + } else { + return Result.error(ResultEnum.PAY_TYPE_ERROR); } - return Result.error(order.getMessage()); } /** diff --git a/src/main/java/com/ynxbd/common/action/pay/MedicalTestAction.java b/src/main/java/com/ynxbd/common/action/pay/MedicalTestAction.java index 5b5081b..2b5163b 100644 --- a/src/main/java/com/ynxbd/common/action/pay/MedicalTestAction.java +++ b/src/main/java/com/ynxbd/common/action/pay/MedicalTestAction.java @@ -1,5 +1,6 @@ package com.ynxbd.common.action.pay; +import com.ynxbd.ali.config.AliMIConfig; import com.ynxbd.common.action.base.BaseAction; import com.ynxbd.common.bean.HisRecipe; import com.ynxbd.common.dao.his.HisMedicalTestDao; @@ -8,7 +9,7 @@ import com.ynxbd.common.result.Result; import com.ynxbd.common.result.ResultEnum; import com.ynxbd.common.service.RecipeService; import com.ynxbd.wx.wxfactory.WxMedicalHelper; -import com.ynxbd.wx.wxfactory.bean.MedicalOrder; +import com.ynxbd.wx.wxfactory.bean.MIOrder; import com.ynxbd.wx.wxfactory.medical.WechatMIConfig; import lombok.extern.slf4j.Slf4j; import org.apache.struts2.convention.annotation.Action; @@ -37,7 +38,7 @@ public class MedicalTestAction extends BaseAction { return Result.error(ResultEnum.PARAM_IS_DEFECT); } - if (!WechatMIConfig.IS_DEV) { + if (!WechatMIConfig.IS_DEV && !AliMIConfig.IS_DEV) { return Result.error("环境错误"); } @@ -64,10 +65,10 @@ public class MedicalTestAction extends BaseAction { if (patientId == null || begDate == null || endDate == null) { return Result.error(ResultEnum.PARAM_IS_DEFECT); } - - if (!WechatMIConfig.IS_DEV) { + if (!WechatMIConfig.IS_DEV && !AliMIConfig.IS_DEV) { return Result.error("环境错误"); } + List hisRecipeList = HisMedicalTestDao.devPaidRecipeList(patientId, begDate, endDate); return Result.success(new RecipeService().filterPaidList(hisRecipeList)); } @@ -117,7 +118,7 @@ public class MedicalTestAction extends BaseAction { public Result queryWxOrder(String accessToken, String outTradeNo, String medTransId) { log.info("[医保]现金退费 outTradeNo={}, medTransId={}", outTradeNo, medTransId); - MedicalOrder order = WxMedicalHelper.queryOrder(accessToken, outTradeNo, medTransId); + MIOrder order = WxMedicalHelper.queryOrder(accessToken, outTradeNo, medTransId); if (order.isOk()) { return Result.success(order); } diff --git a/src/main/java/com/ynxbd/common/action/pay/PayAction.java b/src/main/java/com/ynxbd/common/action/pay/PayAction.java index 2242e8d..a4e9f00 100644 --- a/src/main/java/com/ynxbd/common/action/pay/PayAction.java +++ b/src/main/java/com/ynxbd/common/action/pay/PayAction.java @@ -21,6 +21,7 @@ import com.ynxbd.common.service.*; import com.ynxbd.common.service.params.RequestParams; import com.ynxbd.wx.config.WeChatConfig; import com.ynxbd.wx.wxfactory.WxPayHelper; +import com.ynxbd.wx.wxfactory.bean.WxNativePayResult; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.ObjectUtils; @@ -906,7 +907,11 @@ public class PayAction extends BaseAction { return Result.success(order); } - String url = WxPayHelper.createNativeOrder(MerchantEnum.WX, title, totalFee, outTradeNo, PEnum.OUT_COLLECT.CODE, ip); + WxNativePayResult nativeOrder = WxPayHelper.createNativeOrder(MerchantEnum.WX.QR_NOTIFY_URL, title, totalFee, outTradeNo, PEnum.OUT_COLLECT.CODE, ip); + if (nativeOrder == null) { + return Result.error(ResultEnum.PAY_ERROR_ORDER); + } + String url = nativeOrder.getCodeUrl(); if (isQrCode) { String base64 = QRCodeHelper.encodeToBase64(url, 200, 200); Result.success(base64); diff --git a/src/main/java/com/ynxbd/common/bean/enums/MerchantEnum.java b/src/main/java/com/ynxbd/common/bean/enums/MerchantEnum.java index e04a98a..308032c 100644 --- a/src/main/java/com/ynxbd/common/bean/enums/MerchantEnum.java +++ b/src/main/java/com/ynxbd/common/bean/enums/MerchantEnum.java @@ -97,9 +97,16 @@ public enum MerchantEnum { this.PAY_WAY_MICRO = PAY_WAY_MICRO; } - public static MerchantEnum findEnumByCode(String code) { + /** + * 查询支付类型 + * + * @param code 用于匹配的编码 + * @param defaultMerchant 为空时的返回 + * @return enum + */ + public static MerchantEnum findEnumByCode(String code, MerchantEnum defaultMerchant) { if (ObjectUtils.isEmpty(code)) { - return null; + return defaultMerchant; } for (MerchantEnum merchantEnum : MerchantEnum.values()) { @@ -110,6 +117,10 @@ public enum MerchantEnum { return null; } + public static MerchantEnum findEnumByCode(String code) { + return findEnumByCode(code, null); + } + /** * 根据商户订单号获取支付类型 diff --git a/src/main/java/com/ynxbd/common/bean/pay/Order.java b/src/main/java/com/ynxbd/common/bean/pay/Order.java index 14e6f4b..97d623c 100644 --- a/src/main/java/com/ynxbd/common/bean/pay/Order.java +++ b/src/main/java/com/ynxbd/common/bean/pay/Order.java @@ -147,6 +147,11 @@ public class Order implements Serializable { // PEIS返回提示 private String peisResult; + // 第三方回调状态(-1:未调用,0:调用成功,500:调用超时,其他:调用失败) + private Integer noticeStatus; + // 订单额外信息 + private Object orderObj; + public void filterInfo() { if (id != null) { @@ -177,4 +182,25 @@ public class Order implements Serializable { } } + + // 是否已付款 + public boolean hasPayStatusPaid() { + return (this.payStatus != null && this.payStatus == 0); + } + + // 回调通知成功 + public boolean hasNoticeStatusPaid() { + return (this.noticeStatus != null && this.noticeStatus == 0); + } + + // 回调通知超时 + public boolean hasNoticeStatusTimeout() { + return (this.noticeStatus != null && this.noticeStatus == 500); + } + + // 商户订单是否已退费 + public boolean hasRefundByRefundResult() { + return ("OK".equals(this.refundResult)); + } + } diff --git a/src/main/java/com/ynxbd/common/dao/RecipeDao.java b/src/main/java/com/ynxbd/common/dao/RecipeDao.java index 34696a9..3840160 100644 --- a/src/main/java/com/ynxbd/common/dao/RecipeDao.java +++ b/src/main/java/com/ynxbd/common/dao/RecipeDao.java @@ -31,13 +31,13 @@ public class RecipeDao { * @param recipeId 处方号 * @return 是否已支付完成 */ - public boolean isHisPaidByPatient(String patientId, String treatNum, String recipeId) { - String sql = "select * from pay where patientId= ? and treatNum= ? and recipeId= ? and hisStatus=0"; - return DataBase.select(sql, PayResult.class, ps -> { + public boolean hasHisPaidByPatient(String patientId, String treatNum, String recipeId) { + String sql = "select * from pay where updateTime >= DATE_SUB(CurDate(), INTERVAL 90 DAY) and recipeId= ? and hisStatus=0 and patientId= ? and treatNum= ?"; + return !DataBase.select(sql, PayResult.class, ps -> { ps.setString(1, patientId); ps.setString(2, treatNum); ps.setString(3, recipeId); - }).size() > 0; + }).isEmpty(); } /** @@ -190,6 +190,8 @@ public class RecipeDao { } + + /** * 查询同一次支付的所有处方信息(多处方一次支付,多张发票) * diff --git a/src/main/java/com/ynxbd/common/dao/RegisterDao.java b/src/main/java/com/ynxbd/common/dao/RegisterDao.java index c59d0f2..39e58b8 100644 --- a/src/main/java/com/ynxbd/common/dao/RegisterDao.java +++ b/src/main/java/com/ynxbd/common/dao/RegisterDao.java @@ -417,6 +417,17 @@ public class RegisterDao { return null; } + public Register selectByTradeNo(String tradeNo) { + String sql = "select * from register where tradeNo=? order by updateTime desc"; + List list = DataBase.select(sql, Register.class, ps -> { + ps.setString(1, tradeNo); + }); + if (list.size() > 0) { + return list.get(0); + } + return null; + } + /** * 更新挂号支付信息 * diff --git a/src/main/java/com/ynxbd/common/dao/his/HisMIDao.java b/src/main/java/com/ynxbd/common/dao/his/HisMIDao.java index 16e7559..e0b9ef8 100644 --- a/src/main/java/com/ynxbd/common/dao/his/HisMIDao.java +++ b/src/main/java/com/ynxbd/common/dao/his/HisMIDao.java @@ -207,7 +207,7 @@ public class HisMIDao { params.put("BankTransNo", bankTransNo); params.put("TransNo", refundTradeNo); params.put("PayDeviceID", "mobile"); - params.put("PayMoney", cashFee); + params.put("PayMoney", cashFee); // 现金部分 }); } diff --git a/src/main/java/com/ynxbd/common/helper/common/Base64Helper.java b/src/main/java/com/ynxbd/common/helper/common/Base64Helper.java index 638ca1a..13c518c 100644 --- a/src/main/java/com/ynxbd/common/helper/common/Base64Helper.java +++ b/src/main/java/com/ynxbd/common/helper/common/Base64Helper.java @@ -97,36 +97,36 @@ public class Base64Helper { return str.matches("^[a-z0-9A-Z]+$"); } - /** - * 解密 - * - * @param data 需要解密的字符串 - */ - public static String decodeEn(String data) { - try { - if (data == null) { - return null; - } - int dataLen = data.length(); - if (dataLen > 32) { // 截取掉uuid - data = data.substring(32); - } - if (dataLen > 57) { // 超过57位的需要调换前后位置 - String endStr = data.substring(0, 10); - String begStr = data.substring(data.length() - 15, data.length() - 5); - String middle = data.substring(10, data.length() - 15); - String end = data.substring(data.length() - 5); - data = begStr + middle + endStr + end; - } - String deData = new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8);// base64解密 - if (!isLetterDigit(deData)) { - return null; - } - return deData; - } catch (Exception e) { - return null; - } - } +// /** +// * 解密 +// * +// * @param data 需要解密的字符串 +// */ +// public static String decodeEn(String data) { +// try { +// if (data == null) { +// return null; +// } +// int dataLen = data.length(); +// if (dataLen > 32) { // 截取掉uuid +// data = data.substring(32); +// } +// if (dataLen > 57) { // 超过57位的需要调换前后位置 +// String endStr = data.substring(0, 10); +// String begStr = data.substring(data.length() - 15, data.length() - 5); +// String middle = data.substring(10, data.length() - 15); +// String end = data.substring(data.length() - 5); +// data = begStr + middle + endStr + end; +// } +// String deData = new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8);// base64解密 +// if (!isLetterDigit(deData)) { +// return null; +// } +// return deData; +// } catch (Exception e) { +// return null; +// } +// } /** diff --git a/src/main/java/com/ynxbd/common/service/MedicalService.java b/src/main/java/com/ynxbd/common/service/MedicalService.java index 26ccdba..bb6c380 100644 --- a/src/main/java/com/ynxbd/common/service/MedicalService.java +++ b/src/main/java/com/ynxbd/common/service/MedicalService.java @@ -1,10 +1,12 @@ package com.ynxbd.common.service; import com.alibaba.fastjson.JSONObject; +import com.ynxbd.ali.config.AliMIConfig; import com.ynxbd.ali.helper.AliMedicalHelper; import com.ynxbd.common.action.pay.PEnum; import com.ynxbd.common.bean.enums.MerchantEnum; import com.ynxbd.common.bean.pay.ApiResult; +import com.ynxbd.common.bean.pay.Order; import com.ynxbd.common.bean.pay.Recipe; import com.ynxbd.common.bean.pay.Register; import com.ynxbd.common.dao.RecipeDao; @@ -20,22 +22,31 @@ import com.ynxbd.wx.config.WeChatConfig; import com.ynxbd.wx.wxfactory.WxMedicalHelper; import com.ynxbd.wx.wxfactory.WxPayHelper; import com.ynxbd.wx.wxfactory.bean.MedicalInfo; -import com.ynxbd.wx.wxfactory.bean.MedicalOrder; +import com.ynxbd.wx.wxfactory.bean.MIOrder; import com.ynxbd.wx.wxfactory.bean.MedicalUserInfo; import com.ynxbd.wx.wxfactory.bean.OrderMIEnum; import com.ynxbd.wx.wxfactory.medical.WechatMIConfig; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import java.math.BigDecimal; +import java.util.HashMap; import java.util.Map; @Slf4j public class MedicalService { + public static Boolean isDevUser(String openid) { + if (ObjectUtils.isEmpty(openid)) { + return null; + } + return openid.equals(WechatMIConfig.DEV_OPENID) || openid.equals(AliMIConfig.DEV_OPENID); + } + public void commonNotify(MerchantEnum merchantEnum, String notifyType, String outTradeNo, BigDecimal totalFee, String bankTransNo, String payDate, String payTime, String openid, String payInfo) { switch (PEnum.toEnum(notifyType)) { case RECIPE: - rxPayNotify(merchantEnum, outTradeNo, bankTransNo, payDate, payTime, openid, payInfo); + rxPaidNotify(merchantEnum, outTradeNo, bankTransNo, payDate, payTime, openid, payInfo); break; case REG: // 挂号 @@ -51,6 +62,21 @@ public class MedicalService { } } + public String getPayAuthNo(MerchantEnum merchantEnum, String openid, String qrCode) throws ServiceException { + return getPayAuthNo(merchantEnum, openid, qrCode, null, null); + } + + private String getPayAuthNo(MerchantEnum merchantEnum, String openid, String qrCode, String cardNo, String realName) throws ServiceException { + String payAuthNo; + if (merchantEnum == MerchantEnum.ALI_MEDICAL) { // 支付宝医保 + payAuthNo = qrCode; + } else { + MedicalUserInfo userInfo = WxMedicalHelper.getUserInfo(openid, qrCode, cardNo, realName); + payAuthNo = userInfo.getPayAuthNo(); + } + return payAuthNo; + } + /** * [医保]挂号上报 @@ -63,14 +89,7 @@ public class MedicalService { * @param reg 挂号信息 */ public MedicalInfo hisRegMIUploadFeeDetails(MerchantEnum merchantEnum, String qrCode, String openid, String patientId, String cardNo, String realName, Register reg, Boolean isSplitTime) throws ServiceException { - String payAuthNo; - if (merchantEnum == MerchantEnum.ALI_MEDICAL) { // 支付宝医保 - payAuthNo = qrCode; - } else { - MedicalUserInfo userInfo = WxMedicalHelper.getUserInfo(openid, qrCode, cardNo, realName); - payAuthNo = userInfo.getPayAuthNo(); - } - + String payAuthNo = getPayAuthNo(merchantEnum, openid, qrCode, cardNo, realName); log.info("[医保]上传明细:patientId={}, cardNo={}, realName={}, reg={}", patientId, cardNo, realName, reg); JsonResult result = HisMIDao.regMIUploadFeeDetails(merchantEnum, reg, payAuthNo, cardNo, isSplitTime); return hisMIPlaceOrder(merchantEnum, payAuthNo, result); @@ -165,12 +184,61 @@ public class MedicalService { return medicalInfo; } + // 创建医保订单 + private Map createMIOrder(MerchantEnum merchantEnum, OrderMIEnum orderMIEnum, String orderTitle, String openid, String outTradeNo, String cardNo, String realName, String ip, String payOrdId, String payAuthNo, String chrgBchno, + BigDecimal totalFee, BigDecimal insuranceFee, BigDecimal cashFee, String notifyType, + String familyType, String familyName, String familyCardNo, String medicalCardInstId, String medicalCardId) throws ServiceException { + if (MerchantEnum.WX_MEDICAL.equals(merchantEnum)) { + return WxMedicalHelper.createOrder(orderMIEnum, + openid, + outTradeNo, + chrgBchno, + cardNo, + realName, + ip, + payOrdId, + payAuthNo, + totalFee, + insuranceFee, + cashFee, + orderTitle, + WeChatConfig.getWebUrl() + orderMIEnum.WX_CALL_BACK_URL, + WeChatConfig.getBaseUrl() + merchantEnum.NOTIFY_URL, + notifyType, + familyType, + familyName, + familyCardNo); + + } else if (MerchantEnum.ALI_MEDICAL.equals(merchantEnum)) { + if (ObjectUtils.isEmpty(medicalCardId)) { + throw new ServiceException("【支付宝】医保授权码medicalCardId为空"); + } + + String notifyUrl = WeChatConfig.getHttpsBaseUrl() + merchantEnum.getNotifyUrl(outTradeNo, notifyType); + return AliMedicalHelper.createOrder(orderMIEnum, + openid, + outTradeNo, + payOrdId, + payAuthNo, + totalFee, + cashFee, + orderTitle, + notifyUrl, + notifyType, + familyType, + familyName, + familyCardNo, + medicalCardInstId, + medicalCardId); + } + return null; + } /** * [医保]处方下单 */ public Result createRxOrder(String payCode, String openid, String patientId, String cardNo, String realName, String ip, String payOrdId, String payAuthNo, String mdTrtId, String chrgBchno, String mdUserId, BigDecimal totalFee, BigDecimal acctFee, BigDecimal hifpFee, BigDecimal insuranceFee, BigDecimal cashFee, String notifyType, - String recipeJson, String treatNum, String payWay, String familyType, String familyName, String familyCardNo) { + String recipeJson, String treatNum, String payWay, String familyType, String familyName, String familyCardNo, String medicalCardInstId, String medicalCardId) { Result result = PayService.isPaymentPermittedByTime(); if (result != null) { return result; @@ -184,57 +252,28 @@ public class MedicalService { payWay = payWay == null ? "0" : payWay; String outTradeNo = CodeHelper.getOutTradeNo(merchantEnum); - Recipe recipeInfo = new RecipeService().recipeJsonToInfo(treatNum, recipeJson); - log.info("[医保]处方json解析 info={}", recipeInfo); - if (recipeInfo == null) { + Recipe rxInfo = new RecipeService().recipeJsonToInfo(treatNum, recipeJson); + log.info("[医保][处方]JSON解析 RxInfo={}", rxInfo); + if (rxInfo == null) { return Result.error(ResultEnum.PAY_PREPAY_SAVE_ERROR); } - String recipeJsonStr = recipeInfo.getRecipeJson(); + String recipeJsonStr = rxInfo.getRecipeJson(); if (recipeJsonStr == null) { return Result.error(ResultEnum.PAY_PREPAY_SAVE_ERROR); } - log.info("[医保]下单 openid={}, outTradeNo={}, payOrdId={}, payAuthNo={}, totalFee={}, acctFee={}, cashFee={}, idCardNo={}, realName={}", openid, outTradeNo, payOrdId, payAuthNo, totalFee, acctFee, cashFee, cardNo, realName); - + log.info("[医保][处方]下单 openid={}, outTradeNo={}, payOrdId={}, payAuthNo={}, totalFee={}, acctFee={}, cashFee={}, idCardNo={}, realName={}, familyType={}, familyName={}, medicalCardId={}, medicalCardInstId={}", openid, outTradeNo, payOrdId, payAuthNo, totalFee, acctFee, cashFee, cardNo, realName, familyType, familyName, medicalCardId, medicalCardInstId); try { - Map map = null; String orderTitle = "门诊号:" + treatNum; - if (MerchantEnum.WX_MEDICAL.equals(merchantEnum)) { - map = WxMedicalHelper.createOrder(OrderMIEnum.DIAG_PAY, - openid, - outTradeNo, - chrgBchno, - cardNo, - realName, - ip, - payOrdId, - payAuthNo, - totalFee, - insuranceFee, - cashFee, - orderTitle, - WeChatConfig.getWebUrl() + "pay-info.html#/rx-paid", - WeChatConfig.getBaseUrl() + merchantEnum.NOTIFY_URL, - notifyType, - familyType, familyName, familyCardNo); - } else if (MerchantEnum.ALI_MEDICAL.equals(merchantEnum)) { - String notifyUrl = WeChatConfig.getHttpsBaseUrl() + merchantEnum.getNotifyUrl(outTradeNo, notifyType); - map = AliMedicalHelper.createOrder(OrderMIEnum.DIAG_PAY, - openid, - outTradeNo, - payOrdId, - payAuthNo, - totalFee, - cashFee, - orderTitle, - notifyUrl, - notifyType, - familyType, familyName, familyCardNo); - } + Map map = createMIOrder(merchantEnum, OrderMIEnum.DIAG_PAY, orderTitle, openid, outTradeNo, cardNo, realName, ip, payOrdId, payAuthNo, chrgBchno, + totalFee, insuranceFee, cashFee, notifyType, + familyType, familyName, familyCardNo, medicalCardInstId, medicalCardId); + if (map == null) { return Result.error(ResultEnum.PAY_ERROR_ORDER); } - String bankTransNo = String.valueOf(map.get("bankTransNo")); + Object bankTransNoVal = map.get("bankTransNo"); + String bankTransNo = ObjectUtils.isEmpty(bankTransNoVal) ? null : String.valueOf(bankTransNoVal); RecipeDao recipeDao = new RecipeDao(); // Recipe recipeInfo = recipeDao.selectByTradeNo(payOrdId); @@ -248,10 +287,10 @@ public class MedicalService { addRecipe.setMdUserId(mdUserId); addRecipe.setMdTrtId(mdTrtId); addRecipe.setRecipeJson(recipeJsonStr); - addRecipe.setDeptCode(recipeInfo.getDeptCode()); - addRecipe.setDeptName(recipeInfo.getDeptName()); - addRecipe.setReqDeptCode(recipeInfo.getReqDeptCode()); - addRecipe.setReqDeptName(recipeInfo.getReqDeptName()); + addRecipe.setDeptCode(rxInfo.getDeptCode()); + addRecipe.setDeptName(rxInfo.getDeptName()); + addRecipe.setReqDeptCode(rxInfo.getReqDeptCode()); + addRecipe.setReqDeptName(rxInfo.getReqDeptName()); // addRecipe.setPayMoney(cashFee); // 现金 addRecipe.setAcctFee(acctFee); @@ -281,7 +320,8 @@ public class MedicalService { /** * [医保]挂号下单 */ - public Result createRegOrder(String payCode, String openid, String cardNo, String realName, String ip, String payOrdId, String payAuthNo, String mdTrtId, String chrgBchno, String mdUserId, BigDecimal totalFee, BigDecimal acctFee, BigDecimal hifpFee, BigDecimal insuranceFee, BigDecimal cashFee, String notifyType, Register reg, String familyType, String familyName, String familyCardNo) { + public Result createRegOrder(String payCode, String openid, String cardNo, String realName, String ip, String payOrdId, String payAuthNo, String mdTrtId, String chrgBchno, String mdUserId, BigDecimal totalFee, BigDecimal acctFee, BigDecimal hifpFee, BigDecimal insuranceFee, BigDecimal cashFee, String notifyType, Register reg, + String familyType, String familyName, String familyCardNo, String medicalCardInstId, String medicalCardId) { Result result = PayService.isPaymentPermittedByTime(); if (result != null) { return result; @@ -293,44 +333,12 @@ public class MedicalService { } String outTradeNo = CodeHelper.getOutTradeNo(merchantEnum); - log.info("[医保]挂号下单 openid={}, outTradeNo={}, payOrdId={}, payAuthNo={}, totalFee={}, acctFee={}, cashFee={}, idCardNo={}, realName={}", openid, outTradeNo, payOrdId, payAuthNo, totalFee, acctFee, cashFee, cardNo, realName); - + log.info("[医保][挂号]下单 openid={}, outTradeNo={}, payOrdId={}, payAuthNo={}, totalFee={}, acctFee={}, cashFee={}, idCardNo={}, realName={}, medicalCardInstId={}, medicalCardId={}", openid, outTradeNo, payOrdId, payAuthNo, totalFee, acctFee, cashFee, cardNo, realName, medicalCardId, medicalCardInstId); try { - Map map = null; String orderTitle = "挂号"; - if (MerchantEnum.WX_MEDICAL.equals(merchantEnum)) { - map = WxMedicalHelper.createOrder(OrderMIEnum.REG_PAY, - openid, - outTradeNo, - chrgBchno, - cardNo, - realName, - ip, - payOrdId, - payAuthNo, - totalFee, - insuranceFee, - cashFee, - orderTitle, - WeChatConfig.getWebUrl() + "my-info.html", - WeChatConfig.getBaseUrl() + merchantEnum.NOTIFY_URL, - notifyType, - familyType, familyName, familyCardNo); - - } else if (MerchantEnum.ALI_MEDICAL.equals(merchantEnum)) { - String notifyUrl = WeChatConfig.getHttpsBaseUrl() + merchantEnum.getNotifyUrl(outTradeNo, notifyType); - map = AliMedicalHelper.createOrder(OrderMIEnum.REG_PAY, - openid, - outTradeNo, - payOrdId, - payAuthNo, - totalFee, - cashFee, - orderTitle, - notifyUrl, - notifyType, - familyType, familyName, familyCardNo); - } + Map map = createMIOrder(merchantEnum, OrderMIEnum.REG_PAY, orderTitle, openid, outTradeNo, cardNo, realName, ip, payOrdId, payAuthNo, chrgBchno, + totalFee, insuranceFee, cashFee, notifyType, + familyType, familyName, familyCardNo, medicalCardInstId, medicalCardId); if (map == null) { return Result.error(ResultEnum.PAY_ERROR_ORDER); } @@ -468,7 +476,7 @@ public class MedicalService { private ApiResult autoRefund(MerchantEnum merchantEnum, String outTradeNo, String tradeNo, BigDecimal cashFee, String reason) { ApiResult apiResult = new ApiResult(); if (MerchantEnum.WX_MEDICAL == merchantEnum) { - MedicalOrder order = WxMedicalHelper.refundCash(outTradeNo, tradeNo, cashFee, reason); + MIOrder order = WxMedicalHelper.refundCash(outTradeNo, tradeNo, cashFee, reason); apiResult.setSuccess(order.isOk()); apiResult.setMessage(order.isOk() ? WxPayHelper.OK : order.getMessage()); } else { @@ -490,7 +498,7 @@ public class MedicalService { * @param openid openid * @param payInfo 支付信息 */ - public boolean rxPayNotify(MerchantEnum merchantEnum, String outTradeNo, String bankTransNo, String payDate, String payTime, String openid, String payInfo) { + public boolean rxPaidNotify(MerchantEnum merchantEnum, String outTradeNo, String bankTransNo, String payDate, String payTime, String openid, String payInfo) { if (outTradeNo == null || bankTransNo == null || payDate == null || payTime == null) { log.info("[医保][处方]HIS结算参数缺失 outTradeNo={}, bankTransNo={}, payDate={}, payTime={}", outTradeNo, bankTransNo, payDate, payTime); return false; @@ -597,34 +605,40 @@ public class MedicalService { * @param outTradeNo 订单号 * @param bankTransNo 交易流水号 * @param tradeNo HIS交易流水号(医保订单号) - * @param payAuthNo 用户授权码 * @param fpNum 发票号(退费去掉M) * @param cashFee 现金金额 * @param insuranceFee 医保金额 */ - public Result refund(String payAuthNo, String fpNum, String outTradeNo, String bankTransNo, String tradeNo, String mdUserId, BigDecimal cashFee, BigDecimal insuranceFee) { + public Result refund(MerchantEnum merchantEnum, String openid, String qrCode, String fpNum, String outTradeNo, String bankTransNo, String tradeNo, String mdUserId, BigDecimal cashFee, BigDecimal insuranceFee) { if (insuranceFee.compareTo(BigDecimal.ZERO) == 0 && cashFee.compareTo(BigDecimal.ZERO) == 0) { log.info("[医保][处方]退费金额为0不退费"); return Result.error("[医保][处方]金额为0不退费"); } - // 医保退费-必须先执行 - log.info("[医保][处方][个账]退费开始 tradeNo={}, payAuthNo={}, fpNum={}", tradeNo, payAuthNo, fpNum); - JsonResult jsonResult = HisMIDao.miRefund(MerchantEnum.WX, payAuthNo, fpNum, bankTransNo, ("R" + tradeNo), cashFee); - if (!jsonResult.success()) { // 失败 - log.info("[医保][处方][个账]退费失败 outTradeNo={}, bankTransNo={}, tradeNo={}", outTradeNo, bankTransNo, tradeNo); - String message = jsonResult.getMessage(); - if (message != null && !message.contains("已医保全部退款")) { - return Result.error(message); - } + if (!merchantEnum.equals(MerchantEnum.WX_MEDICAL) && !merchantEnum.equals(MerchantEnum.ALI_MEDICAL)) { // 支付方式异常 + return Result.error(ResultEnum.PAY_TYPE_ERROR); } - // String hisRefundNo = jsonResult.getDataMapString("N_FPNum"); // His生成的退费流水号 - log.info("[医保]HIS退费返回 fpNum={} resp={}", fpNum, jsonResult.getDataMap()); + try { + String payAuthNo = getPayAuthNo(merchantEnum, openid, qrCode); // 用户授权码 + + // 医保退费-必须先执行 + log.info("[医保][处方][个账]退费开始 tradeNo={}, payAuthNo={}, fpNum={}", tradeNo, payAuthNo, fpNum); + JsonResult jsonResult = HisMIDao.miRefund(MerchantEnum.WX, payAuthNo, fpNum, bankTransNo, ("R" + tradeNo), cashFee); + if (!jsonResult.success()) { // 失败 + log.info("[医保][处方][个账]退费失败 outTradeNo={}, bankTransNo={}, tradeNo={}", outTradeNo, bankTransNo, tradeNo); + String message = jsonResult.getMessage(); + if (message != null && !message.contains("已医保全部退款")) { + return Result.error(message); + } + } + // String hisRefundNo = jsonResult.getDataMapString("N_FPNum"); // His生成的退费流水号 + + log.info("[医保]HIS退费返回 fpNum={} resp={}", fpNum, jsonResult.getDataMap()); - // 现金退费 - if (cashFee.compareTo(BigDecimal.ZERO) != 0) { - log.info("[医保][处方]现金开始退费"); + // 现金退费 + if (cashFee.compareTo(BigDecimal.ZERO) != 0) { + log.info("[医保][处方]现金开始退费"); // MedicalInfo info = getMedicalAccounts(mdUserId); // if (info == null) { // return Result.error("[订单]未找到结算信息"); @@ -643,15 +657,32 @@ public class MedicalService { // return Result.error(order.getMessage()); // } - MedicalOrder order = WxMedicalHelper.refund(outTradeNo, ("R" + tradeNo), tradeNo, cashFee, null); - boolean isOk = order.isOk(); - log.info("[处方][现金]退费{} outTradeNo={}, bankTransNo={}, tradeNo={}", (isOk ? "成功" : "失败"), outTradeNo, bankTransNo, tradeNo); - if (isOk) { - return Result.success(); + String outRefundNo = ("R" + tradeNo); + if (merchantEnum.equals(MerchantEnum.WX_MEDICAL)) { + MIOrder order = WxMedicalHelper.refund(outTradeNo, outRefundNo, tradeNo, cashFee, null); + boolean isOk = order.isOk(); + log.info("[微信][处方][现金]退费{} outTradeNo={}, bankTransNo={}, tradeNo={}", (isOk ? "成功" : "失败"), outTradeNo, bankTransNo, tradeNo); + if (isOk) { + return Result.success(); + } + return Result.error(order.getMessage()); + + } else { + + // 支付宝 + String respBody = AliMedicalHelper.refundTrade(outTradeNo, outRefundNo, tradeNo, cashFee, "线上退费"); + boolean isOk = respBody != null; + log.info("[支付宝][处方][现金]退费{} outTradeNo={}, bankTransNo={}, tradeNo={}", (isOk ? "成功" : "失败"), outTradeNo, bankTransNo, tradeNo); + if (isOk) { + return Result.success(); + } + return Result.error(); + } } - return Result.error(order.getMessage()); + return Result.success(); + } catch (Exception e) { + return Result.error(e); } - return Result.success(); } @@ -698,4 +729,30 @@ public class MedicalService { return medicalInfo; } + public Map queryOrderFee(String callNo, String outTradeNo, String tradeNo) { + Map map = new HashMap<>(); + Order order = null; + switch (PEnum.toEnum(callNo)) { + case REG: + order = new RegService().queryRegByOrderNo(outTradeNo, tradeNo); + break; + + case RECIPE: + order = new RecipeService().queryRxByOrderNo(outTradeNo, tradeNo); + break; + + default: + log.info("[订单费用查询]业务类型未匹配"); + break; + } + + if (order == null) { // 未支付 + return null; + } + map.put("totalFee", order.getTotalFee()); + map.put("cashFee", order.getPayMoney()); + map.put("acctFee", order.getAcctFee()); + map.put("hifpFee", order.getHifpFee()); + return map; + } } diff --git a/src/main/java/com/ynxbd/common/service/RecipeService.java b/src/main/java/com/ynxbd/common/service/RecipeService.java index 953b8cd..1396176 100644 --- a/src/main/java/com/ynxbd/common/service/RecipeService.java +++ b/src/main/java/com/ynxbd/common/service/RecipeService.java @@ -25,7 +25,7 @@ import com.ynxbd.wx.config.TZReserveConfig; import com.ynxbd.wx.utils.DateGenerate; import com.ynxbd.wx.wxfactory.WxMedicalHelper; import com.ynxbd.wx.wxfactory.WxPayHelper; -import com.ynxbd.wx.wxfactory.bean.MedicalOrder; +import com.ynxbd.wx.wxfactory.bean.MIOrder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; @@ -98,6 +98,7 @@ public class RecipeService { if (merchantEnum == null) { // 支付方式异常 return Result.error(ResultEnum.PAY_TYPE_ERROR); } + log.info("{}[处方预支付]参数 openid={}, patientId={}, treatNum={}, totalFee={}, awaitSecond={}, payWay={}", merchantEnum.NAME, openid, patientId, treatNum, totalFee, awaitSecond, payWay); if (openid == null || patientId == null || treatNum == null || totalFee == null || recipeJson == null) { return Result.error(ResultEnum.PARAM_IS_DEFECT); @@ -685,7 +686,7 @@ public class RecipeService { } - MedicalOrder order = WxMedicalHelper.refund(outTradeNo, ("R" + payOrdId), payOrdId, cashFee, reason); + MIOrder order = WxMedicalHelper.refund(outTradeNo, ("R" + payOrdId), payOrdId, cashFee, reason); log.info("[医保]现金退费order={}", order); String refundReason = "线下现金退费"; if (!ObjectUtils.isEmpty(reason)) { @@ -1042,4 +1043,30 @@ public class RecipeService { } return dataList; } + + // 添加旧版体检订单 + public boolean addOldPEISOrder(String outTradeNo, String payMoney, String openid, String patientId, String treatNum, String rxId) { + Recipe recipe = new Recipe(); + recipe.setOpenid(openid); + recipe.setPatientId(patientId); + recipe.setTreatNum(treatNum); + recipe.setOutTradeNo(outTradeNo); + recipe.setPayWay("1"); + recipe.setPayMoney(new BigDecimal(payMoney)); + recipe.setTotalFee(new BigDecimal(payMoney)); + recipe.setRecipeId(rxId); + recipe.setHisStatus(-1); + recipe.setPayStatus(-1); + return new RecipeDao().insert(recipe); + } + + public Order queryRxByOrderNo(String outTradeNo, String tradeNo) { + if (!ObjectUtils.isEmpty(outTradeNo)) { + return new RecipeDao().selectOneByOutTradeNo(outTradeNo); + } + if (ObjectUtils.isEmpty(tradeNo)) { + return null; + } + return new RecipeDao().selectByTradeNo(tradeNo); + } } diff --git a/src/main/java/com/ynxbd/common/service/RegService.java b/src/main/java/com/ynxbd/common/service/RegService.java index df5d0bf..0c2d1b2 100644 --- a/src/main/java/com/ynxbd/common/service/RegService.java +++ b/src/main/java/com/ynxbd/common/service/RegService.java @@ -24,7 +24,7 @@ import com.ynxbd.wx.config.MessagePushConfig; import com.ynxbd.wx.utils.DateGenerate; import com.ynxbd.wx.wxfactory.WxMedicalHelper; import com.ynxbd.wx.wxfactory.WxPayHelper; -import com.ynxbd.wx.wxfactory.bean.MedicalOrder; +import com.ynxbd.wx.wxfactory.bean.MIOrder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; @@ -81,6 +81,18 @@ public class RegService { return isResult; } + // 查询订单 + public Register queryRegByOrderNo(String outTradeNo, String tradeNo) { + if (!ObjectUtils.isEmpty(outTradeNo)) { + return new RegisterDao().selectByOutTradeNo(outTradeNo); + } + if (ObjectUtils.isEmpty(tradeNo)) { + return null; + } + return new RegisterDao().selectByTradeNo(tradeNo); + } + + /** * 支付完成-->挂号回调 * @@ -101,7 +113,7 @@ public class RegService { String regType, patientId; String tradeNo = HisHelper.getHisTradeNo(bankTransNo, PEnum.REG); try { - reg = registerDao.selectByOutTradeNo(outTradeNo); + reg = queryRegByOrderNo(outTradeNo, null); if (reg == null) { throw new ServiceException(ResultEnum.DATA_NOT_FOUND, String.format("{%s} [挂号]数据库中未找到订单 outTradeNo={%s}, bankTransNo={%s}, tradeNo={%s}", merchantEnum.NAME, outTradeNo, bankTransNo, tradeNo)); @@ -266,7 +278,7 @@ public class RegService { return Result.success(map); } - MedicalOrder order = WxMedicalHelper.refund(outTradeNo, ("R" + payOrdId), payOrdId, cashFee, reason); + MIOrder order = WxMedicalHelper.refund(outTradeNo, ("R" + payOrdId), payOrdId, cashFee, reason); log.info("[医保]挂号现金退费order={}", order); String refundReason = "线下现金退费"; if (!ObjectUtils.isEmpty(reason)) { diff --git a/src/main/java/com/ynxbd/wx/servlet/QServlet.java b/src/main/java/com/ynxbd/wx/servlet/QServlet.java index af9eec1..cfa0e8b 100644 --- a/src/main/java/com/ynxbd/wx/servlet/QServlet.java +++ b/src/main/java/com/ynxbd/wx/servlet/QServlet.java @@ -53,13 +53,15 @@ public class QServlet extends HttpServlet { String patientId = request.getParameter("p"); + String cardNo = request.getParameter("t"); log.info("{} [patientId={}]多张处方扫码请求,开始解析...", merchantEnum.NAME, patientId); - if (StringUtils.isEmpty(patientId) && StringUtils.isEmpty(request.getParameter("t"))) { + if (StringUtils.isEmpty(patientId) && StringUtils.isEmpty(cardNo)) { log.info("[支付] 多张扫码请求 patientId is null and idCardNo is null"); return; } - // 体检缴费,patientId赋值为0 - if (!StringUtils.isEmpty(request.getParameter("t")) && StringUtils.isEmpty(patientId)) { + + // 新版体检缴费,patientId赋值为0 + if (!StringUtils.isEmpty(cardNo) && StringUtils.isEmpty(patientId)) { patientId = "0"; //patientId 赋值0 } @@ -67,7 +69,6 @@ public class QServlet extends HttpServlet { log.info("[支付] 多张扫码请求 参数无效"); return; } - String cardNo = request.getParameter("t"); cardNo = ObjectUtils.isEmpty(cardNo) ? "" : Base64Helper.decode(cardNo); if (!ObjectUtils.isEmpty(cardNo)) { cardNo = "&ent=" + AesWxHelper.encode(cardNo); diff --git a/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet.java b/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet.java index 5f1257d..51501c5 100644 --- a/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet.java +++ b/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet.java @@ -1,18 +1,16 @@ package com.ynxbd.wx.servlet.oldpay; import com.ynxbd.common.bean.enums.MerchantEnum; -import com.ynxbd.common.bean.pay.Recipe; import com.ynxbd.common.dao.RecipeDao; import com.ynxbd.common.helper.common.CodeHelper; import com.ynxbd.common.helper.common.HttpHelper; +import com.ynxbd.common.service.RecipeService; import com.ynxbd.wx.config.WeChatConfig; +import com.ynxbd.wx.wxfactory.WxPayHelper; +import com.ynxbd.wx.wxfactory.bean.WxNativePayResult; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import org.slf4j.MDC; -import weixin.popular.api.PayMchAPI; -import weixin.popular.bean.paymch.MchPayNativeReply; -import weixin.popular.bean.paymch.Unifiedorder; -import weixin.popular.bean.paymch.UnifiedorderResult; -import weixin.popular.util.PayUtil; import weixin.popular.util.SignatureUtil; import weixin.popular.util.XMLConverUtil; @@ -28,7 +26,6 @@ import java.io.InputStreamReader; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.Map; -import java.util.UUID; /** * 扫码支付接口(旧版) @@ -48,7 +45,7 @@ public class QRPayServlet extends HttpServlet { MDC.remove("ip"); MDC.put("ip", HttpHelper.getIpAddress(request)); - log.info("[旧版]收到扫码支付请求,开始解析..."); + log.info("[Native]扫码支付收到支付请求,开始解析..."); // 读取参数 InputStream inputStream = request.getInputStream(); @@ -63,18 +60,6 @@ public class QRPayServlet extends HttpServlet { // 解析xml成map Map map = XMLConverUtil.convertToMap(sb.toString()); - // 过滤空 设置 TreeMap -// SortedMap payParams = new TreeMap<>(); -// Iterator it = map.keySet().iterator(); -// while (it.hasNext()) { -// String parameter = (String) it.next(); -// String parameterValue = map.get(parameter); -// String v = ""; -// if (null != parameterValue) { -// v = parameterValue.trim(); -// } -// payParams.put(parameter, v); -// } // 验证请求签名 if (!map.get("sign").equals(SignatureUtil.generateSign(map, WeChatConfig.MCH_KEY))) { @@ -83,102 +68,65 @@ public class QRPayServlet extends HttpServlet { return; } + String openid = map.get("openid"); String product_id = map.get("product_id"); - log.info("收到扫码支付请求:product_id=" + product_id + ", open_id=" + map.get("openid")); + + log.info("[Native]扫码支付 收到请求:product_id={}, openid={}", product_id, openid); if (!product_id.contains("XBD")) { - log.info("扫码支付:不合规则的字符串,不允许支付!product_id=" + product_id); + log.info("[Native]扫码支付 不合规则的字符串,不允许支付!product_id={}", product_id); HttpHelper.outRespAlert(response, "扫码支付:不合规则的字符串,不允许支付!"); return; } - String[] array = product_id.split("XBD"); - if (array.length < 5) { - log.info("扫码支付:参数少于5个,不允许支付!product_id=" + product_id); + String[] params = product_id.split("XBD"); // product_id=0XBD0XBD258597|80.00XBD80XBD1 + if (params.length < 5) { + log.info("[Native]扫码支付 体检缴费参数少于5个,不允许支付!product_id={}", product_id); HttpHelper.outRespAlert(response, "扫码支付:参数少于5个"); return; } - String patientId = array[0]; - String mzNum = array[1]; - String recipeId = array[2]; - String payMoney = array[3]; -// String recipeIdJson = "[{\"id\":" + "\"" + recipeId + "\"" + ",\"fee\":" + payMoney + "}]"; - String flag = array[4]; - - log.info("扫码支付:patientId=" + array[0] + ", mzNum=" + array[1] + ",recipeId=" + array[2] + ",payMoney=" + array[3] + ", flag=" + array[4]); + String patientId = params[0]; + String treatNum = params[1]; + String recipeId = params[2]; + String payMoney = params[3]; + String flag = params[4]; + // String recipeIdJson = "[{\"id\":" + "\"" + recipeId + "\"" + ",\"fee\":" + payMoney + "}]"; - if (patientId.equals("") || mzNum.equals("") || payMoney.equals("")) { - log.info("扫码支付:参数中有空值,不允许支付!product_id=" + product_id); + log.info("[Native]扫码支付 patientId={} treatNum={}, recipeId={}, payMoney={}, flag={}", patientId, treatNum, recipeId, payMoney, flag); + if (ObjectUtils.isEmpty(patientId) || ObjectUtils.isEmpty(treatNum) || ObjectUtils.isEmpty(payMoney)) { + log.info("[Native]扫码支付 参数中有空值,不允许支付!product_id={}", product_id); HttpHelper.outRespAlert(response, "扫码支付:参数中有空值,不允许支付!"); return; } // 是否重复支付 - log.info("判断是否支付过:" + patientId + "," + mzNum + "," + recipeId); - if (new RecipeDao().isHisPaidByPatient(patientId, mzNum, recipeId)) { - log.info("该码已经缴费,无需支付!product_id=" + product_id); - HttpHelper.outRespAlert(response, "该码已经缴费,无需支付!"); + if (new RecipeDao().hasHisPaidByPatient(patientId, treatNum, recipeId)) { + log.info("[Native]扫码支付 已缴费成功,无需再次支付!product_id={}", product_id); + HttpHelper.outRespAlert(response, "已缴费成功,无需支付!"); return; } - // 统一下单 - Unifiedorder unifiedorder = new Unifiedorder(); - String appID = WeChatConfig.APP_ID; - String mchID = WeChatConfig.MCH_ID; - String mchKey = WeChatConfig.MCH_KEY; - - unifiedorder.setAppid(appID); - unifiedorder.setMch_id(mchID); - unifiedorder.setNonce_str(UUID.randomUUID().toString().replace("-", "")); + String outTradeNo = CodeHelper.getOutTradeNo(MerchantEnum.WX); + String title = ""; if (flag.equals("1")) { - unifiedorder.setBody("ID:" + patientId + " 处方单号:" + recipeId); + title = ("ID:" + patientId + " 处方单号:" + recipeId); } else if (flag.equals("2")) { - unifiedorder.setBody("ID:" + patientId + " 申请单号:" + recipeId); + title = ("ID:" + patientId + " 申请单号:" + recipeId); } - - // unifiedorder.setBody("pay"); - unifiedorder.setOut_trade_no(CodeHelper.getOutTradeNo(MerchantEnum.WX)); - - // 此处使用Float或者Double转换金额会因为精度问题丢失1分钱,使用BigDecimal来转换 - BigDecimal v1 = new BigDecimal(payMoney); - BigDecimal v2 = new BigDecimal("100"); - double b = v1.multiply(v2).doubleValue(); - int fFee = (int) b; - - unifiedorder.setTotal_fee(String.valueOf(fFee)); - unifiedorder.setSpbill_create_ip(request.getRemoteAddr()); - unifiedorder.setNotify_url(WeChatConfig.getBaseUrl() + "old_pay_notify_servlet"); - unifiedorder.setTrade_type("NATIVE"); - log.info("扫码回调地址:" + WeChatConfig.getBaseUrl() + "old_pay_notify_servlet"); - - UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey); - - Recipe recipe = new Recipe(); - - recipe.setOpenid(map.get("openid")); - recipe.setPatientId(patientId); - recipe.setTreatNum(mzNum); - recipe.setOutTradeNo(unifiedorder.getOut_trade_no()); - recipe.setPayWay("1"); - recipe.setPayMoney(new BigDecimal(payMoney)); - recipe.setRecipeId(recipeId); -// recipe.setTradeNo(CodeHelper.getHisTradeNo()); // 本次his交易流水号 - recipe.setTotalFee(new BigDecimal(payMoney)); - recipe.setHisStatus(-1); - recipe.setPayStatus(-1); - if (!new RecipeDao().insert(recipe)) { - log.info("[扫码支付]存储失败"); + WxNativePayResult nativeOrder = WxPayHelper.createNativeOrder("old_pay_notify_servlet", title, new BigDecimal(payMoney), outTradeNo, "PEIS", request.getRemoteAddr()); + if (nativeOrder == null) { + log.info("[Native]扫码支付 下单失败 product_id={}", product_id); + HttpHelper.outRespAlert(response, "已缴费成功,无需支付!"); + return; + } + boolean isOk = new RecipeService().addOldPEISOrder(outTradeNo, payMoney, openid, patientId, treatNum, recipeId); + if (!isOk) { + log.info("[Native]扫码支付 存储数据失败 product_id={}", product_id); } - MchPayNativeReply reply = new MchPayNativeReply(); - reply.setAppid(appID); - reply.setMch_id(mchID); - reply.setNonce_str(UUID.randomUUID().toString().replace("-", "")); - reply.setPrepay_id(unifiedorderResult.getPrepay_id()); - reply.setResult_code("SUCCESS"); - reply.setReturn_code("SUCCESS"); - String string = PayUtil.generateMchPayNativeReplyXML(reply, mchKey); - response.getWriter().write(string); + String nativeReplyXML = WxPayHelper.toNativeReplyXML(nativeOrder.getPrepayId()); + log.info("[Native]扫码支付 nativeReplyXML={}", nativeReplyXML); + response.getWriter().write(nativeReplyXML); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { diff --git a/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet2.java b/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet2.java new file mode 100644 index 0000000..ebf737a --- /dev/null +++ b/src/main/java/com/ynxbd/wx/servlet/oldpay/QRPayServlet2.java @@ -0,0 +1,189 @@ +package com.ynxbd.wx.servlet.oldpay; + +import com.ynxbd.common.bean.enums.MerchantEnum; +import com.ynxbd.common.bean.pay.Recipe; +import com.ynxbd.common.dao.RecipeDao; +import com.ynxbd.common.helper.common.CodeHelper; +import com.ynxbd.common.helper.common.HttpHelper; +import com.ynxbd.wx.config.WeChatConfig; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.slf4j.MDC; +import weixin.popular.api.PayMchAPI; +import weixin.popular.bean.paymch.MchPayNativeReply; +import weixin.popular.bean.paymch.Unifiedorder; +import weixin.popular.bean.paymch.UnifiedorderResult; +import weixin.popular.util.PayUtil; +import weixin.popular.util.SignatureUtil; +import weixin.popular.util.XMLConverUtil; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.UUID; + +/** + * 扫码支付接口(旧版) + * + * @author 张剑峰 + * @version v1.0.0 + * @Project:微信公众号 + * @date 2018年6月4日上午10:44:31 + * @Copyright: 2018云南新八达科技有限公司 All rights reserved. + */ +@Slf4j +@WebServlet("/qrpay2") +public class QRPayServlet2 extends HttpServlet { + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + MDC.remove("ip"); + MDC.put("ip", HttpHelper.getIpAddress(request)); + + log.info("[旧版]收到扫码支付请求,开始解析..."); + + // 读取参数 + InputStream inputStream = request.getInputStream(); + StringBuilder sb = new StringBuilder(); + String s; + BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + while ((s = in.readLine()) != null) { + sb.append(s); + } + in.close(); + inputStream.close(); + + // 解析xml成map + Map map = XMLConverUtil.convertToMap(sb.toString()); + // 过滤空 设置 TreeMap +// SortedMap payParams = new TreeMap<>(); +// Iterator it = map.keySet().iterator(); +// while (it.hasNext()) { +// String parameter = (String) it.next(); +// String parameterValue = map.get(parameter); +// String v = ""; +// if (null != parameterValue) { +// v = parameterValue.trim(); +// } +// payParams.put(parameter, v); +// } + + // 验证请求签名 + if (!map.get("sign").equals(SignatureUtil.generateSign(map, WeChatConfig.MCH_KEY))) { + log.info("收到扫码支付请求:签名无效!"); + HttpHelper.outRespAlert(response, "收到扫码支付请求:签名无效!"); + return; + } + + String product_id = map.get("product_id"); + log.info("收到扫码支付请求:product_id=" + product_id + ", open_id=" + map.get("openid")); + if (!product_id.contains("XBD")) { + log.info("扫码支付:不合规则的字符串,不允许支付!product_id=" + product_id); + HttpHelper.outRespAlert(response, "扫码支付:不合规则的字符串,不允许支付!"); + return; + } + + String[] array = product_id.split("XBD"); + if (array.length < 5) { + log.info("扫码支付:参数少于5个,不允许支付!product_id=" + product_id); + HttpHelper.outRespAlert(response, "扫码支付:参数少于5个"); + return; + } + + String patientId = array[0]; + String mzNum = array[1]; + String recipeId = array[2]; + String payMoney = array[3]; + String flag = array[4]; + // String recipeIdJson = "[{\"id\":" + "\"" + recipeId + "\"" + ",\"fee\":" + payMoney + "}]"; + + log.info("扫码支付:patientId={} treatNum={}, recipeId={}, payMoney={}, flag={}", patientId, mzNum, recipeId, payMoney, flag); + + if (ObjectUtils.isEmpty(patientId) || ObjectUtils.isEmpty(mzNum) || ObjectUtils.isEmpty(payMoney)) { + log.info("扫码支付:参数中有空值,不允许支付!product_id={}", product_id); + HttpHelper.outRespAlert(response, "扫码支付:参数中有空值,不允许支付!"); + return; + } + + // 是否重复支付 + log.info("判断是否支付过:patientId={}, mzNum={}, recipeId={}", patientId, mzNum, recipeId); + if (new RecipeDao().hasHisPaidByPatient(patientId, mzNum, recipeId)) { + log.info("该码已经缴费,无需支付!product_id={}", product_id); + HttpHelper.outRespAlert(response, "该码已经缴费,无需支付!"); + return; + } + + // 统一下单 + Unifiedorder unifiedorder = new Unifiedorder(); + String appID = WeChatConfig.APP_ID; + String mchID = WeChatConfig.MCH_ID; + String mchKey = WeChatConfig.MCH_KEY; + + unifiedorder.setAppid(appID); + unifiedorder.setMch_id(mchID); + unifiedorder.setNonce_str(UUID.randomUUID().toString().replace("-", "")); + if (flag.equals("1")) { + unifiedorder.setBody("ID:" + patientId + " 处方单号:" + recipeId); + } else if (flag.equals("2")) { + unifiedorder.setBody("ID:" + patientId + " 申请单号:" + recipeId); + } + + // unifiedorder.setBody("pay"); + unifiedorder.setOut_trade_no(CodeHelper.getOutTradeNo(MerchantEnum.WX)); + + // 此处使用Float或者Double转换金额会因为精度问题丢失1分钱,使用BigDecimal来转换 + BigDecimal v1 = new BigDecimal(payMoney); + BigDecimal v2 = new BigDecimal("100"); + double b = v1.multiply(v2).doubleValue(); + int fFee = (int) b; + + unifiedorder.setTotal_fee(String.valueOf(fFee)); + unifiedorder.setSpbill_create_ip(request.getRemoteAddr()); + unifiedorder.setNotify_url(WeChatConfig.getBaseUrl() + "old_pay_notify_servlet"); + unifiedorder.setTrade_type("NATIVE"); + log.info("扫码回调地址:" + WeChatConfig.getBaseUrl() + "old_pay_notify_servlet"); + + UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey); + + Recipe recipe = new Recipe(); + + recipe.setOpenid(map.get("openid")); + recipe.setPatientId(patientId); + recipe.setTreatNum(mzNum); + recipe.setOutTradeNo(unifiedorder.getOut_trade_no()); + recipe.setPayWay("1"); + recipe.setPayMoney(new BigDecimal(payMoney)); + recipe.setRecipeId(recipeId); +// recipe.setTradeNo(CodeHelper.getHisTradeNo()); // 本次his交易流水号 + recipe.setTotalFee(new BigDecimal(payMoney)); + recipe.setHisStatus(-1); + recipe.setPayStatus(-1); + if (!new RecipeDao().insert(recipe)) { + log.info("[扫码支付]存储失败"); + } + + MchPayNativeReply reply = new MchPayNativeReply(); + reply.setAppid(appID); + reply.setMch_id(mchID); + reply.setNonce_str(UUID.randomUUID().toString().replace("-", "")); + reply.setPrepay_id(unifiedorderResult.getPrepay_id()); + reply.setResult_code("SUCCESS"); + reply.setReturn_code("SUCCESS"); + String string = PayUtil.generateMchPayNativeReplyXML(reply, mchKey); + response.getWriter().write(string); + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request, response); + } + +} diff --git a/src/main/java/com/ynxbd/wx/wxfactory/AesWxHelper.java b/src/main/java/com/ynxbd/wx/wxfactory/AesWxHelper.java index 14d4440..42c9eda 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/AesWxHelper.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/AesWxHelper.java @@ -23,7 +23,7 @@ public class AesWxHelper extends AesHelper { public static void main(String[] args) { // System.out.println(encode("397631")); System.out.println(encode("847149")); - System.out.println(decode("9CDBAA0694E6DAED726B6783D51190F2")); + System.out.println(decode("5ED36B8AF24847FBFFA6350642E38DBA")); // System.out.println(decode("FC0E7CBB32E595E2C84D44CE8B79E5325FA2C20E1090E9E5A5D7FBEA974023EB")); // System.out.println(encode("533103198603294034")); // System.out.println(decode("EF6115C81C9EA8FF79E21180FEC20294")); diff --git a/src/main/java/com/ynxbd/wx/wxfactory/WxMedicalHelper.java b/src/main/java/com/ynxbd/wx/wxfactory/WxMedicalHelper.java index 86bc9c3..ac06f50 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/WxMedicalHelper.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/WxMedicalHelper.java @@ -201,7 +201,7 @@ public class WxMedicalHelper { * @param outTradeNo 订单号 * @param medTransId 医保订单号 */ - public static MedicalOrder queryOrder(String accessToken, String outTradeNo, String medTransId) { + public static MIOrder queryOrder(String accessToken, String outTradeNo, String medTransId) { return WxFactory.Medical.Common().queryOrder( accessToken, WechatMIConfig.MD_APP_ID, @@ -247,9 +247,9 @@ public class WxMedicalHelper { /** - * [医保]HIS调用退费 + * [医保]退费 */ - public static MedicalOrder refund(String outTradeNo, String outRefundNo, String payOrdId, BigDecimal cashFee, String reason) { + public static MIOrder refund(String outTradeNo, String outRefundNo, String payOrdId, BigDecimal cashFee, String reason) { return WxFactory.Medical.Common().refund( WxCacheHelper.getAccessToken(), WechatMIConfig.MD_APP_ID, @@ -267,7 +267,7 @@ public class WxMedicalHelper { /** * [医保]退现金部分 */ - public static MedicalOrder refundCash(String outTradeNo, String outRefundNo, String payOrdId, BigDecimal cashFee, String reason) { + public static MIOrder refundCash(String outTradeNo, String outRefundNo, String payOrdId, BigDecimal cashFee, String reason) { return WxFactory.Medical.Common().refund( WxCacheHelper.getAccessToken(), WechatMIConfig.MD_APP_ID, @@ -285,7 +285,7 @@ public class WxMedicalHelper { /** * [医保]退现金部分 */ - public static MedicalOrder refundCash(String outTradeNo, String tradeNo, BigDecimal cashFee, String reason) { + public static MIOrder refundCash(String outTradeNo, String tradeNo, BigDecimal cashFee, String reason) { return refundCash(outTradeNo, "R" + tradeNo, tradeNo, cashFee, reason); } diff --git a/src/main/java/com/ynxbd/wx/wxfactory/WxPayHelper.java b/src/main/java/com/ynxbd/wx/wxfactory/WxPayHelper.java index e3a346b..cdef347 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/WxPayHelper.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/WxPayHelper.java @@ -9,6 +9,7 @@ import com.ynxbd.common.helper.common.*; import com.ynxbd.common.result.ResultEnum; import com.ynxbd.common.result.ServiceException; import com.ynxbd.wx.config.WeChatConfig; +import com.ynxbd.wx.wxfactory.bean.WxNativePayResult; import com.ynxbd.wx.wxfactory.bean.WxPayNotify; import com.ynxbd.wx.wxfactory.bean.refund.WxRefundItem; import com.ynxbd.wx.wxfactory.bean.refund.WxRefundQueryRoot; @@ -19,6 +20,7 @@ import org.apache.commons.lang3.StringUtils; import weixin.popular.api.PayMchAPI; import weixin.popular.bean.paymch.*; import weixin.popular.client.LocalHttpClient; +import weixin.popular.util.PayUtil; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; @@ -1138,18 +1140,18 @@ public class WxPayHelper { /** * 商户创建订单 * - * @param merchantEnum 枚举 - * @param title 商品标题 - * @param totalFee 订单金额 - * @param outTradeNo 订单号 - * @param notifyType 回调类型 - * @param ip ip地址 + * @param notifyUrl 回调通知链接 + * @param title 商品标题 + * @param totalFee 订单金额 + * @param outTradeNo 订单号 + * @param notifyType 回调类型 + * @param ip ip地址 * @return 成功返回 json, 否则返回null */ - public static String createNativeOrder(MerchantEnum merchantEnum, String title, BigDecimal totalFee, String outTradeNo, String notifyType, String ip) { + public static WxNativePayResult createNativeOrder(String notifyUrl, String title, BigDecimal totalFee, String outTradeNo, String notifyType, String ip) { try { - if (ObjectUtils.isEmpty(outTradeNo) || merchantEnum == null || totalFee == null || notifyType == null) { - log.info("【微信】下单参数缺失"); + if (ObjectUtils.isEmpty(outTradeNo) || ObjectUtils.isEmpty(notifyUrl) || totalFee == null || notifyType == null) { + log.info("【微信】Native下单参数缺失"); return null; } @@ -1159,7 +1161,7 @@ public class WxPayHelper { cents = totalFee.movePointRight(2).toString(); // 单位分 } catch (Exception e) { ErrorHelper.println(e); - log.info("【微信】统一下单金额转换异常"); + log.info("【微信】Native统一下单金额转换异常"); return null; } @@ -1177,7 +1179,7 @@ public class WxPayHelper { unifiedorder.setTotal_fee(cents); // 金额分 unifiedorder.setSpbill_create_ip(ip); - unifiedorder.setNotify_url(WeChatConfig.getBaseUrl() + merchantEnum.QR_NOTIFY_URL); + unifiedorder.setNotify_url(WeChatConfig.getBaseUrl() + notifyUrl); unifiedorder.setTrade_type("NATIVE"); // 额外参数 unifiedorder.setAttach(notifyType); @@ -1189,13 +1191,13 @@ public class WxPayHelper { + outTradeNo + "," + totalFee + "," + ip + "," - + merchantEnum.NOTIFY_URL); + + notifyUrl); // 微信统一下单 UnifiedorderResult unifiedorderResult = PayMchAPI.payUnifiedorder(unifiedorder, mchKey); - log.info("【微信】统一下单返回:{}", JsonHelper.toJsonString(unifiedorderResult)); + log.info("【微信】Native统一下单返回:{}", JsonHelper.toJsonString(unifiedorderResult)); if (unifiedorderResult == null) { - log.info("【微信】统一下单失败"); + log.info("【微信】Native统一下单失败"); return null; } @@ -1203,14 +1205,19 @@ public class WxPayHelper { // 下单是否成功 Boolean sign_status = unifiedorderResult.getSign_status(); if (!SUCCESS.equals(result_code) || sign_status == null || !sign_status) { - log.info("【微信】统一下单返回异常信息:sign_status={}, err_code=[ {} ], err_code_des=[ {} ]", sign_status, unifiedorderResult.getErr_code(), unifiedorderResult.getErr_code_des()); + log.info("【微信】Native统一下单返回异常信息:sign_status={}, err_code=[ {} ], err_code_des=[ {} ]", sign_status, unifiedorderResult.getErr_code(), unifiedorderResult.getErr_code_des()); return null; } String codeUrl = unifiedorderResult.getCode_url(); - log.info("codeUrl={}", codeUrl); - if (codeUrl != null) { - return codeUrl; + String prepayId = unifiedorderResult.getPrepay_id(); + log.info("微信】Native统一下单 返回 codeUrl={}, prepayId={}", codeUrl, prepayId); + if (ObjectUtils.isEmpty(codeUrl) || ObjectUtils.isEmpty(prepayId)) { + return null; } + WxNativePayResult wxNativePayResult = new WxNativePayResult(); + wxNativePayResult.setCodeUrl(codeUrl); + wxNativePayResult.setPrepayId(prepayId); + return wxNativePayResult; } catch (Exception e) { log.error("【微信】统一下单异常信息:" + e.getMessage()); ErrorHelper.println(e); @@ -1218,6 +1225,17 @@ public class WxPayHelper { return null; } + public static String toNativeReplyXML(String prepayId) { + MchPayNativeReply nativeReply = new MchPayNativeReply(); + nativeReply.setAppid(WeChatConfig.APP_ID); + nativeReply.setMch_id(WeChatConfig.MCH_ID); + nativeReply.setNonce_str(UUID.randomUUID().toString().replace("-", "")); + nativeReply.setPrepay_id(prepayId); // unifiedorderResult.getPrepay_id() + nativeReply.setResult_code(SUCCESS); + nativeReply.setReturn_code(SUCCESS); + return PayUtil.generateMchPayNativeReplyXML(nativeReply, WeChatConfig.MCH_KEY); + } + public static String dataHandle(String data) { if (ObjectUtils.isEmpty(data)) { diff --git a/src/main/java/com/ynxbd/wx/wxfactory/bean/MedicalOrder.java b/src/main/java/com/ynxbd/wx/wxfactory/bean/MIOrder.java similarity index 91% rename from src/main/java/com/ynxbd/wx/wxfactory/bean/MedicalOrder.java rename to src/main/java/com/ynxbd/wx/wxfactory/bean/MIOrder.java index 0dcf13f..d013acc 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/bean/MedicalOrder.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/bean/MIOrder.java @@ -1,55 +1,57 @@ -package com.ynxbd.wx.wxfactory.bean; - -import com.ynxbd.wx.wxfactory.WxFactory; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - - -// 医保查询订单 - -@Setter -@Getter -@ToString(callSuper = true) -@NoArgsConstructor -public class MedicalOrder extends WxFactory.ResponseCheck.ResultBase { - private String medTradeState; - private String cashTradeStatus; - private String insuranceTradeStatus; - private String tradeStatusDesc; - private String timeEnd; - private String orderType; - private String payType; - private String openid; - private String subOpenid; - private String nonceStr; - private String totalFee; - private String cashFee; - private String allowFeeChange; - private String spbillCreateIp; - private String notifyUrl; - private String insuranceFee; - private String insuranceSelfFee; - private String insuranceFundFee; - private String insuranceOtherFee; - private String hospOutTradeNo; - private String serialNo; - private String orgNo; - private String gmtOutCreate; - private String requestContent; - private String billNo; - private String limitPay; - private String medicalCardId; - private String hospitalName; - private String returnUrl; - private String responseContent; - private String body; - private String Extends; - - private String medTransId; - private String insuranceOrderId; - private String cashOrderId; - private String attach; - -} +package com.ynxbd.wx.wxfactory.bean; + +import com.ynxbd.wx.wxfactory.WxFactory; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + + +// 医保查询订单 + +@Setter +@Getter +@ToString(callSuper = true) +@NoArgsConstructor +public class MIOrder extends WxFactory.ResponseCheck.ResultBase { + private String medTradeState; + private String cashTradeStatus; + private String insuranceTradeStatus; + private String tradeStatusDesc; + private String timeEnd; + private String orderType; + private String payType; + private String openid; + private String subOpenid; + private String nonceStr; + private String totalFee; + private String cashFee; + private String allowFeeChange; + private String spbillCreateIp; + private String notifyUrl; + private String insuranceFee; + private String insuranceSelfFee; + private String insuranceFundFee; + private String insuranceOtherFee; + private String hospOutTradeNo; + private String serialNo; + private String orgNo; + private String gmtOutCreate; + private String requestContent; + private String billNo; + private String limitPay; + private String medicalCardId; + private String hospitalName; + private String returnUrl; + private String responseContent; + private String body; + private String Extends; + + private String medTransId; + private String insuranceOrderId; + private String cashOrderId; + private String attach; + + private Object orderObj; + +} diff --git a/src/main/java/com/ynxbd/wx/wxfactory/bean/OrderMIEnum.java b/src/main/java/com/ynxbd/wx/wxfactory/bean/OrderMIEnum.java index 7189d0a..a6a8c7a 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/bean/OrderMIEnum.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/bean/OrderMIEnum.java @@ -5,16 +5,18 @@ import lombok.ToString; @ToString public enum OrderMIEnum { - REG_PAY("RegPay", "挂号支付", "APPOINTMENT"), - MED_PAY("MedPay", "药费支付", "OUTPATIENT"), - DIAG_PAY("DiagPay", "诊间支付", "OUTPATIENT"), - IN_HOSP_PAY("InHospPay", "住院费支付", "INPATIENT"), - PHARMACY_PAY("PharmacyPay", "药店支付", "DRUGSTORE"), - INSURANCE_PAY("InsurancePay", "保险费支付", "OUTPATIENT"), - INT_REG_PAY("IntRegPay", "互联网医院挂号支付", "APPOINTMENT"), - INT_RE_DIAG_PAY("IntReDiagPay", "互联网医院复诊支付", ""), - INT_PSC_PAY("IntPscPay", "互联网医院处方支付", "OUTPATIENT"), - COVID_EXAM_PAY("CovidExamPay", "新冠检测费用", "OUTPATIENT"); + REG_PAY("RegPay", "挂号支付", "APPOINTMENT", "my-info.html"), + DIAG_PAY("DiagPay", "诊间支付", "OUTPATIENT", "pay-info.html#/rx-paid"), + + IN_HOSP_PAY("InHospPay", "住院费支付", "INPATIENT", ""), + + MED_PAY("MedPay", "药费支付", "OUTPATIENT", ""), + PHARMACY_PAY("PharmacyPay", "药店支付", "DRUGSTORE", ""), + INSURANCE_PAY("InsurancePay", "保险费支付", "OUTPATIENT", ""), + INT_REG_PAY("IntRegPay", "互联网医院挂号支付", "APPOINTMENT", ""), + INT_RE_DIAG_PAY("IntReDiagPay", "互联网医院复诊支付", "", ""), + INT_PSC_PAY("IntPscPay", "互联网医院处方支付", "OUTPATIENT", ""), + COVID_EXAM_PAY("CovidExamPay", "新冠检测费用", "OUTPATIENT", ""); // 微信医保编码 public final String WX_MI_CODE; @@ -22,10 +24,13 @@ public enum OrderMIEnum { public final String NAME; // 支付宝医保编码 public final String ALI_MI_CODE; + // 微信跳转地址 + public final String WX_CALL_BACK_URL; - OrderMIEnum(String WX_MI_CODE, String NAME, String ALI_MI_CODE) { + OrderMIEnum(String WX_MI_CODE, String NAME, String ALI_MI_CODE, String WX_CALL_BACK_URL) { this.WX_MI_CODE = WX_MI_CODE; this.NAME = NAME; this.ALI_MI_CODE = ALI_MI_CODE; + this.WX_CALL_BACK_URL = WX_CALL_BACK_URL; } } diff --git a/src/main/java/com/ynxbd/wx/wxfactory/medical/Client.java b/src/main/java/com/ynxbd/wx/wxfactory/medical/Client.java index d0f46a5..7ef1517 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/medical/Client.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/medical/Client.java @@ -159,7 +159,7 @@ public class Client { } - public MedicalOrder queryOrder(String accessToken, String appId, String mchId, String mdPayKey, String outTradeNo, String medTransId) { + public MIOrder queryOrder(String accessToken, String appId, String mchId, String mdPayKey, String outTradeNo, String medTransId) { JsonResult jsonResult = WxRequestHelper.postMdXml(("https://api.weixin.qq.com/payinsurance/queryorder?access_token=" + accessToken), params -> { params.put("appid", appId); params.put("mch_id", mchId); @@ -172,10 +172,10 @@ public class Client { log.info("[医保][查询订单] req={}", JsonHelper.toJsonString(params)); }); if (!jsonResult.success()) { - return new MedicalOrder().createResult(jsonResult); + return new MIOrder().createResult(jsonResult); } log.info("[医保][查询订单] req={}", JsonHelper.toJsonString(jsonResult)); - return jsonResult.dataMapToBean(MedicalOrder.class); + return jsonResult.dataMapToBean(MIOrder.class); } @@ -192,7 +192,7 @@ public class Client { * @param cashFee 退费现金 * @param reason 退费原因 */ - public MedicalOrder refund(String accessToken, String appId, String mchId, String mdPayKey, String outTradeNo, String outRefundNo, String payOrdId, BigDecimal cashFee, MdRefundTypeEnum mdRefundTypeEnum, String reason) { + public MIOrder refund(String accessToken, String appId, String mchId, String mdPayKey, String outTradeNo, String outRefundNo, String payOrdId, BigDecimal cashFee, MdRefundTypeEnum mdRefundTypeEnum, String reason) { if (reason == null) { reason = "申请退款"; } @@ -240,7 +240,7 @@ public class Client { if (!jsonResult.success()) { return new MedicalRefund().createResult(jsonResult); } - return jsonResult.dataMapToBean(MedicalOrder.class); + return jsonResult.dataMapToBean(MIOrder.class); } diff --git a/src/main/java/com/ynxbd/wx/wxfactory/medical/WechatMIConfig.java b/src/main/java/com/ynxbd/wx/wxfactory/medical/WechatMIConfig.java index e7d3342..b89847a 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/medical/WechatMIConfig.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/medical/WechatMIConfig.java @@ -35,6 +35,7 @@ public class WechatMIConfig { public static boolean IS_DEV; public static boolean IS_AUTO_REFUND; + public static String DEV_OPENID; static { ProperHelper config = new ProperHelper().read("medical.properties"); @@ -44,6 +45,7 @@ public class WechatMIConfig { IS_DEV = config.getBoolean("medical.is_dev", true); IS_AUTO_REFUND = config.getBoolean("medical.is_auto_refund", false); + DEV_OPENID = config.getString("medical.dev_openid", null); CHANNEL = config.getString("medical.channel"); ORG_NO = config.getString("medical.org_no"); @@ -88,7 +90,6 @@ public class WechatMIConfig { } - private static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { diff --git a/src/main/java/com/ynxbd/wx/wxfactory/utils/XmlHelper.java b/src/main/java/com/ynxbd/wx/wxfactory/utils/XmlHelper.java index f499c78..51814ec 100644 --- a/src/main/java/com/ynxbd/wx/wxfactory/utils/XmlHelper.java +++ b/src/main/java/com/ynxbd/wx/wxfactory/utils/XmlHelper.java @@ -3,6 +3,7 @@ package com.ynxbd.wx.wxfactory.utils; import com.alibaba.fastjson.JSON; import com.ynxbd.wx.wxfactory.bean.MedicalNotify; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import org.dom4j.*; import java.util.HashMap; @@ -66,7 +67,7 @@ public class XmlHelper { */ public static Map xmlOuterToMap(String xml, boolean isEmpty) { Map result = new HashMap<>(); - if (xml == null || "".equals(xml)) { + if (ObjectUtils.isEmpty(xml)) { return result; } Document document; diff --git a/src/main/resources/medical-ali.properties b/src/main/resources/medical-ali.properties index 51523fe..c223c60 100644 --- a/src/main/resources/medical-ali.properties +++ b/src/main/resources/medical-ali.properties @@ -2,6 +2,7 @@ mi_ali.is_dev=true mi_ali.is_enable=true mi_ali.is_auto_refund=true +mi_ali.mi_ali.dev_openid= # \u5B9A\u70B9\u533B\u7597\u673A\u6784\u540D\u79F0\uFF08\u652F\u4ED8\u5B9D\u53EF\u80FD\u4E0D\u540C\uFF09 mi_ali.org_hospital_name=\u4E91\u5357\u7701\u6EC7\u5357\u4E2D\u5FC3\u533B\u9662 diff --git a/src/main/resources/medical.properties b/src/main/resources/medical.properties index 41e88f3..c5a0185 100644 --- a/src/main/resources/medical.properties +++ b/src/main/resources/medical.properties @@ -2,6 +2,8 @@ medical.is_dev=true medical.is_enable=true medical.is_auto_refund=true +# \u7528\u4E8E\u6D4B\u8BD5\u7684openid +medical.dev_openid= # \u53EA\u6709\u8BA2\u9605\u53F7\u7684\u533B\u9662\u6216\u53EA\u60F3\u7528\u8BA2\u9605\u53F7\u7684\u533B\u9662 \u7533\u8BF7\u4E00\u4E2A\u7A7A\u670D\u52A1\u53F7 \u7A7A\u670D\u52A1\u53F7\u914D\u7F6E\u5FAE\u4FE1\u516C\u4F17\u53F7\uFF08\u670D\u52A1\u53F7\uFF09appId medical.md_app_id=wx73fc2dbf84a43dce