package com.lunhan.water.host.controller.notify;
|
|
import com.google.gson.Gson;
|
import com.lunhan.water.common.ConstantFactory;
|
import com.lunhan.water.common.ExecutedResult;
|
import com.lunhan.water.common.config.SysConfig;
|
import com.lunhan.water.common.enums.ELogger;
|
import com.lunhan.water.common.util.LocalDateTimeUtil;
|
import com.lunhan.water.common.util.LoggerUtil;
|
import com.lunhan.water.common.util.SerializeUtil;
|
import com.lunhan.water.common.util.StringUtil;
|
import com.lunhan.water.common.wechat.WechatPayV3Util;
|
import com.lunhan.water.entity.dto.pay.WeiXinPayNotifyDto;
|
import com.lunhan.water.entity.dto.pay.WeiXinRefundNotifyDto;
|
import com.lunhan.water.entity.enums.EPaymentChannel;
|
import com.lunhan.water.entity.response.pay.ResWeiXinPayNotify;
|
import com.lunhan.water.host.api.NonLogin;
|
import com.lunhan.water.service.PaymentServices;
|
import com.lunhan.water.service.ThirdNotifyService;
|
import org.slf4j.Logger;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.web.bind.annotation.*;
|
|
import javax.annotation.Resource;
|
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletResponse;
|
import java.math.BigDecimal;
|
import java.nio.charset.StandardCharsets;
|
import java.time.LocalDateTime;
|
import java.util.HashMap;
|
import java.util.Iterator;
|
import java.util.Map;
|
import java.util.Objects;
|
|
/**
|
* 10010.异步通知相关接口
|
*
|
* @order 10010
|
*/
|
@NonLogin
|
@RestController
|
@RequestMapping("notify")
|
public class NotifyController {
|
private Logger logger = LoggerUtil.get(ELogger.PAY_SERVICE);
|
@Autowired
|
private PaymentServices paymentService;
|
@Autowired
|
private ThirdNotifyService thirdNotifyService;
|
|
/**
|
* 微信支付结果通知
|
*
|
* @param reqBody 请求参数列表
|
*/
|
@PostMapping(value = "pay/{channel}/{payWay}/{businessType}")
|
public Object pay(@RequestBody Map<String, Object> reqBody, @PathVariable Integer channel, @PathVariable Integer payWay, @PathVariable Integer businessType) {
|
String action = "pay";
|
|
if (Objects.isNull(reqBody) || reqBody.isEmpty()) {
|
logger.error(action + ", body cant't be null.");
|
return "body cant't be null.";
|
}
|
String body = SerializeUtil.toJson(reqBody);
|
logger.info(action + "-notify, channel: " + channel + "; payWay: " + payWay + "; body: " + body);
|
|
EPaymentChannel findChannel = EPaymentChannel.getByValue(channel);
|
if (Objects.isNull(findChannel)) {
|
return "支付渠道不支持." + channel;
|
}
|
Object result;
|
switch (findChannel) {
|
case WE_CHAT:
|
result = this.doWeiXinPayNotify(reqBody, body, businessType);
|
break;
|
|
case ALI_PAY:
|
// TODO
|
result = "支付宝支付待实现";
|
break;
|
|
default:
|
result = "支付渠道不支持." + channel;
|
break;
|
}
|
return result;
|
}
|
|
private ResWeiXinPayNotify doWeiXinPayNotify(Map<String, Object> reqBody, String body, Integer businessType) {
|
String action = "doWeiXinPayNotify";
|
ResWeiXinPayNotify result = new ResWeiXinPayNotify("FAILED", "unknown error.");
|
|
ExecutedResult<Long> saveWinXinNotify = thirdNotifyService.saveWinXinPayNotify(EPaymentChannel.WE_CHAT, "PAY", body, businessType);
|
if (saveWinXinNotify.isFailed()) {
|
if (Objects.equals(saveWinXinNotify.getMsgCode(), ConstantFactory.MCODE_TOKENERROR)) {
|
result.setCode("SUCCESS");
|
result.setMessage("SUCCESS");
|
} else {
|
result.setMessage(saveWinXinNotify.getMsg());
|
}
|
return result;
|
}
|
Long notifyId = saveWinXinNotify.getData();
|
|
// 处理微信支付结果通知
|
try {
|
if (reqBody.containsKey("event_type") && "TRANSACTION.SUCCESS".equals(reqBody.get("event_type").toString())) {
|
this.doWeiXinPayResult(SerializeUtil.toJson(reqBody.get("resource")), notifyId);
|
}
|
} catch (Exception e) {
|
logger.error(action, e);
|
result.setMessage(e.getMessage());
|
return result;
|
}
|
result.setCode("SUCCESS");
|
result.setMessage("SUCCESS");
|
return result;
|
}
|
|
/**
|
* 更新微信支付结果
|
*
|
* @param body 通知报文
|
* @param notifyId 通知报文记录id
|
*/
|
private void doWeiXinPayResult(String body, Long notifyId) throws Exception {
|
String actionStr = "weiXinPay, ";
|
Map<String, Object> data = new Gson().fromJson(body, Map.class);
|
if (data.containsKey("algorithm") && "AEAD_AES_256_GCM".equals(data.get("algorithm").toString())) {
|
String signParams = data.get("ciphertext").toString();
|
|
String paramsStr = "";
|
|
byte[] bufferKey = SysConfig.weiXinPay.getMerchantKey().getBytes(StandardCharsets.UTF_8);
|
byte[] bufferAssociatedData = data.get("associated_data").toString().getBytes(StandardCharsets.UTF_8);
|
byte[] bufferNonce = data.get("nonce").toString().getBytes(StandardCharsets.UTF_8);
|
paramsStr = WechatPayV3Util.decryptToString(bufferAssociatedData, bufferNonce, signParams, bufferKey);
|
logger.info(actionStr + "支付结果解密: " + paramsStr);
|
|
WeiXinPayNotifyDto payInfo = SerializeUtil.toObject(paramsStr, WeiXinPayNotifyDto.class);
|
|
// 更新通知报文业务编号
|
thirdNotifyService.updateBusinessCode(notifyId, payInfo.getOut_trade_no());
|
|
if ("SUCCESS".equalsIgnoreCase(payInfo.getTrade_state())) {
|
LocalDateTime payTime = null;
|
if (StringUtil.isNotNullOrEmpty(payInfo.getSuccess_time())) {
|
payTime = LocalDateTimeUtil.getDateTime(payInfo.getSuccess_time(), "yyyy-MM-d'T'HH:mm:ssXXX");
|
}
|
Integer paidAmount = payInfo.getAmount().getPayer_total();
|
|
ExecutedResult<String> doPaidSuccess = paymentService.doPaidSuccess(
|
payInfo.getOut_trade_no(),
|
payInfo.getTransaction_id(),
|
new BigDecimal(paidAmount.toString()).divide(new BigDecimal(ConstantFactory.NUM100.toString())), // 微信支付金额乘了100
|
payTime
|
);
|
logger.info(actionStr + "支付成功更新支付结果: " + SerializeUtil.toJson(doPaidSuccess));
|
} else if ("PAYERROR".equalsIgnoreCase(payInfo.getTrade_state())) {
|
ExecutedResult<String> doPaidFailed = paymentService.doPaidFailed(
|
payInfo.getOut_trade_no(),
|
payInfo.getTransaction_id(),
|
payInfo.getTrade_state_desc()
|
);
|
logger.info(actionStr + "支付失败,更新支付结果: " + SerializeUtil.toJson(doPaidFailed));
|
}
|
} else {
|
logger.error(actionStr + "报文加密方式未能识别!" + body);
|
}
|
}
|
|
|
/**
|
* 微信退款结果通知
|
*
|
* @param reqBody 请求参数列表
|
*/
|
@PostMapping(value = "refund/{channel}/{payWay}/{businessType}")
|
public Object refund(@RequestBody Map<String, Object> reqBody, @PathVariable Integer channel, @PathVariable Integer payWay, @PathVariable Integer businessType) {
|
String action = "refund";
|
|
if (Objects.isNull(reqBody)) {
|
logger.error(action + ", body cant't be null.");
|
return "请求正文不能为空.";
|
}
|
String body = SerializeUtil.toJson(reqBody);
|
logger.info(action + "-notify, channel: " + channel + "; payWay: " + payWay + "; body: " + body);
|
|
EPaymentChannel findChannel = EPaymentChannel.getByValue(channel);
|
if (Objects.isNull(findChannel)) {
|
return "支付渠道不支持." + channel;
|
}
|
Object result;
|
switch (findChannel) {
|
case WE_CHAT:
|
result = this.doWeiXinRefundNotify(reqBody, body, businessType);
|
break;
|
|
case ALI_PAY:
|
// TODO
|
result = "支付宝支付待实现";
|
break;
|
|
default:
|
result = "支付渠道不支持";
|
break;
|
}
|
return result;
|
}
|
|
private Object doWeiXinRefundNotify(Map<String, Object> reqBody, String body, Integer businessType) {
|
String action = "doWeiXinRefundNotify";
|
ResWeiXinPayNotify result = new ResWeiXinPayNotify("FAILED", "unknown error.");
|
|
ExecutedResult<Long> saveWinXinNotify = thirdNotifyService.saveWinXinPayNotify(EPaymentChannel.WE_CHAT, "REFUND", body, businessType);
|
if (saveWinXinNotify.isFailed()) {
|
if (Objects.equals(saveWinXinNotify.getMsgCode(), ConstantFactory.MCODE_TOKENERROR)) {
|
result.setCode("SUCCESS");
|
result.setMessage("SUCCESS");
|
} else {
|
result.setMessage(saveWinXinNotify.getMsg());
|
}
|
return result;
|
}
|
Long notifyId = saveWinXinNotify.getData();
|
|
// 处理微信支付结果通知
|
try {
|
if (reqBody.containsKey("event_type") && "REFUND.SUCCESS".equals(reqBody.get("event_type").toString())) {
|
this.doWeiXinRefundResult(SerializeUtil.toJson(reqBody.get("resource")), notifyId);
|
}
|
} catch (Exception e) {
|
logger.error(action, e);
|
result.setMessage(e.getMessage());
|
return result;
|
}
|
result.setCode("SUCCESS");
|
result.setMessage("SUCCESS");
|
return result;
|
}
|
|
/**
|
* 更新微信退款结果
|
*
|
* @param body 通知报文
|
* @param notifyId 通知报文记录id
|
*/
|
private void doWeiXinRefundResult(String body, Long notifyId) throws Exception {
|
String actionStr = "weiXinRefund, ";
|
Map<String, Object> data = new Gson().fromJson(body, Map.class);
|
if (data.containsKey("algorithm") && "AEAD_AES_256_GCM".equals(data.get("algorithm").toString())) {
|
String signParams = data.get("ciphertext").toString();
|
|
|
String paramsStr = "";
|
|
byte[] bufferKey = SysConfig.weiXinPay.getMerchantKey().getBytes(StandardCharsets.UTF_8);
|
byte[] bufferAssociatedData = data.get("associated_data").toString().getBytes(StandardCharsets.UTF_8);
|
byte[] bufferNonce = data.get("nonce").toString().getBytes(StandardCharsets.UTF_8);
|
paramsStr = WechatPayV3Util.decryptToString(bufferAssociatedData, bufferNonce, signParams, bufferKey);
|
logger.info(actionStr + "退款结果解密: " + paramsStr);
|
|
WeiXinRefundNotifyDto refundInfo = SerializeUtil.toObject(paramsStr, WeiXinRefundNotifyDto.class);
|
|
// 更新通知报文业务编号
|
thirdNotifyService.updateBusinessCode(notifyId, refundInfo.getOut_trade_no());
|
|
if ("SUCCESS".equalsIgnoreCase(refundInfo.getRefund_status())) {
|
LocalDateTime refundTime = null;
|
if (StringUtil.isNotNullOrEmpty(refundInfo.getSuccess_time())) {
|
refundTime = LocalDateTimeUtil.getDateTime(refundInfo.getSuccess_time(), "yyyy-MM-d'T'HH:mm:ssXXX");
|
}
|
Integer paidAmount = refundInfo.getAmount().getPayer_refund();
|
|
ExecutedResult<String> doRefundSuccess = paymentService.doRefundSuccess(
|
refundInfo.getOut_trade_no(),
|
refundInfo.getTransaction_id(),
|
new BigDecimal(paidAmount.toString()).divide(new BigDecimal(ConstantFactory.NUM100.toString())), // 微信支付金额乘了100
|
refundTime
|
);
|
logger.info(actionStr + "退款成功更新支付结果: " + SerializeUtil.toJson(doRefundSuccess));
|
} else {
|
String errorMsg = "refund_status: ABNORMAL. 退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款";
|
if ("CLOSED".equalsIgnoreCase(refundInfo.getRefund_status())) {
|
errorMsg = "refund_status: CLOSED. 退款关闭";
|
}
|
ExecutedResult<String> doRefundFailed = paymentService.doRefundFailed(
|
refundInfo.getOut_trade_no(),
|
refundInfo.getTransaction_id(),
|
errorMsg
|
);
|
logger.info(actionStr + "退款失败,更新退款结果: " + SerializeUtil.toJson(doRefundFailed));
|
}
|
} else {
|
logger.error(actionStr + "报文加密方式未能识别!" + body);
|
}
|
}
|
}
|