package com.crm.settlement.service.impl; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.TypeReference; import com.crm.rely.backend.core.dto.base.BaseResultDto; import com.crm.rely.backend.core.exception.PayValidatedException; import com.crm.rely.backend.core.exception.ServiceException; import com.crm.rely.backend.service.MqSendService; import com.crm.rely.backend.util.DateUtil; import com.crm.rely.backend.util.HttpUtil; import com.crm.rely.backend.util.MapUtil; import com.crm.rely.backend.model.constant.ConfigConstants; import com.crm.rely.backend.model.entity.settlement.SettlementDepositEntity; import com.crm.rely.backend.model.entity.settlement.SettlementWithdrawEntity; import com.crm.rely.backend.model.pojo.table.SettlementDepositRecordTable; import com.crm.rely.backend.model.pojo.table.SettlementWithdrawRecordTable; import com.crm.settlement.dao.mapper.SettlementDepositRecordMapper; import com.crm.settlement.dao.mapper.SettlementWithdrawRecordMapper; import com.crm.settlement.dao.repository.SettlementDepositRecordRepository; import com.crm.settlement.dao.repository.SettlementWithdrawRecordRepository; import com.crm.settlement.service.SettlementService; import com.crm.settlement.service.impl.base.BaseSettlementServiceImpl; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jsoup.Connection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.annotation.JmsListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j @Service public class SettlementServiceImpl extends BaseSettlementServiceImpl implements SettlementService { @Autowired private SettlementDepositRecordRepository settlementDepositRecordRepository; @Autowired private SettlementWithdrawRecordRepository settlementWithdrawRecordRepository; @Autowired private SettlementDepositRecordMapper settlementDepositRecordMapper; @Autowired private SettlementWithdrawRecordMapper settlementWithdrawRecordMapper; @Autowired private MqSendService mqSendService; /** * 验证入金订单 */ @Override public BaseResultDto validateDepositOrder(SettlementDepositEntity entity) { try { //验证时间 long time = DateUtil.getTimestampByZero() - entity.getRequestTime().getTime(); if ((time > (10 * 1000)) || (time < 0)) { log.error("time out ,time:" + time); throw ServiceException.exception("Time out"); } verifySign(entity.getSign(), getDepositSignString(entity), entity.getMerchantId()); SettlementDepositRecordTable recordTable = settlementDepositRecordRepository.findFirstBySerialAndChannelCode(entity.getSerial(), entity.getChannelCode()); if (recordTable == null) { return BaseResultDto.error(); } if (recordTable.getCallbackAmount().compareTo(entity.getCallbackAmount()) != 0 || !recordTable.getCallbackCurrency().equals(entity.getCallbackCurrency()) || !recordTable.getCallbackSerial().equals(entity.getCallbackSerial()) || !recordTable.getStatus().equals(entity.getStatus())){ return BaseResultDto.error(); } } catch (Exception e){ return BaseResultDto.error(); } return BaseResultDto.success(); } /** * 验证出金订单 */ @Override public BaseResultDto validateWithdrawOrder(SettlementWithdrawEntity entity) throws PayValidatedException { try { //验证时间 long time = DateUtil.getTimestampByZero() - entity.getRequestTime().getTime(); if ((time > (10 * 1000)) || (time < 0)) { log.error("time out ,time:" + time); throw ServiceException.exception("Time out"); } verifySign(entity.getSign(), getWithdrawSignString(entity), entity.getMerchantId()); SettlementWithdrawRecordTable recordTable = settlementWithdrawRecordRepository.findFirstBySerialAndChannelCode(entity.getSerial(), entity.getChannelCode()); if (recordTable == null) { return BaseResultDto.error(); } if (recordTable.getCallbackAmount().compareTo(entity.getCallbackAmount()) != 0 || !recordTable.getCallbackCurrency().equals(entity.getCallbackCurrency()) || !recordTable.getCallbackSerial().equals(entity.getCallbackSerial()) || !recordTable.getStatus().equals(entity.getStatus())){ return BaseResultDto.error(); } } catch (Exception e){ return BaseResultDto.error(); } return BaseResultDto.success(); } @Transactional(rollbackFor = Exception.class) @JmsListener(destination = ConfigConstants.DEPOSIT_CALLBACK) public void depositCallback(String content) throws Exception { log.info("DEPOSIT_CALLBACK: content:" + content); SettlementDepositRecordTable recordTable = JSON.parseObject(content, SettlementDepositRecordTable.class); SettlementDepositRecordTable table = settlementDepositRecordRepository.findFirstById(recordTable.getId()); if (table == null){ log.error("DEPOSIT_CALLBACK SettlementDepositRecordTable is null"); return; } // 判断状态 if (table.getCallbackMerchantStatus().equals(1)){ log.info("DEPOSIT_CALLBACK: callbackMerchantStatus is Success "); return; } if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){ log.info("DEPOSIT_CALLBACK: callbackMerchantCount is max retry count"); return; } String callbackUrl = table.getCallbackUrl(); Map params = new HashMap<>(); params.put("serial", table.getSerial()); params.put("channelCode", table.getChannelCode()); params.put("callbackAmount", table.getCallbackAmount().stripTrailingZeros().toPlainString()); params.put("callbackCurrency", table.getCallbackCurrency()); params.put("callbackSerial", table.getCallbackSerial()); params.put("status", String.valueOf(table.getStatus())); String callbackMerchantBody = JSON.toJSONString(params); String sign = getSign(params, table.getMerchantId()); params.put("sign", sign); String callbackContent = JSON.toJSONString(params); try { Connection.Response response = HttpUtil.post(callbackUrl, callbackContent); //打印请求日志 log.info("DEPOSIT_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body()); BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class); if (requestEntity == null || requestEntity.getCode() != 200) { table.setCallbackMerchantStatus(2); } else { table.setCallbackMerchantStatus(1); } } catch (Exception e) { log.error("DEPOSIT_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e); table.setCallbackMerchantStatus(2); } table.setCallbackMerchantBody(callbackMerchantBody); table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1); table.setCallbackMerchantTime(new Date()); settlementDepositRecordRepository.save(table); if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){ mqSendService.send(ConfigConstants.DEPOSIT_RETRY_CALLBACK, table , 3 * 60 * 1000); } } @Transactional(rollbackFor = Exception.class) @JmsListener(destination = ConfigConstants.WITHDRAW_CALLBACK) public void withdrawCallback(String content) throws Exception { log.info("WITHDRAW_CALLBACK: content:" + content); SettlementWithdrawRecordTable withdrawRecordTable = JSON.parseObject(content, SettlementWithdrawRecordTable.class); SettlementWithdrawRecordTable table = settlementWithdrawRecordRepository.findFirstById(withdrawRecordTable.getId()); if (table == null){ log.error("WITHDRAW_CALLBACK SettlementDepositRecordTable is null"); return; } // 判断状态 if (table.getCallbackMerchantStatus().equals(1)){ log.info("WITHDRAW_CALLBACK: callbackMerchantStatus is Success "); return; } if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){ log.info("WITHDRAW_CALLBACK: callbackMerchantCount is max retry count"); return; } String callbackUrl = table.getCallbackUrl(); Map params = new HashMap<>(); params.put("serial", table.getSerial()); params.put("channelCode", table.getChannelCode()); params.put("callbackAmount", table.getCallbackAmount().stripTrailingZeros().toPlainString()); params.put("callbackCurrency", table.getCallbackCurrency()); params.put("callbackSerial", table.getCallbackSerial()); params.put("status", String.valueOf(table.getStatus())); String callbackMerchantBody = JSON.toJSONString(params); String sign = getSign(params, table.getMerchantId()); params.put("sign", sign); String callbackContent = JSON.toJSONString(params); try { Connection.Response response = HttpUtil.post(callbackUrl, callbackContent); //打印请求日志 log.info("WITHDRAW_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body()); BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class); if (requestEntity == null || requestEntity.getCode() != 200) { table.setCallbackMerchantStatus(2); } else { table.setCallbackMerchantStatus(1); } } catch (Exception e) { log.error("WITHDRAW_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e); table.setCallbackMerchantStatus(2); } table.setCallbackMerchantBody(callbackMerchantBody); table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1); table.setCallbackMerchantTime(new Date()); settlementWithdrawRecordRepository.save(table); if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){ mqSendService.send(ConfigConstants.WITHDRAW_RETRY_CALLBACK, table , 3 * 60 * 1000); } } @Transactional(rollbackFor = Exception.class) @JmsListener(destination = ConfigConstants.DEPOSIT_RETRY_CALLBACK) public void depositRetryCallback(String content) throws Exception { log.info("DEPOSIT_RETRY_CALLBACK: content:" + content); SettlementDepositRecordTable table = JSON.parseObject(content, SettlementDepositRecordTable.class); if (table == null){ log.error("DEPOSIT_RETRY_CALLBACK SettlementDepositRecordTable is null"); return; } // 判断状态 if (table.getCallbackMerchantStatus().equals(1)){ log.info("DEPOSIT_RETRY_CALLBACK: callbackMerchantStatus is Success , serial:{}", table.getSerial()); return; } if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){ log.info("DEPOSIT_RETRY_CALLBACK: callbackMerchantCount is max retry count, serial:{}", table.getSerial()); return; } String callbackUrl = table.getCallbackUrl(); String body = table.getCallbackMerchantBody(); if (StringUtils.isEmpty(callbackUrl) || StringUtils.isEmpty(body)){ log.error("DEPOSIT_RETRY_CALLBACK: callbackUrl or callbackMerchantBody is null , serial:{}", table.getSerial()); return; } Map params = JSON.parseObject(body, new TypeReference>(){}); String sign = getSign(params, table.getMerchantId()); params.put("sign", sign); String callbackContent = JSON.toJSONString(params); try { Connection.Response response = HttpUtil.post(callbackUrl, callbackContent); //打印请求日志 log.info("DEPOSIT_RETRY_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body()); BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class); if (requestEntity == null || requestEntity.getCode() != 200) { table.setCallbackMerchantStatus(2); } else { table.setCallbackMerchantStatus(1); } } catch (Exception e) { log.error("DEPOSIT_RETRY_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e); table.setCallbackMerchantStatus(2); } table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1); settlementDepositRecordRepository.save(table); if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){ mqSendService.send(ConfigConstants.DEPOSIT_RETRY_CALLBACK, table , 3 * 60 * 1000); } } @Transactional(rollbackFor = Exception.class) @JmsListener(destination = ConfigConstants.WITHDRAW_RETRY_CALLBACK) public void withdrawRetryCallback(String content) throws Exception { log.info("WITHDRAW_RETRY_CALLBACK: content:" + content); SettlementWithdrawRecordTable table = JSON.parseObject(content, SettlementWithdrawRecordTable.class); if (table == null){ log.error("WITHDRAW_RETRY_CALLBACK SettlementDepositRecordTable is null"); return; } // 判断状态 if (table.getCallbackMerchantStatus().equals(1)){ log.info("WITHDRAW_RETRY_CALLBACK: callbackMerchantStatus is Success "); return; } if (table.getCallbackMerchantCount() >= table.getCallbackMerchantMaxRetryCount()){ log.info("WITHDRAW_RETRY_CALLBACK: callbackMerchantCount is max retry count"); return; } String callbackUrl = table.getCallbackUrl(); String body = table.getCallbackMerchantBody(); if (StringUtils.isEmpty(callbackUrl) || StringUtils.isEmpty(body)){ log.error("WITHDRAW_RETRY_CALLBACK: callbackUrl or callbackMerchantBody is null , serial:{}", table.getSerial()); return; } Map params = JSON.parseObject(body, new TypeReference>(){}); String sign = getSign(params, table.getMerchantId()); params.put("sign", sign); String callbackContent = JSON.toJSONString(params); try { Connection.Response response = HttpUtil.post(callbackUrl, callbackContent); //打印请求日志 log.info("WITHDRAW_CALLBACK Request URL: {},data:{},response body:{}", callbackUrl, callbackContent, response.body()); BaseResultDto requestEntity = JSON.parseObject(response.body(), BaseResultDto.class); if (requestEntity == null || requestEntity.getCode() != 200) { table.setCallbackMerchantStatus(2); } else { table.setCallbackMerchantStatus(1); } } catch (Exception e) { log.error("WITHDRAW_CALLBACK, Request URL: {}, data:{}", callbackUrl, callbackContent, e); table.setCallbackMerchantStatus(2); } table.setCallbackMerchantCount(table.getCallbackMerchantCount() + 1); settlementWithdrawRecordRepository.save(table); if (table.getCallbackMerchantStatus().equals(2) && table.getCallbackMerchantCount() < table.getCallbackMerchantMaxRetryCount()){ mqSendService.send(ConfigConstants.WITHDRAW_RETRY_CALLBACK, table , 3 * 60 * 1000); } } /** * 订单通知失败重新发送通知 * 查询入金和出金 回调失败的 未达到最大回调次数的 超过一天的数据 分别发送回调 */ @Scheduled(cron = "0 23 0 * * ?") @Transactional(rollbackFor = Exception.class) @Override public void scheduleCallback() throws ServiceException { log.info("scheduleCallback start"); List depositRecordTables = settlementDepositRecordMapper.getAllCallback(); List withdrawRecordTables = settlementWithdrawRecordMapper.getAllCallback(); if (!CollectionUtils.isEmpty(depositRecordTables)){ log.info("scheduleCallback: depositRecordTables size:{}", depositRecordTables.size()); for (SettlementDepositRecordTable table : depositRecordTables){ mqSendService.send(ConfigConstants.DEPOSIT_RETRY_CALLBACK, table); } } if (!CollectionUtils.isEmpty(withdrawRecordTables)){ log.info("scheduleCallback: withdrawRecordTables size:{}", withdrawRecordTables.size()); for (SettlementWithdrawRecordTable table : withdrawRecordTables){ mqSendService.send(ConfigConstants.WITHDRAW_RETRY_CALLBACK, table); } } log.info("scheduleCallback end"); } private String getDepositSignString(SettlementDepositEntity entity) { Map map = new HashMap<>(); map.put("merchantId", entity.getMerchantId()); map.put("channelCode", entity.getChannelCode()); map.put("serial", entity.getSerial()); map.put("callbackAmount", entity.getCallbackAmount().stripTrailingZeros().toPlainString()); map.put("callbackCurrency", entity.getCallbackCurrency()); map.put("callbackSerial", entity.getCallbackSerial()); map.put("status", String.valueOf(entity.getStatus())); map.put("requestTime", DateUtil.formatTime(entity.getRequestTime())); return MapUtil.getStringSortByKey(map); } private String getWithdrawSignString(SettlementWithdrawEntity entity) { Map map = new HashMap<>(); map.put("merchantId", entity.getMerchantId()); map.put("channelCode", entity.getChannelCode()); map.put("serial", entity.getSerial()); map.put("callbackAmount", entity.getCallbackAmount().stripTrailingZeros().toPlainString()); map.put("callbackCurrency", entity.getCallbackCurrency()); map.put("callbackSerial", entity.getCallbackSerial()); map.put("status", String.valueOf(entity.getStatus())); map.put("requestTime", DateUtil.formatTime(entity.getRequestTime())); return MapUtil.getStringSortByKey(map); } }