package com.mzl.flower.service.payment;
|
|
import com.alibaba.fastjson.JSON;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.mzl.flower.config.PyamentV3Configurer;
|
import com.mzl.flower.config.exception.ValidationException;
|
import com.mzl.flower.config.security.SecurityUtils;
|
import com.mzl.flower.constant.Constants;
|
import com.mzl.flower.dto.request.payment.TransferDetailReqDTO;
|
import com.mzl.flower.dto.request.payment.TransferReqDTO;
|
import com.mzl.flower.dto.request.payment.UserPaymentDTO;
|
import com.mzl.flower.entity.payment.*;
|
import com.mzl.flower.entity.system.UserWechat;
|
import com.mzl.flower.mapper.flower.FlowerMapper;
|
import com.mzl.flower.mapper.payment.*;
|
import com.mzl.flower.mapper.system.UserWechatMapper;
|
import com.mzl.flower.service.BaseService;
|
import com.mzl.flower.service.flower.FlowerService;
|
import com.mzl.flower.service.point.PointGoodsService;
|
import com.mzl.flower.utils.UUIDGenerator;
|
import com.wechat.pay.java.core.notification.NotificationParser;
|
import com.wechat.pay.java.core.notification.RequestParam;
|
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
|
import com.wechat.pay.java.service.payments.jsapi.model.*;
|
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
|
import com.wechat.pay.java.service.payments.model.Transaction;
|
import com.wechat.pay.java.service.payments.model.TransactionAmount;
|
import com.wechat.pay.java.service.refund.RefundService;
|
import com.wechat.pay.java.service.refund.model.*;
|
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
|
import com.wechat.pay.java.service.transferbatch.model.*;
|
import io.micrometer.core.instrument.util.StringUtils;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.http.HttpStatus;
|
import org.springframework.http.ResponseEntity;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import javax.servlet.ServletInputStream;
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.BufferedReader;
|
import java.io.InputStreamReader;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDateTime;
|
import java.time.ZoneId;
|
import java.time.ZonedDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
import com.wechat.pay.java.core.http.Constant;
|
|
@Service
|
@Transactional
|
@Slf4j
|
public class UserPaymentV3Service extends BaseService {
|
|
@Autowired
|
private UserPaymentMapper userPaymentMapper;
|
|
@Autowired
|
private RedisLockService lockService;
|
|
@Autowired
|
private UserWechatMapper wechatMapper;
|
|
@Autowired
|
private OrderMapper orderMapper;
|
|
@Autowired
|
private OrderItemMapper orderItemMapper;
|
|
@Autowired
|
private FlowerMapper flowerMapper;
|
|
@Autowired
|
private FlowerService flowerService;
|
|
@Autowired
|
private DeliveryOrderService deliveryOrderService;
|
|
@Autowired
|
private JsapiServiceExtension jsapiService;
|
|
@Autowired
|
private NotificationParser notificationParser;
|
|
@Autowired
|
private RefundService refundService;
|
|
@Autowired
|
private TransferBatchService transferBatchService;
|
|
@Autowired
|
private TransferMapper transferMapper;
|
|
@Autowired
|
private TransferDetailMapper transferDetailMapper;
|
|
@Autowired
|
private OrderRefundMapper orderRefundMapper;
|
|
@Autowired
|
private OrderPointGoodsMapper orderPointGoodsMapper;
|
|
@Autowired
|
private PointGoodsService pointGoodsService;
|
|
/**
|
* 微信预支付
|
*
|
* @param order
|
* @return
|
*/
|
public Map wxPrepay(Order order){
|
String openId = order.getPayOpenid();
|
|
String userId = SecurityUtils.getUserId();
|
// UserWechat wechat = wechatMapper.selectOne(new QueryWrapper<UserWechat>().eq("user_id", userId));
|
// if (wechat != null) {
|
// openId = wechat.getOpenId();
|
// }
|
|
if(StringUtils.isEmpty(openId)){
|
throw new ValidationException("openId不存在");
|
}
|
|
UserPayment up = prepareUserPayment(userId, order);
|
log.info("UserPayment: " + toJSONString(up));
|
|
PrepayRequest request = new PrepayRequest();
|
Amount amount = new Amount();
|
amount.setTotal(prepareAmount(up.getPaymentAmount()));
|
request.setAmount(amount);
|
request.setAppid(PyamentV3Configurer.customer_app_id);
|
request.setMchid(PyamentV3Configurer.merchantId);
|
request.setDescription(order.getOrderNo());
|
request.setNotifyUrl(PyamentV3Configurer.notify_url_pay);
|
request.setOutTradeNo(up.getOrderId());
|
request.setTimeExpire(getTimeExpire());
|
Payer payer = new Payer();
|
payer.setOpenid(openId);
|
request.setPayer(payer);
|
|
// 调用接口
|
PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(request);
|
|
up.setPrepayResponse(toJSONString(response));
|
|
userPaymentMapper.insert(up);
|
|
Map<String, Object> map = new HashMap<>();
|
map.put("appId", response.getAppId());
|
map.put("timeStamp", response.getTimeStamp());
|
map.put("nonceStr", response.getNonceStr());
|
map.put("package", response.getPackageVal());
|
map.put("signType", response.getSignType());
|
map.put("paySign", response.getPaySign());
|
|
return map;
|
}
|
|
private static String getTimeExpire(){//设置微信订单5分钟过期
|
// 获取当前日期和时间
|
ZonedDateTime zonedDateTime = ZonedDateTime.now();
|
|
// 创建一个DateTimeFormatter
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
|
|
// 使用formatter格式化ZonedDateTime
|
String formattedDateTime = zonedDateTime.plusMinutes(5).format(formatter);
|
|
return formattedDateTime; // 格式化日期
|
}
|
|
private Integer prepareAmount(BigDecimal amount){
|
return amount.multiply(new BigDecimal(100)).intValue();
|
}
|
|
private UserPayment prepareUserPayment(String userId, Order order){
|
UserPayment up = new UserPayment();
|
up.setId(UUIDGenerator.getUUID());
|
up.setUserId(userId);
|
up.setOrderId(order.getId());
|
up.setPaymentAmount(order.getTotalAmount());
|
up.setPaymentTime(LocalDateTime.now());
|
up.create(userId);
|
|
return up;
|
}
|
|
public ResponseEntity<?> handlePayCallback(HttpServletRequest request) {
|
try {
|
ServletInputStream inputStream = request.getInputStream();
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
// 读取请求体
|
StringBuilder sb = new StringBuilder();
|
String line;
|
while ((line = bufferedReader.readLine()) != null) {
|
sb.append(line);
|
}
|
String notify = sb.toString();
|
log.info("微信支付通知: {}", notify);
|
RequestParam requestParam = buildByHeaderRequestParam(request, notify);
|
log.info("微信支付通知解析请求: {}", JSON.toJSONString(requestParam));
|
// 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败,SDK 会抛出 ValidationException。
|
log.info("支付通知回调:验签、解密并转换成 Transaction对象:-------");
|
Transaction transaction = null;
|
try {
|
/**
|
* 常用的通知回调调对象类型有:
|
* 支付 Transaction
|
* 退款 RefundNotification
|
* 若 SDK 暂不支持的类型,请使用 Map.class,嵌套的 Json 对象将被转换成 LinkedTreeMap
|
*/
|
transaction = notificationParser.parse(requestParam, Transaction.class);
|
}catch (ValidationException e) {
|
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
log.error("sign verification failed", e);
|
return new ResponseEntity<>("", HttpStatus.UNAUTHORIZED);
|
}
|
log.info("Transaction:===>>>>支付CallBack状态" + transaction.getTradeState());
|
|
String originalXml = toJSONString(transaction);//回调请求内容
|
log.info("transaction: " + originalXml);
|
String outTradeNo = transaction.getOutTradeNo();//对外贸易编号
|
String transactionId = transaction.getTransactionId();//事务处理id
|
String orderId = outTradeNo;
|
TransactionAmount amount = transaction.getAmount();
|
Integer paymentAmountCallback = amount.getTotal();//实际支付金额 单位:分
|
log.info("======paymentAmountCallback: " + paymentAmountCallback);
|
|
Transaction.TradeStateEnum tradeState = transaction.getTradeState();//支付状态
|
String status = Constants.PAYMENT_STATUS.FAILED.name();
|
//交易状态
|
if (Transaction.TradeStateEnum.SUCCESS.equals(tradeState)){
|
status = Constants.PAYMENT_STATUS.SUCCESS.name();
|
}
|
|
UserPaymentDTO dto = new UserPaymentDTO();
|
dto.setOrderId(orderId);
|
dto.setTransactionId(transactionId);
|
dto.setOutTradeNo(outTradeNo);
|
dto.setOriginalXml(originalXml);
|
dto.setPaymentAmountCallback(paymentAmountCallback + "");
|
dto.setStatus(status);
|
|
saveCallbackInfo(dto, Constants.ORDER_STATUS_BACKEND.PAYMENT.name());
|
|
} catch (Exception e) {
|
log.error("解析付款通知出错:{}", e.getMessage(), e);
|
return new ResponseEntity<>("", HttpStatus.INTERNAL_SERVER_ERROR);
|
}
|
|
return new ResponseEntity<>("", HttpStatus.OK);
|
}
|
|
private Map prepareReturn(String code, String message){
|
Map m = new HashMap();
|
m.put("code", code);
|
m.put("message", message);
|
|
return m;
|
}
|
|
public RequestParam buildByHeaderRequestParam(HttpServletRequest request, String notify) {
|
log.info("-------------------WxPay---------GetHeader--------------------BEGIN");
|
// HTTP 头 Wechatpay-Timestamp。签名中的时间戳。
|
String timestamp = request.getHeader(Constant.WECHAT_PAY_TIMESTAMP);
|
// HTTP 头 Wechatpay-Nonce。签名中的随机数。
|
String nonce = request.getHeader(Constant.WECHAT_PAY_NONCE);
|
// HTTP 头 Wechatpay-Signature-Type。签名类型。
|
String signType = request.getHeader("Wechatpay-Signature-Type");
|
//HTTP 头 Wechatpay-Serial。微信支付平台证书的序列号,验签必须使用序列号对应的微信支付平台证书。
|
String serialNo = request.getHeader(Constant.WECHAT_PAY_SERIAL);
|
// HTTP 头 Wechatpay-Signature。应答的微信支付签名。
|
String signature = request.getHeader(Constant.WECHAT_PAY_SIGNATURE);
|
log.info("应答的微信支付签名: {}", signature);
|
if(StringUtils.isNotBlank(signature) && signature.contains("WECHATPAY/SIGNTEST/")){
|
//应对签名探测流量
|
//为了确保商户系统的安全,微信支付会在极少数应答或通知回调中生成错误签名,以探测商户系统是否正确地验证了签名。
|
log.error("微信对签名探测流量验证");
|
}
|
log.info("-------------------WxPay---------GetHeader--------------------ENDING");
|
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
|
RequestParam requestParam = new RequestParam.Builder()
|
.serialNumber(serialNo)
|
.nonce(nonce)
|
.signature(signature)
|
.timestamp(timestamp)
|
.signType(signType)
|
.body(notify)
|
.build();
|
return requestParam;
|
}
|
|
public void saveCallbackInfo(UserPaymentDTO dto, String orderStatus){
|
String orderId = dto.getOrderId();
|
boolean lock = lockService.getObjectLock(RedisLockService.LOCK_KEY_PAYMENT_NOTIFY_, orderId);
|
if(!lock){
|
return;
|
}
|
|
try {
|
UserPayment up = userPaymentMapper.selectOne(
|
new QueryWrapper<UserPayment>().eq("order_id", orderId));
|
BeanUtils.copyProperties(dto, up);
|
|
up.setPaymentTimeCallback(LocalDateTime.now());
|
userPaymentMapper.updateById(up);
|
|
Order order = orderMapper.selectById(orderId);
|
if(Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(orderStatus)) {
|
order.setPaymentTrId(dto.getTransactionId());
|
if (up.getPaymentAmountCallback() != null) {
|
order.setPaymentAmount(new BigDecimal(up.getPaymentAmountCallback()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));//转换成单位元
|
} else {
|
order.setPaymentAmount(order.getTotalAmount());
|
}
|
order.setPaymentTime(up.getPaymentTimeCallback());
|
order.setStatus(Constants.ORDER_STATUS.SEND.name());
|
} else if (Constants.ORDER_STATUS_BACKEND.CANCEL.name().equals(orderStatus)){
|
order.setStatus(Constants.ORDER_STATUS.CANCEL.name());
|
}
|
order.setStatusBackend(orderStatus);
|
orderMapper.updateById(order);
|
|
if(Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(orderStatus)) {
|
postPayment(order);
|
} else if (Constants.ORDER_STATUS_BACKEND.CANCEL.name().equals(orderStatus)){
|
releasePrepayLock(order);
|
}
|
} catch (Exception e) {
|
log.error(e.getMessage(), e);
|
} finally {
|
lockService.releaseObjectLock(RedisLockService.LOCK_KEY_PAYMENT_NOTIFY_, orderId);
|
}
|
}
|
|
private void postPayment(Order order){
|
log.info("回调后处理订单信息:" + toJSONString(order));
|
//创建配送单
|
deliveryOrderService.createDeliveryOrder(order);
|
flowerService.updateFlowerSales(order);
|
}
|
|
public Transaction queryOrder(String outTradeNo){
|
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
|
request.setOutTradeNo(outTradeNo);
|
request.setMchid(PyamentV3Configurer.merchantId);
|
// 调用接口
|
return jsapiService.queryOrderByOutTradeNo(request);
|
}
|
|
public boolean checkOrderStatus(String outTradeNo){
|
return checkOrderStatus(outTradeNo, false);
|
}
|
|
public boolean checkOrderStatus(String outTradeNo, boolean isPayAgain){
|
UserPayment up = userPaymentMapper.selectOne(
|
new QueryWrapper<UserPayment>().eq("order_id", outTradeNo));
|
String s = up.getStatus();
|
if(StringUtils.isNotEmpty(s)){
|
return true;
|
}
|
|
Transaction transaction = queryOrder(outTradeNo);
|
/*
|
交易成功判断条件: return_code、result_code和trade_state都为SUCCESS
|
SUCCESS--支付成功
|
REFUND--转入退款
|
NOTPAY--未支付
|
CLOSED--已关闭
|
REVOKED--已撤销(刷卡支付)
|
USERPAYING--用户支付中
|
PAYERROR--支付失败(其他原因,如银行返回失败)
|
ACCEPT--已接收,等待扣款
|
*/
|
String originalXml = toJSONString(transaction);//回调请求内容
|
log.info("message: " + originalXml);
|
Transaction.TradeStateEnum tradeState = transaction.getTradeState();
|
String transactionId = transaction.getTransactionId();//事务处理id
|
String orderId = outTradeNo;
|
|
if(Transaction.TradeStateEnum.SUCCESS.equals(tradeState)
|
|| Transaction.TradeStateEnum.CLOSED.equals(tradeState)) {
|
UserPaymentDTO dto = new UserPaymentDTO();
|
dto.setOrderId(orderId);
|
dto.setTransactionId(transactionId);
|
dto.setOutTradeNo(outTradeNo);
|
dto.setOriginalXml(originalXml);
|
TransactionAmount amount = transaction.getAmount();
|
if(amount != null) {
|
Integer paymentAmountCallback = amount.getTotal();//实际支付金额 单位:分
|
log.info("======paymentAmountCallback: " + paymentAmountCallback);
|
dto.setPaymentAmountCallback(paymentAmountCallback + "");
|
}
|
dto.setStatus(tradeState.name());
|
|
String orderStatus = Transaction.TradeStateEnum.CLOSED.equals(tradeState)
|
? Constants.ORDER_STATUS_BACKEND.CANCEL.name()
|
: Constants.ORDER_STATUS_BACKEND.PAYMENT.name();
|
|
saveCallbackInfo(dto, orderStatus);
|
|
return true;
|
}
|
|
if(isPayAgain){
|
return !Transaction.TradeStateEnum.NOTPAY.equals(tradeState);
|
}
|
|
return false;
|
}
|
|
public void cancelOrder(String orderId){
|
UserPayment up = userPaymentMapper.selectOne(
|
new QueryWrapper<UserPayment>().eq("order_id", orderId));
|
if(StringUtils.isNotEmpty(up.getStatus())){
|
throw new ValidationException("订单不可取消");
|
}
|
|
CloseOrderRequest request = new CloseOrderRequest();
|
request.setOutTradeNo(orderId);
|
request.setMchid(PyamentV3Configurer.merchantId);
|
// 调用接口
|
jsapiService.closeOrder(request);
|
|
Order order = orderMapper.selectById(orderId);
|
order.setStatus(Constants.ORDER_STATUS.CANCEL.name());
|
order.setStatusBackend(Constants.ORDER_STATUS_BACKEND.CANCEL.name());
|
order.setCancelTime(LocalDateTime.now());
|
order.update(SecurityUtils.getUserId());
|
orderMapper.updateById(order);
|
|
up.setStatus(Constants.PAYMENT_STATUS.CLOSED.name());
|
up.update(SecurityUtils.getUserId());
|
userPaymentMapper.updateById(up);
|
|
releasePrepayLock(order);
|
}
|
|
private void releasePrepayLock(Order order){
|
log.info("恢复库存 积分商品兑换券 优惠券: " + order);
|
revertFlowerStock(order.getId());
|
|
//恢复积分商品兑换券
|
revertPointGoodsRecord(order.getId());
|
|
//TODO 恢复优惠券
|
}
|
|
private void revertPointGoodsRecord(String orderId){
|
List<OrderPointGoods> ls = orderPointGoodsMapper.selectList(new QueryWrapper<OrderPointGoods>()
|
.eq("order_id", orderId));
|
if(ls != null && ls.size() > 0){
|
for(OrderPointGoods pg : ls){
|
pointGoodsService.revertExchangeGoods(pg.getGoodsRecordId());
|
}
|
}
|
}
|
|
public Map payAgain(String orderId){
|
UserPayment up = userPaymentMapper.selectOne(
|
new QueryWrapper<UserPayment>().eq("order_id", orderId));
|
if(StringUtils.isNotEmpty(up.getStatus())){
|
throw new ValidationException("订单不可再支付");
|
}
|
|
PrepayWithRequestPaymentResponse response = parseObject(up.getPrepayResponse()
|
, PrepayWithRequestPaymentResponse.class);
|
|
Map<String, Object> map = new HashMap<>();
|
map.put("appId", response.getAppId());
|
map.put("timeStamp", response.getTimeStamp());
|
map.put("nonceStr", response.getNonceStr());
|
map.put("package", response.getPackageVal());
|
map.put("signType", response.getSignType());
|
map.put("paySign", response.getPaySign());
|
|
return map;
|
}
|
|
public synchronized void revertFlowerStock(String orderId){
|
List<OrderItem> ls = orderItemMapper.selectList(new QueryWrapper<OrderItem>()
|
.eq("order_id", orderId));
|
for(OrderItem c : ls){
|
flowerMapper.addFlowerStock(c.getFlowerId(), c.getNum());
|
}
|
}
|
|
/**
|
* 退款
|
*
|
* @param orderId
|
*/
|
public void refundOrder(String orderId){
|
Order o = orderMapper.selectById(orderId);
|
/*String status = o.getStatusBackend();
|
if(!Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(status)){
|
throw new ValidationException("未支付订单不可申请退款");
|
}*/
|
|
CreateRequest request = new CreateRequest();
|
request.setOutTradeNo(orderId);
|
request.setOutRefundNo(UUIDGenerator.getUUID());
|
request.setTransactionId(o.getPaymentTrId());
|
request.setNotifyUrl(PyamentV3Configurer.notify_url_refund);
|
|
AmountReq amount = new AmountReq();
|
int oa = prepareAmount(o.getPaymentAmount());
|
amount.setTotal((long)oa);
|
amount.setRefund((long)oa);
|
amount.setCurrency("CNY");
|
request.setAmount(amount);
|
// 调用接口
|
refundService.create(request);
|
|
o.setRefundAmount(o.getPaymentAmount());
|
o.setRefundNo(request.getOutRefundNo());
|
o.setRefundTime(LocalDateTime.now());
|
|
o.setStatus(Constants.ORDER_STATUS.REFUND.name());
|
o.setStatusBackend(Constants.ORDER_STATUS_BACKEND.REFUND.name());
|
o.update("sys");
|
|
orderMapper.updateById(o);
|
|
deliveryOrderService.refundDelete(orderId);
|
|
releasePrepayLock(o);
|
}
|
|
public String refundOrderSub(Order o, BigDecimal refundAmount){
|
if(o.getPaymentTime() == null || o.getPaymentAmount() == null){
|
throw new ValidationException("未支付订单不可退款");
|
}
|
|
if(o.getRefundTime() != null){
|
throw new ValidationException("已全额退款订单不可退款");
|
}
|
|
if (refundAmount == null || refundAmount.doubleValue() == 0) {
|
throw new ValidationException("退款金额不能为空");
|
}
|
|
List<OrderRefund> reLs = orderRefundMapper.selectList(new QueryWrapper<OrderRefund>()
|
.eq("order_id", o.getId()));
|
BigDecimal rra = new BigDecimal(0);
|
if(reLs != null && reLs.size() > 0){
|
for(OrderRefund r : reLs){
|
rra = rra.add(r.getRefundAmount());
|
}
|
}
|
|
long total = (long)prepareAmount(o.getPaymentAmount());
|
long refund = (long)prepareAmount(refundAmount);
|
|
long rraa = (long)prepareAmount(rra);
|
if(rraa + refund > total){
|
throw new ValidationException("退款金额不能大于订单金额");
|
}
|
|
OrderRefund re = new OrderRefund();
|
re.setId(UUIDGenerator.getUUID());
|
re.setOrderId(o.getId());
|
re.setOrderAmount(o.getPaymentAmount());
|
re.setRefundAmount(refundAmount);
|
|
CreateRequest request = new CreateRequest();
|
request.setOutTradeNo(o.getId());
|
request.setOutRefundNo(re.getId());
|
request.setTransactionId(o.getPaymentTrId());
|
request.setNotifyUrl(PyamentV3Configurer.notify_url_refund);
|
|
AmountReq amount = new AmountReq();
|
amount.setTotal(total);
|
amount.setRefund(refund);
|
amount.setCurrency("CNY");
|
request.setAmount(amount);
|
|
// 调用接口
|
Refund refund1 = refundService.create(request);
|
|
re.setRequest(toJSONString(refund1));
|
re.create(SecurityUtils.getUserId());
|
|
orderRefundMapper.insert(re);
|
|
return re.getId();
|
}
|
|
/**
|
* 查询退款
|
*
|
* @param orderId
|
* @return
|
*/
|
public Refund refundQuery(String orderId){
|
Order o = orderMapper.selectById(orderId);
|
if(StringUtils.isEmpty(o.getRefundNo())){
|
throw new ValidationException("该订单没有退款");
|
}
|
|
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
request.setOutRefundNo(orderId);
|
// 调用接口
|
return refundService.queryByOutRefundNo(request);
|
}
|
|
public ResponseEntity<?> handleRefundCallback(HttpServletRequest request) {
|
try {
|
ServletInputStream inputStream = request.getInputStream();
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
// 读取请求体
|
StringBuilder sb = new StringBuilder();
|
String line;
|
while ((line = bufferedReader.readLine()) != null) {
|
sb.append(line);
|
}
|
String notify = sb.toString();
|
log.info("微信退款通知: {}", notify);
|
RequestParam requestParam = buildByHeaderRequestParam(request, notify);
|
|
// 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败,SDK 会抛出 ValidationException。
|
log.info("退款通知回调:验签、解密并转换成 RefundNotification对象:-------");
|
/**
|
* 常用的通知回调调对象类型有:
|
* 支付 Transaction
|
* 退款 RefundNotification
|
* 若 SDK 暂不支持的类型,请使用 Map.class,嵌套的 Json 对象将被转换成 LinkedTreeMap
|
*/
|
RefundNotification refundNotification;
|
try {
|
refundNotification = notificationParser.parse(requestParam, RefundNotification.class);
|
} catch (ValidationException e) {
|
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
log.error("sign verification failed", e);
|
return new ResponseEntity<>("", HttpStatus.UNAUTHORIZED);
|
}
|
log.info("Transaction:===>>>>退款CallBack状态" + refundNotification.getRefundStatus());
|
String outRefundNo = refundNotification.getOutRefundNo();
|
OrderRefund re = orderRefundMapper.selectById(outRefundNo);
|
if(re != null) {
|
re.setStatus(refundNotification.getRefundStatus().name());
|
re.setNotification(toJSONString(refundNotification));
|
re.setNotifyTime(LocalDateTime.now());
|
orderRefundMapper.updateById(re);
|
}
|
} catch (Exception e) {
|
log.error("解析退款通知出错:{}", e.getMessage(), e);
|
return new ResponseEntity<>("", HttpStatus.INTERNAL_SERVER_ERROR);
|
}
|
|
return new ResponseEntity<>("", HttpStatus.OK);
|
}
|
|
/**
|
* 商家转账
|
*
|
* @return
|
*/
|
public String doBatchTransfer(TransferReqDTO dto, String userId) {
|
Transfer transfer = new Transfer();
|
transfer.setId(StringUtils.isNotEmpty(dto.getId()) ? dto.getId() : UUIDGenerator.getUUID());
|
transfer.setName(dto.getName());
|
transfer.setRemarks(dto.getRemarks());
|
|
InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
|
request.setAppid(dto.getAppId());
|
request.setOutBatchNo(transfer.getId());//【商家批次单号】 商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
|
request.setBatchName(transfer.getName());//【批次名称】 该笔批量转账的名称
|
request.setBatchRemark(transfer.getRemarks());//【批次备注】 转账说明,UTF8编码,最多允许32个字符
|
|
List<TransferDetailReqDTO> details = dto.getDetails();
|
transfer.setTotalNum(details.size());
|
|
Long totalAmount = 0L;
|
for(TransferDetailReqDTO d : details) {
|
TransferDetail td = new TransferDetail();
|
td.setId(StringUtils.isNotEmpty(d.getId()) ? d.getId() : UUIDGenerator.getUUID());
|
td.setTransferId(transfer.getId());
|
td.setAmount(d.getAmount());
|
td.setRemarks(d.getRemarks());
|
td.setUserName(d.getUserName());
|
td.setOpenId(d.getOpenId());
|
td.create(userId);
|
transferDetailMapper.insert(td);
|
|
totalAmount += td.getAmount();
|
|
TransferDetailInput di = new TransferDetailInput();
|
di.setOutDetailNo(td.getId());//【商家明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成
|
di.setTransferAmount(td.getAmount());//【转账金额】 转账金额单位为“分”
|
di.setTransferRemark(td.getRemarks());//【转账备注】 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
|
di.setOpenid(td.getOpenId());
|
di.setUserName(td.getUserName());//【收款用户姓名】 收款方真实姓名。明细转账金额<0.3元时,不允许填写收款用户姓名。明细转账金额 >= 2,000元,该笔明细必须填写收款用户姓名
|
request.getTransferDetailList().add(di);
|
}
|
transfer.setTotalAmount(totalAmount);
|
|
request.setTotalAmount(transfer.getTotalAmount());//【转账总金额】 转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作
|
request.setTotalNum(transfer.getTotalNum());//【转账总笔数】 一个转账批次单最多发起一千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作
|
|
//ACCEPTED:已受理。批次已受理成功,若发起批量转账的30分钟后,转账批次单仍处于该状态,可能原因是商户账户余额不足等。商户可查询账户资金流水,若该笔转账批次单的扣款已经发生,则表示批次已经进入转账中,请再次查单确认
|
//PROCESSING:转账中。已开始处理批次内的转账明细单
|
//FINISHED:已完成。批次内的所有转账明细单都已处理完成
|
//CLOSED:已关闭。可查询具体的批次关闭原因确认
|
InitiateBatchTransferResponse r = transferBatchService.initiateBatchTransfer(request);
|
transfer.setBatchId(r.getBatchId());
|
transfer.setStatus(r.getBatchStatus());
|
transfer.create(userId);
|
transferMapper.insert(transfer);
|
|
return transfer.getId();
|
}
|
|
public void checkTransferStatus(Transfer transfer) {//定时任务验证转账状态
|
GetTransferBatchByOutNoRequest request = new GetTransferBatchByOutNoRequest();
|
request.setOutBatchNo(transfer.getId());
|
request.setNeedQueryDetail(true);
|
request.setDetailStatus("ALL");
|
TransferBatchEntity tb = transferBatchService.getTransferBatchByOutNo(request);
|
transfer.setCheckLog(toJSONString(tb));
|
transfer.setCheckTime(LocalDateTime.now());
|
TransferBatchGet b = tb.getTransferBatch();
|
transfer.setStatus(b.getBatchStatus());
|
transfer.update("sys");
|
transferMapper.updateById(transfer);
|
|
List<TransferDetailCompact> dLs = tb.getTransferDetailList();
|
if(dLs != null && dLs.size() > 0) {
|
for (TransferDetailCompact d : dLs) {
|
TransferDetail td = transferDetailMapper.selectById(d.getOutDetailNo());
|
td.setStatus(d.getDetailStatus());
|
td.update("sys");
|
transferDetailMapper.updateById(td);
|
}
|
}
|
}
|
|
public List<Transfer> getUnCompletedTransfer(){//定时任务验证转账状态
|
List<String> statusList = new ArrayList<>();
|
statusList.add(Constants.TRANSFER_STATUS.ACCEPTED.name());
|
statusList.add(Constants.TRANSFER_STATUS.PROCESSING.name());
|
return transferMapper.selectList(new QueryWrapper<Transfer>()
|
.in("status", statusList)
|
);
|
}
|
}
|