退款通知

This commit is contained in:
磷叶 2024-12-11 16:35:56 +08:00
parent 6421f31853
commit dc01fb47cd
9 changed files with 123 additions and 44 deletions

View File

@ -128,4 +128,5 @@ public class AliAuthService {
return plainData;
}
}

View File

@ -1,9 +1,12 @@
package com.ruoyi.common.pay.ali.enums;
import com.ruoyi.common.utils.collection.CollectionUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* @author wjh
* 2024/12/10
@ -20,4 +23,11 @@ public enum AliTradeStatusEnum {
private String status;
private String msg;
/**
* 通知成功的交易状态
*/
public static List<String> notifySuccessList() {
return CollectionUtils.map(AliTradeStatusEnum::getStatus, TRADE_SUCCESS, TRADE_FINISHED);
}
}

View File

@ -5,6 +5,7 @@ import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
@ -21,10 +22,16 @@ import com.ruoyi.common.pay.Refundable;
import com.ruoyi.common.pay.ali.enums.AliTradeStatusEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.http.HttpUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author wjh
@ -124,10 +131,12 @@ public class AliPayService implements PayApi {
// 设置退款请求号
model.setOutRequestNo(refund.refundOutRefundNo());
request.setBizModel(model);
request.setNotifyUrl(aliConfig.getNotifyUrl());
try {
AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
ServiceUtil.assertion(!response.isSuccess(), response.getMsg());
ServiceUtil.assertion(!"Y".equals(response.getFundChange()), "退款失败,本次退款未导致资金变化");
return response;
} catch (Exception e) {
throw new ServiceException("调用支付宝退款失败:" + e.getMessage());
@ -151,4 +160,17 @@ public class AliPayService implements PayApi {
AlipayTradeQueryResponse res = (AlipayTradeQueryResponse) result;
return DateUtils.toLocalDateTime(res.getSendPayDate());
}
/**
* 对通知进行验签
*/
public void checkNotifySign(Map<String, String> params) {
//调用SDK验证签名
try {
boolean signVerified = AlipaySignature.rsaCertCheckV1(params, aliConfig.getAlipayCertPath(),"UTF-8","RSA2");
ServiceUtil.assertion(!signVerified, "验签失败");
} catch (Exception e) {
throw new ServiceException("验签失败" + e.getMessage());
}
}
}

View File

@ -138,7 +138,7 @@ public class SybPayService implements PayApi {
@Override
public LocalDateTime getPayTime(Object result) {
Map<String, String> res = (Map<String, String>)result;
return DateUtils.toLocalDate(res.get("fintime"), "yyyyMMddHHmmss");
return DateUtils.toLocalDateTime(res.get("fintime"), "yyyyMMddHHmmss");
}
/**

View File

@ -5,10 +5,8 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.util.Calendar;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
import static org.apache.commons.lang3.time.DateFormatUtils.format;
@ -309,15 +307,6 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
/**
* 字符串转LocalDateTime
* @param timeStr 字符串
* @param format 格式化
*/
public static LocalDateTime toLocalDate(String timeStr, String format) {
return LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern(format));
}
public static LocalDateTime toLocalDateTime(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
}

View File

@ -11,6 +11,8 @@ import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@ -491,4 +493,28 @@ public class HttpUtils
}
return result.toString();
}
/**
* 将request中的参数转换成Map
* @param request
* @return
*/
public static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> retMap = new HashMap<String, String>();
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决这段代码在出现乱码时使用如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
retMap.put(name, valueStr);
}
return retMap;
}
}

View File

@ -148,7 +148,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/>
</trim>
where pay_id = #{payId}
where pay_id = #{data.payId}
</update>
<sql id="updateColumns">

View File

@ -1,11 +1,14 @@
package com.ruoyi.web.controller.app;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.pay.ali.enums.AliTradeStatusEnum;
import com.ruoyi.common.pay.ali.service.AliPayService;
import com.ruoyi.common.pay.syb.enums.SybTrxCode;
import com.ruoyi.common.pay.syb.enums.SybTrxStatus;
import com.ruoyi.common.pay.syb.service.SybPayService;
@ -72,6 +75,9 @@ public class AppPayController extends BaseController {
@Autowired
private TmPayService tmPayService;
@Autowired
private AliPayService aliPayService;
@ApiOperation("微信支付充值订单")
@GetMapping("/wx/{billNo}")
public AjaxResult wxPay(@PathVariable @ApiParam("订单编号") String billNo) {
@ -185,7 +191,7 @@ public class AppPayController extends BaseController {
// 微信支付通知
if (SybTrxCode.WX_PAY.equalsCode(trxCode)) {
// 新版支付订单
payBillService.handleSuccess(cusOrderId, DateUtils.toLocalDate(params.get("paytime"), "yyyyMMddHHmmss"));
payBillService.handleSuccess(cusOrderId, DateUtils.toLocalDateTime(params.get("paytime"), "yyyyMMddHHmmss"));
}
// 微信退款通知
else if (SybTrxCode.WX_REFUND.equalsCode(trxCode)) {
@ -233,18 +239,42 @@ public class AppPayController extends BaseController {
@ApiOperation("支付宝支付通知")
@PostMapping("/notify/ali")
@Anonymous
public ResponseEntity<Boolean> aliPayNotify(HttpServletRequest request) {
public String aliPayNotify(HttpServletRequest request) {
try {
log.info("收到支付宝支付通知{}", request);
String body = HttpUtils.getBody(request);
log.info("收到支付宝支付通知{}", body);
// todo
} catch (ValidationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
Map<String, String> params = HttpUtils.convertRequestParamsToMap(request);
aliPayService.checkNotifySign(params);
log.info("收到支付宝支付通知{}", params);
// 交易状态
String tradeStatus = params.get("trade_status");
// 交易状态不为已支付
if (!AliTradeStatusEnum.notifySuccessList().contains(tradeStatus)) {
return "fail";
}
// 判断是否是退款订单
String refundFee = params.get("refund_fee");
// 退款订单
if (StrUtil.isNotBlank(refundFee)) {
// 商家业务号主要是退款单号
String outBizNo = params.get("out_biz_no");
refundService.handleRefundSuccess(outBizNo);
}
// 支付订单
else {
//商户订单号
String outTradeNo = params.get("out_trade_no");
payBillService.handleSuccess(outTradeNo, DateUtils.toLocalDateTime(params.get("gmt_payment")));
}
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
return "fail";
}
return ResponseEntity.status(HttpStatus.OK).body(null);
return "success";
}
@ApiOperation("支付宝转账通知")
@ -255,7 +285,8 @@ public class AppPayController extends BaseController {
log.info("收到支付宝转账通知{}", request);
String body = HttpUtils.getBody(request);
log.info("收到支付宝转账通知{}", body);
// todo
// TODO 似乎不生效
} catch (ValidationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
} catch (Exception e) {

View File

@ -77,35 +77,35 @@ public class PayBillController extends BaseController
/**
* 新增支付订单
*/
@PreAuthorize("@ss.hasPermi('ss:payBill:add')")
@Log(title = "支付订单", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody PayBill payBill)
{
return toAjax(payBillService.insertPayBill(payBill));
}
// @PreAuthorize("@ss.hasPermi('ss:payBill:add')")
// @Log(title = "支付订单", businessType = BusinessType.INSERT)
// @PostMapping
// public AjaxResult add(@RequestBody PayBill payBill)
// {
// return toAjax(payBillService.insertPayBill(payBill));
// }
/**
* 修改支付订单
*/
@PreAuthorize("@ss.hasPermi('ss:payBill:edit')")
@Log(title = "支付订单", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody PayBill payBill)
{
return toAjax(payBillService.updatePayBill(payBill));
}
// @PreAuthorize("@ss.hasPermi('ss:payBill:edit')")
// @Log(title = "支付订单", businessType = BusinessType.UPDATE)
// @PutMapping
// public AjaxResult edit(@RequestBody PayBill payBill)
// {
// return toAjax(payBillService.updatePayBill(payBill));
// }
/**
* 删除支付订单
*/
@PreAuthorize("@ss.hasPermi('ss:payBill:remove')")
@Log(title = "支付订单", businessType = BusinessType.DELETE)
@DeleteMapping("/{payIds}")
public AjaxResult remove(@PathVariable Long[] payIds)
{
return toAjax(payBillService.deletePayBillByPayIds(payIds));
}
// @PreAuthorize("@ss.hasPermi('ss:payBill:remove')")
// @Log(title = "支付订单", businessType = BusinessType.DELETE)
// @DeleteMapping("/{payIds}")
// public AjaxResult remove(@PathVariable Long[] payIds)
// {
// return toAjax(payBillService.deletePayBillByPayIds(payIds));
// }
/**
* 退款