|
@@ -0,0 +1,817 @@
|
|
|
+package com.demo.wjj.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.demo.wjj.bo.*;
|
|
|
+import com.demo.wjj.mapper.ExplainMapper;
|
|
|
+import com.demo.wjj.mapper.OfferMapper;
|
|
|
+import com.demo.wjj.po.*;
|
|
|
+import com.demo.wjj.service.*;
|
|
|
+import com.demo.wjj.utils.*;
|
|
|
+import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
|
|
|
+import org.apache.commons.codec.binary.Base64;
|
|
|
+import org.apache.commons.codec.digest.DigestUtils;
|
|
|
+import org.apache.commons.collections4.MapUtils;
|
|
|
+import org.apache.commons.lang3.ArrayUtils;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.apache.commons.lang3.math.NumberUtils;
|
|
|
+import org.dom4j.Document;
|
|
|
+import org.dom4j.DocumentException;
|
|
|
+import org.dom4j.Element;
|
|
|
+import org.dom4j.io.SAXReader;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.BeanWrapper;
|
|
|
+import org.springframework.beans.BeanWrapperImpl;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class PaySaleforServiceImpl implements PaySaleforService {
|
|
|
+ private final Logger LOG = LoggerFactory.getLogger(getClass());
|
|
|
+ /**
|
|
|
+ * 回调地址域名
|
|
|
+ */
|
|
|
+ @Value("${weixin.pay.domain}")
|
|
|
+ private String domain;
|
|
|
+ /**
|
|
|
+ * 微信支付接口
|
|
|
+ */
|
|
|
+ @Value("${weixin.pay.apiAddress}")
|
|
|
+ private String payApiAddress;
|
|
|
+ @Value("${jpjgh}")
|
|
|
+ private String jpjgh;
|
|
|
+ @Autowired
|
|
|
+ private DiggerAgentService diggerAgentService;
|
|
|
+ @Autowired
|
|
|
+ private WeiXinPayService weiXinPayService;
|
|
|
+ @Autowired
|
|
|
+ private CreditsExchangeService creditsExchangeService;
|
|
|
+ @Autowired
|
|
|
+ private AgentService agentService;
|
|
|
+ @Autowired
|
|
|
+ private DepositService depositService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ DisplaceAuditService displaceAuditService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ ExplainMapper explainMapper;
|
|
|
+ @Autowired
|
|
|
+ private SaleService saleService;
|
|
|
+ @Autowired
|
|
|
+ OfferMapper offerMapper;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @CommitTransactional
|
|
|
+ public ExecuteResult<CreateOrderResult> createOrder(CreateOrderBo createOrderBo) {
|
|
|
+ LOG.info("进入创建订单, createOrderBo:{}", createOrderBo);
|
|
|
+ String agentId = createOrderBo.getAgentId();
|
|
|
+ if (StringUtils.isBlank(agentId)) {
|
|
|
+ LOG.warn("agentId为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+
|
|
|
+ String openId = createOrderBo.getOpenId();
|
|
|
+ if (StringUtils.isBlank(openId)) {
|
|
|
+ LOG.warn("微信openId为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+
|
|
|
+ String productId = createOrderBo.getProductId();
|
|
|
+ /* if (StringUtils.isBlank(productId)) {
|
|
|
+ LOG.warn("productId为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }*/
|
|
|
+ Agent agent = agentService.getAgent(agentId);
|
|
|
+ if (agent == null) {
|
|
|
+ LOG.info("未查找到商家信息, agentId:{}", agentId);
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+
|
|
|
+ final String appId = agent.getMchAppId();
|
|
|
+ if (StringUtils.isBlank(appId)) {
|
|
|
+ LOG.info("商家appId为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+
|
|
|
+ String appSecret = agent.getMchAppSecret();
|
|
|
+ if (StringUtils.isBlank(appSecret)) {
|
|
|
+ LOG.info("商家appSecret为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+
|
|
|
+ String mchId = agent.getMchId();
|
|
|
+ if (StringUtils.isBlank(mchId)) {
|
|
|
+ LOG.info("微信商家mchId为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+ Sale sale = saleService.getSale(agentId, openId);
|
|
|
+ if (sale == null) {
|
|
|
+ LOG.info("未查找到销售员信息, agentId:{}, openId:{}", agentId, openId);
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.NO_AUTHORITY);
|
|
|
+ }
|
|
|
+ Integer deposit = offerMapper.selectDisplaceDeposit(productId);
|
|
|
+
|
|
|
+ CreateOrderParameter orderParameter = new CreateOrderParameter();
|
|
|
+ orderParameter.setAppId(appId);
|
|
|
+ orderParameter.setMchId(mchId);
|
|
|
+ orderParameter.setDeviceInfo("WEB");
|
|
|
+ orderParameter.setNonceStr(generateNonceStr());
|
|
|
+ orderParameter.setBody("固定保证金");
|
|
|
+ orderParameter.setAttach("productId@" + productId);
|
|
|
+ final String outTradeNo = generateTradeNo(agentId, sale.getSaleId());
|
|
|
+ orderParameter.setOutTradeNo(outTradeNo);
|
|
|
+ orderParameter.setTotalFee(deposit);
|
|
|
+ orderParameter.setSpbillCreateIp(createOrderBo.getClientIp());
|
|
|
+ orderParameter.setOpenId(openId);
|
|
|
+ orderParameter.setNotifyUrl(createNotifyUrl());
|
|
|
+ orderParameter.setSign(SignFactory.generate(orderParameter.createRequestParameter(), appSecret));
|
|
|
+
|
|
|
+ // 请求微信接口
|
|
|
+ String parameterXml = orderParameter.toString();
|
|
|
+ LOG.info("请求参数xml格式:{}", parameterXml);
|
|
|
+ byte[] bytes;
|
|
|
+ try {
|
|
|
+ bytes = parameterXml.getBytes("utf-8");
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
+ LOG.error("编码请求参数异常", e);
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+ HttpResult httpResult = HttpUtils.post(payApiAddress, bytes);
|
|
|
+ if (httpResult.getStatusCode() != 200) {
|
|
|
+ LOG.info("请求微信创建订单接口失败, statusCode:{}", httpResult.getStatusCode());
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+ PaySaleforServiceImpl.OrderResponse response = parseOrderResponse(httpResult.getContent());
|
|
|
+ if (response == null) {
|
|
|
+ LOG.info("解析订单响应内容失败");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+
|
|
|
+ String prepayId = response.getPrepayId();
|
|
|
+ if (StringUtils.isBlank(prepayId)) {
|
|
|
+ LOG.info("解析订单参数prepayId为空");
|
|
|
+ return new ExecuteResult<CreateOrderResult>()
|
|
|
+ .setExecuteResult(false)
|
|
|
+ .setResult(Result.Failure);
|
|
|
+ }
|
|
|
+ WeiXinPay weiXinPay = new WeiXinPay();
|
|
|
+ weiXinPay.setProductId(productId);
|
|
|
+ weiXinPay.setProductType(2);
|
|
|
+ weiXinPay.setOrderNo(outTradeNo);
|
|
|
+ weiXinPay.setWxOpenId(openId);
|
|
|
+ weiXinPay.setAgentId(agentId);
|
|
|
+ weiXinPay.setDiggerAgentId(sale.getSaleId());
|
|
|
+ weiXinPay.setDeposit(deposit);
|
|
|
+ weiXinPay.setCreateTime(new Date());
|
|
|
+ weiXinPay.setOrderStatus(WeiXinPay.ORDER_STATUS_WAIT);
|
|
|
+ weiXinPay.setPrepayId(prepayId);
|
|
|
+
|
|
|
+ PayParameterJson payParameter = new PayParameterJson();
|
|
|
+ payParameter.setMobile(createOrderBo.getMobile());
|
|
|
+ payParameter.setName(createOrderBo.getName());
|
|
|
+ /* 手机端用户不能设置通票 wangqing 201811302208
|
|
|
+ payParameter.setTp(createOrderBo.getTp());
|
|
|
+ */
|
|
|
+ weiXinPay.setParameterJson(JSON.toJSONString(payParameter));
|
|
|
+ weiXinPayService.save(weiXinPay);
|
|
|
+ Deposit newDeposit = new Deposit();
|
|
|
+ newDeposit.setId(UuidUtils.generate());
|
|
|
+ newDeposit.setProductName("固定保证金");
|
|
|
+// newDeposit.setWxPayNo(prepayId);
|
|
|
+ newDeposit.setDaId(sale.getId());
|
|
|
+ newDeposit.setAgentId(agentId);
|
|
|
+ newDeposit.setAgentName(sale.getSaleName());
|
|
|
+ newDeposit.setAgentPayNo(outTradeNo);
|
|
|
+ newDeposit.setPayAmout(new Double(deposit / 100));
|
|
|
+ newDeposit.setPayStatus("0");
|
|
|
+ newDeposit.setPayCreatetime(new Date());
|
|
|
+ /* 手机端用户不能设置通票 wangqing 201811302208
|
|
|
+ newDeposit.setIsCommon(createOrderBo.getTp() + "");
|
|
|
+ */
|
|
|
+ // 默认不是通票, 通票是不需要支付保证金
|
|
|
+ newDeposit.setIsCommon("0");
|
|
|
+ depositService.save(newDeposit);
|
|
|
+ CreateOrderResult createOrderResult = new CreateOrderResult();
|
|
|
+ createOrderResult.setAppId(appId);
|
|
|
+ createOrderResult.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
|
|
|
+ createOrderResult.setNonceStr(RandomUtils.generate(10));
|
|
|
+ createOrderResult.setPackageStr(prepayId);
|
|
|
+ createOrderResult.setSignType("MD5");
|
|
|
+ createOrderResult.setPaySign(SignFactory.generate(createOrderResult.createRequestParameter(), appSecret));
|
|
|
+ createOrderResult.setOrderNo(outTradeNo);
|
|
|
+
|
|
|
+ LOG.info("退出创建订单, createOrderResult:{}", createOrderResult);
|
|
|
+ return new ExecuteResult<CreateOrderResult>().setExecuteResult(true).setData(createOrderResult);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private PaySaleforServiceImpl.OrderResponse parseOrderResponse(String content) {
|
|
|
+ LOG.info("进入解析订单响应内容, content:{}", content);
|
|
|
+ if (StringUtils.isBlank(content)) {
|
|
|
+ LOG.info("响应内容为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] bytes = content.getBytes();
|
|
|
+ ByteInputStream byteInputStream = new ByteInputStream(bytes, bytes.length);
|
|
|
+
|
|
|
+ PaySaleforServiceImpl.OrderResponse response = new PaySaleforServiceImpl.OrderResponse();
|
|
|
+ BeanWrapper bean = new BeanWrapperImpl(response);
|
|
|
+ SAXReader reader = new SAXReader();
|
|
|
+ Document document;
|
|
|
+ try {
|
|
|
+ document = reader.read(byteInputStream);
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOG.error("读取响应内容流失败", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Element root = document.getRootElement();
|
|
|
+ List<Element> elements = root.elements();
|
|
|
+
|
|
|
+ String name;
|
|
|
+ String attribute;
|
|
|
+ for (Element element : elements) {
|
|
|
+ name = element.getName();
|
|
|
+ attribute = response.getAttribute(name);
|
|
|
+
|
|
|
+ if (attribute != null) {
|
|
|
+ bean.setPropertyValue(attribute, element.getStringValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ LOG.info("退出解析订单响应内容, response:{}", response);
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+ private String createNotifyUrl() {
|
|
|
+ final String path = "diggerDisplace/payfor/callback";
|
|
|
+
|
|
|
+ if (domain.endsWith("/")) {
|
|
|
+ return domain + path;
|
|
|
+ } else {
|
|
|
+ return domain + "/" + path;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建交易订单号
|
|
|
+ * @param agentId 商家id
|
|
|
+ * @param daId 车商id
|
|
|
+ * @return 交易订单号
|
|
|
+ */
|
|
|
+ private String generateTradeNo(String agentId, String daId) {
|
|
|
+ LOG.info("进入创建交易订单号, agentId:{}, daId:{}", agentId, daId);
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
|
|
+
|
|
|
+ String str = "";
|
|
|
+ final int length = 4;
|
|
|
+ Random random = new Random();
|
|
|
+ for (int i = 1; i <= length; i++) {
|
|
|
+ str += random.nextInt(10);
|
|
|
+ }
|
|
|
+
|
|
|
+ String tradeNo = sdf.format(new Date()) + daId.replace("-", "") + str;
|
|
|
+ LOG.info("退出创建交易订单, tradeNo:{}, daId:{}", tradeNo, daId);
|
|
|
+ return tradeNo;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 生成随机串
|
|
|
+ * @return 随机串
|
|
|
+ */
|
|
|
+ private String generateNonceStr() {
|
|
|
+ final int length = 10;
|
|
|
+ String str = "";
|
|
|
+ Random random = new Random();
|
|
|
+ for (int i = 1; i <= length; i++) {
|
|
|
+ str += random.nextInt(10);
|
|
|
+ }
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ @CommitTransactional
|
|
|
+ public String payCallback(String xmlStr) {
|
|
|
+ LOG.info("进入微信订单支付回调, xmlStr:{}", xmlStr);
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(xmlStr)) {
|
|
|
+ LOG.info("xmlStr为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<String, String> result = parseCallBack(xmlStr);
|
|
|
+ if (result == null || MapUtils.isEmpty(result)) {
|
|
|
+ LOG.info("解析微信支付回调xml失败");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ final String signKey = "sign";
|
|
|
+ String sign = result.get(signKey);
|
|
|
+ if (StringUtils.isBlank(sign)) {
|
|
|
+ LOG.info("sign为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ final String outTradeNoKey = "out_trade_no";
|
|
|
+ String outTradeNo = result.get(outTradeNoKey);
|
|
|
+ if (StringUtils.isBlank(outTradeNo)) {
|
|
|
+ LOG.info("商户订单号为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ final String transactionIdKey = "transaction_id";
|
|
|
+ final String transactionId = result.get(transactionIdKey);
|
|
|
+ if (StringUtils.isBlank(transactionId)) {
|
|
|
+ LOG.info("微信支付订单号为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ WeiXinPay weiXinPay = weiXinPayService.get(outTradeNo);
|
|
|
+ if (weiXinPay == null) {
|
|
|
+ LOG.info("未查找到微信支付订单, outTradeNo:{}", outTradeNo);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String response = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
|
|
|
+ if (weiXinPay.getOrderStatus() != 1) {
|
|
|
+ LOG.info("微信订单支付状态为非支付状态, orderStatus:{}", weiXinPay.getOrderStatus());
|
|
|
+ LOG.info("退出微信订单支付回调, result => {}", response);
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+ boolean updateResult = weiXinPayService.updateOrderCallback(outTradeNo, xmlStr);
|
|
|
+ if (!updateResult) {
|
|
|
+ LOG.info("更新微信支付订单回调xml失败");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String agentId = weiXinPay.getAgentId();
|
|
|
+ if (StringUtils.isBlank(agentId)) {
|
|
|
+ LOG.info("微信支付订单对应的商家id为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Agent agent = agentService.getAgent(agentId);
|
|
|
+ if (agent == null) {
|
|
|
+ LOG.info("未查找到商家, agentId:{}", agentId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String mchAppSecret = agent.getMchAppSecret();
|
|
|
+ if (StringUtils.isBlank(mchAppSecret)) {
|
|
|
+ LOG.info("商家mchAppSecret为空, agentId:{}", agentId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ result.remove(signKey);
|
|
|
+ String generateSign = SignFactory.generate(result, mchAppSecret);
|
|
|
+ LOG.info("生成的sign: {}", generateSign);
|
|
|
+ if (!sign.equals(generateSign)) {
|
|
|
+ LOG.info("签名验证失败");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ final String returnCodeKey = "return_code";
|
|
|
+ String returnCode = result.get(returnCodeKey);
|
|
|
+ if ("SUCCESS".equals(returnCode)) {
|
|
|
+ final String payMoneyKey = "cash_fee";
|
|
|
+ String payMoney = result.get(payMoneyKey);
|
|
|
+ if (StringUtils.isBlank(payMoney) || !NumberUtils.isDigits(payMoney)) {
|
|
|
+ LOG.info("订单支付金额非法, payMoney:{}", payMoney);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Integer.parseInt(payMoney) != weiXinPay.getDeposit()) {
|
|
|
+ LOG.info("支付金额与订单金额不一致, payMoney:{}, deposit:{}", payMoney, weiXinPay.getDeposit());
|
|
|
+ weiXinPayService.updateOrderStatus(weiXinPay.getId(), WeiXinPay.ORDER_STATUS_MONEY_ERROR);
|
|
|
+ return null;
|
|
|
+ } else {
|
|
|
+ LOG.info("更新是否支付保证金="+weiXinPay.getProductId());
|
|
|
+ displaceAuditService.updateByDisplaceId(weiXinPay.getProductId(),"2","1");
|
|
|
+ weiXinPayService.updateOrderStatus(weiXinPay.getId(), WeiXinPay.ORDER_STATUS_SUCCESS);
|
|
|
+ depositService.updatePayStatus(weiXinPay.getOrderNo(), transactionId, "1");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ } else {
|
|
|
+ boolean updateStatusResult = weiXinPayService.updateOrderStatus(weiXinPay.getId(), WeiXinPay.ORDER_STATUS_FAUILRE);
|
|
|
+ if (!updateStatusResult) {
|
|
|
+ LOG.info("更新订单状态失败");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ saleService.updateMemberLevelBySaleId(weiXinPay.getDiggerAgentId(),2);
|
|
|
+ saleService.updatePlatTpSet(weiXinPay.getDiggerAgentId(),1);//设置为通票
|
|
|
+
|
|
|
+ LOG.info("退出微信订单支付回调, result => {}", response);
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 解析回调xml
|
|
|
+ * @param xmlStr xml
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private Map<String, String> parseCallBack(String xmlStr) {
|
|
|
+ LOG.info("进入解析微信支付回调xml, xmlStr:{}", xmlStr);
|
|
|
+
|
|
|
+ SAXReader reader = new SAXReader();
|
|
|
+ Document document;
|
|
|
+
|
|
|
+ byte[] bytes;
|
|
|
+ try {
|
|
|
+ bytes = xmlStr.getBytes("UTF-8");
|
|
|
+ } catch (UnsupportedEncodingException e) {
|
|
|
+ LOG.error("编码响应内容异常", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
|
|
+ reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
|
|
+ reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
|
|
+
|
|
|
+ document = reader.read(new ByteInputStream(bytes, bytes.length));
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOG.error("读取响应内容异常", e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, String> result = new HashMap<>();
|
|
|
+ Element root = document.getRootElement();
|
|
|
+ List<Element> elements = root.elements();
|
|
|
+ for (Element element : elements) {
|
|
|
+ result.put(element.getName(), element.getStringValue());
|
|
|
+ }
|
|
|
+ LOG.info("退出解析微信支付回调xml, result => {}", result);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class OrderResponse {
|
|
|
+ private Map<String, String> attributeMap = new HashMap<>();
|
|
|
+
|
|
|
+ public OrderResponse() {
|
|
|
+ attributeMap.put("return_code", "returnCode");
|
|
|
+ attributeMap.put("return_msg", "returnMsg");
|
|
|
+ attributeMap.put("appid", "appId");
|
|
|
+ attributeMap.put("mch_id", "mchId");
|
|
|
+ attributeMap.put("device_info", "deviceInfo");
|
|
|
+ attributeMap.put("nonce_str", "nonceStr");
|
|
|
+ attributeMap.put("sign", "sign");
|
|
|
+ attributeMap.put("result_code", "resultCode");
|
|
|
+ attributeMap.put("err_code", "errCode");
|
|
|
+ attributeMap.put("err_code_des", "errCodeDes");
|
|
|
+ attributeMap.put("trade_type", "tradeType");
|
|
|
+ attributeMap.put("prepay_id", "prepayId");
|
|
|
+ attributeMap.put("code_url", "codeUrl");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return JSON.toJSONString(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getAttribute(String key) {
|
|
|
+ return attributeMap.get(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String returnCode;
|
|
|
+ private String returnMsg;
|
|
|
+ private String appId;
|
|
|
+ private String mchId;
|
|
|
+ private String deviceInfo;
|
|
|
+ private String nonceStr;
|
|
|
+ private String sign;
|
|
|
+ private String resultCode;
|
|
|
+ private String errCode;
|
|
|
+ private String errCodeDes;
|
|
|
+ private String tradeType;
|
|
|
+ private String prepayId;
|
|
|
+ private String codeUrl;
|
|
|
+
|
|
|
+ public String getReturnCode() {
|
|
|
+ return returnCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setReturnCode(String returnCode) {
|
|
|
+ this.returnCode = returnCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getReturnMsg() {
|
|
|
+ return returnMsg;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setReturnMsg(String returnMsg) {
|
|
|
+ this.returnMsg = returnMsg;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getAppId() {
|
|
|
+ return appId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setAppId(String appId) {
|
|
|
+ this.appId = appId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getMchId() {
|
|
|
+ return mchId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setMchId(String mchId) {
|
|
|
+ this.mchId = mchId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getDeviceInfo() {
|
|
|
+ return deviceInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setDeviceInfo(String deviceInfo) {
|
|
|
+ this.deviceInfo = deviceInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getNonceStr() {
|
|
|
+ return nonceStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setNonceStr(String nonceStr) {
|
|
|
+ this.nonceStr = nonceStr;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSign() {
|
|
|
+ return sign;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setSign(String sign) {
|
|
|
+ this.sign = sign;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getResultCode() {
|
|
|
+ return resultCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setResultCode(String resultCode) {
|
|
|
+ this.resultCode = resultCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getErrCode() {
|
|
|
+ return errCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setErrCode(String errCode) {
|
|
|
+ this.errCode = errCode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getErrCodeDes() {
|
|
|
+ return errCodeDes;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setErrCodeDes(String errCodeDes) {
|
|
|
+ this.errCodeDes = errCodeDes;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getTradeType() {
|
|
|
+ return tradeType;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setTradeType(String tradeType) {
|
|
|
+ this.tradeType = tradeType;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getPrepayId() {
|
|
|
+ return prepayId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setPrepayId(String prepayId) {
|
|
|
+ this.prepayId = prepayId;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getCodeUrl() {
|
|
|
+ return codeUrl;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setCodeUrl(String codeUrl) {
|
|
|
+ this.codeUrl = codeUrl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @CommitTransactional
|
|
|
+ @Override
|
|
|
+ public ExecuteResult<String> refund(String id) {
|
|
|
+ LOG.info("进入微信退款, id:{}", id);
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(id)) {
|
|
|
+ LOG.info("保证金id为空");
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("请求参数错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ Deposit deposit = depositService.getDeposit(id);
|
|
|
+ if (deposit == null) {
|
|
|
+ LOG.info("未查询到保证金, id:{}", id);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("未查询到保证金信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ final String refunded = "2";
|
|
|
+ if (refunded.equals(deposit.getPayStatus())) {
|
|
|
+ LOG.info("保证金已退款, id:{}", id);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("保证金已退款");
|
|
|
+ }
|
|
|
+
|
|
|
+ final String[] allowedRefund = {"1", "4"}; //已支付/退款失败
|
|
|
+ if (!ArrayUtils.contains(allowedRefund, deposit.getPayStatus())) {
|
|
|
+ LOG.info("保证金状态处于不允许退款状态, status:{}", deposit.getPayStatus());
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("保证金支付状态错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ final Agent agent = agentService.getAgent(deposit.getAgentId());
|
|
|
+ if (agent == null) {
|
|
|
+ LOG.info("未查找到商家, agentId:{}", deposit.getAgentId());
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("未查找到支付保证金对应的商家信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ final String apiUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
|
|
|
+
|
|
|
+ final String outRefundNo;
|
|
|
+ if (StringUtils.isBlank(deposit.getRefundNo())) {
|
|
|
+ LOG.info("微信退款单号为空,自动生成单号");
|
|
|
+ outRefundNo = generateTradeNo(agent.getAgentId(), deposit.getDaId());
|
|
|
+ } else {
|
|
|
+ LOG.info("微信退款单号不为空, 使用已存在单号. {}", deposit.getRefundNo());
|
|
|
+ outRefundNo = deposit.getRefundNo();
|
|
|
+ }
|
|
|
+
|
|
|
+ ApplyRefundParameter refundParameter = new ApplyRefundParameter();
|
|
|
+ refundParameter.setAppId(agent.getAppId());
|
|
|
+ refundParameter.setMchId(agent.getMchAppId());
|
|
|
+ refundParameter.setNonceStr(RandomUtils.generate(10));
|
|
|
+ refundParameter.setTransactionId(deposit.getWxPayNo());
|
|
|
+ refundParameter.setOutRefundNo(outRefundNo);
|
|
|
+ final int depositAmount = deposit.getPayAmout().intValue();
|
|
|
+ refundParameter.setTotalFee(depositAmount);
|
|
|
+ refundParameter.setRefundFee(depositAmount);
|
|
|
+ refundParameter.setNotifyUrl(domain + "/pay/refund/" + deposit.getId() + "/callback");
|
|
|
+ refundParameter.setSign(SignFactory.generate(refundParameter.createRequestParameter(), agent.getAppSecret()));
|
|
|
+
|
|
|
+ final String refundFail = "4";
|
|
|
+ String content = null;
|
|
|
+
|
|
|
+ final HttpResult httpResult = HttpUtils.post(apiUrl, refundParameter.toString().getBytes(StandardCharsets.UTF_8));
|
|
|
+ if (httpResult.getStatusCode() != 200) {
|
|
|
+ LOG.info("请求微信退款接口失败, statusCode:{}", httpResult.getStatusCode());
|
|
|
+ updateApplyRefund(deposit.getId(), refunded, content);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("请求微信退款接口失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ content = httpResult.getContent();
|
|
|
+ if (StringUtils.isBlank(content)) {
|
|
|
+ LOG.info("请求微信退款接口, 响应内容为空");
|
|
|
+ updateApplyRefund(deposit.getId(), refundFail, content);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("请求微信退款接口失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG.debug("请求微信退款接口, 响应内容:{}", content);
|
|
|
+
|
|
|
+ ApplyRefundResponse applyRefundResponse = new ApplyRefundResponse();
|
|
|
+ try {
|
|
|
+ applyRefundResponse.parseResponse(content);
|
|
|
+ } catch (DocumentException e) {
|
|
|
+ LOG.error("解析申请微信退款响应内容异常", e);
|
|
|
+ updateApplyRefund(deposit.getId(), refundFail, content);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData("请求微信退款接口失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ if ("SUCCESS".equals(applyRefundResponse.getReturnCode())) {
|
|
|
+ if ("SUCCESS".equals(applyRefundResponse.getResultCode())) {
|
|
|
+ LOG.info("请求微信申请退款成功");
|
|
|
+ final String applyRefundSuccess = "3";
|
|
|
+ updateApplyRefund(deposit.getId(), applyRefundSuccess, content);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(true);
|
|
|
+ } else {
|
|
|
+ LOG.info("请求微信申请退款失败");
|
|
|
+ updateApplyRefund(deposit.getId(), refundFail, content);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData(applyRefundResponse.getErrCodeDes());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ LOG.info("请求微信申请退款失败");
|
|
|
+ updateApplyRefund(deposit.getId(), refundFail, content);
|
|
|
+ return new ExecuteResult<String>().setExecuteResult(false).setData(applyRefundResponse.getReturnMsg());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @CommitTransactional
|
|
|
+ @Override
|
|
|
+ public String refundCallback(String depositId, String xml) {
|
|
|
+ LOG.info("进入微信退款回调, depositId:{}, xml:{}", depositId, xml);
|
|
|
+
|
|
|
+ final Deposit deposit = depositService.getDeposit(depositId);
|
|
|
+ if (deposit == null) {
|
|
|
+ LOG.info("未查找到保证金, depositId:{}", depositId);
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+
|
|
|
+ if ("2".equals(deposit.getPayStatus())) {
|
|
|
+ LOG.info("保证金已退款, depositId:{}", deposit);
|
|
|
+ return "SUCCESS";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!"3".equals(deposit.getPayStatus())) {
|
|
|
+ LOG.info("保证金状态未非退款中状态, status:{}", deposit.getPayStatus());
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+
|
|
|
+ final Agent agent = agentService.getAgent(deposit.getAgentId());
|
|
|
+ if (agent == null) {
|
|
|
+ LOG.info("未查找到商家, agentId:{}", deposit.getAgentId());
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+
|
|
|
+ final String refundFail = "4";
|
|
|
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
|
|
|
+ SAXReader reader = new SAXReader();
|
|
|
+ try {
|
|
|
+ final Document document = reader.read(inputStream);
|
|
|
+ final Element rootElement = document.getRootElement();
|
|
|
+ final String returnCode = rootElement.element("return_code").getStringValue();
|
|
|
+
|
|
|
+ if ("SUCCESS".equals(returnCode)) {
|
|
|
+ final Element reqInfo = rootElement.element("req_info");
|
|
|
+ if (reqInfo == null) {
|
|
|
+ LOG.info("xml中未包含req_info节点");
|
|
|
+ updateRefund(depositId, refundFail, xml);
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+ final String reqInfoContent = reqInfo.getStringValue();
|
|
|
+ final byte[] decodeBase64 = Base64.decodeBase64(reqInfoContent.getBytes(StandardCharsets.UTF_8));
|
|
|
+ final String secretMD5 = DigestUtils.md5Hex(agent.getMchAppSecret()).toLowerCase();
|
|
|
+ final String name = "AES/ECB/PKCS7Padding";
|
|
|
+ String decryptXml;
|
|
|
+ try {
|
|
|
+ Cipher cipher = Cipher.getInstance(name, "BC");
|
|
|
+ SecretKeySpec keySpec = new SecretKeySpec(secretMD5.getBytes(StandardCharsets.UTF_8), "AES");
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
|
|
+ byte[] decoded = cipher.doFinal(decodeBase64);
|
|
|
+ decryptXml = new String(decoded, StandardCharsets.UTF_8);
|
|
|
+ LOG.info("微信退款解密xml:{}", decryptXml);
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOG.error("解密微信退款xml异常", e);
|
|
|
+ updateRefund(depositId, refundFail, xml);
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+
|
|
|
+ ByteArrayInputStream inputStream1 = new ByteArrayInputStream(decryptXml.getBytes(StandardCharsets.UTF_8));
|
|
|
+ SAXReader reader1 = new SAXReader();
|
|
|
+ final Document document1 = reader1.read(inputStream1);
|
|
|
+ final Element rootElement1 = document1.getRootElement();
|
|
|
+ final String refundStatus = rootElement1.element("refund_status").getStringValue();
|
|
|
+ final String refundSuccess = "2";
|
|
|
+ updateRefund(depositId, "SUCCESS".equalsIgnoreCase(refundStatus) ? refundSuccess : refundFail, xml);
|
|
|
+ LOG.info("退出微信退款回调");
|
|
|
+ return "SUCCESS";
|
|
|
+
|
|
|
+ } else {
|
|
|
+ LOG.info("微信退款失败");
|
|
|
+ updateRefund(depositId, refundFail, xml);
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ LOG.error("解析微信退款回调xml异常", e);
|
|
|
+ updateRefund(depositId, refundFail, xml);
|
|
|
+ return "FAIL";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateApplyRefund(String id, String status, String xml) {
|
|
|
+ depositService.updateApplyRefund(id, status, xml);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateRefund(String id, String status, String xml) {
|
|
|
+ depositService.updateRefund(id, status, xml);
|
|
|
+ }
|
|
|
+}
|