退款通知

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; return plainData;
} }
} }

View File

@ -1,9 +1,12 @@
package com.ruoyi.common.pay.ali.enums; package com.ruoyi.common.pay.ali.enums;
import com.ruoyi.common.utils.collection.CollectionUtils;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/** /**
* @author wjh * @author wjh
* 2024/12/10 * 2024/12/10
@ -20,4 +23,11 @@ public enum AliTradeStatusEnum {
private String status; private String status;
private String msg; 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.AlipayTradeCreateModel;
import com.alipay.api.domain.AlipayTradeQueryModel; import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCloseRequest; import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradeCreateRequest; import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest; 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.pay.ali.enums.AliTradeStatusEnum;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil; import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.http.HttpUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/** /**
* @author wjh * @author wjh
@ -124,10 +131,12 @@ public class AliPayService implements PayApi {
// 设置退款请求号 // 设置退款请求号
model.setOutRequestNo(refund.refundOutRefundNo()); model.setOutRequestNo(refund.refundOutRefundNo());
request.setBizModel(model); request.setBizModel(model);
request.setNotifyUrl(aliConfig.getNotifyUrl());
try { try {
AlipayTradeRefundResponse response = alipayClient.certificateExecute(request); AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
ServiceUtil.assertion(!response.isSuccess(), response.getMsg()); ServiceUtil.assertion(!response.isSuccess(), response.getMsg());
ServiceUtil.assertion(!"Y".equals(response.getFundChange()), "退款失败,本次退款未导致资金变化");
return response; return response;
} catch (Exception e) { } catch (Exception e) {
throw new ServiceException("调用支付宝退款失败:" + e.getMessage()); throw new ServiceException("调用支付宝退款失败:" + e.getMessage());
@ -151,4 +160,17 @@ public class AliPayService implements PayApi {
AlipayTradeQueryResponse res = (AlipayTradeQueryResponse) result; AlipayTradeQueryResponse res = (AlipayTradeQueryResponse) result;
return DateUtils.toLocalDateTime(res.getSendPayDate()); 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 @Override
public LocalDateTime getPayTime(Object result) { public LocalDateTime getPayTime(Object result) {
Map<String, String> res = (Map<String, String>)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.text.SimpleDateFormat;
import java.time.*; import java.time.*;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
import static org.apache.commons.lang3.time.DateFormatUtils.format; 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(); 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) { public static LocalDateTime toLocalDateTime(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
} }

View File

@ -11,6 +11,8 @@ import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
@ -491,4 +493,28 @@ public class HttpUtils
} }
return result.toString(); 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=","> <trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/> <include refid="updateColumns"/>
</trim> </trim>
where pay_id = #{payId} where pay_id = #{data.payId}
</update> </update>
<sql id="updateColumns"> <sql id="updateColumns">

View File

@ -1,11 +1,14 @@
package com.ruoyi.web.controller.app; package com.ruoyi.web.controller.app;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.ServiceException; 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.SybTrxCode;
import com.ruoyi.common.pay.syb.enums.SybTrxStatus; import com.ruoyi.common.pay.syb.enums.SybTrxStatus;
import com.ruoyi.common.pay.syb.service.SybPayService; import com.ruoyi.common.pay.syb.service.SybPayService;
@ -72,6 +75,9 @@ public class AppPayController extends BaseController {
@Autowired @Autowired
private TmPayService tmPayService; private TmPayService tmPayService;
@Autowired
private AliPayService aliPayService;
@ApiOperation("微信支付充值订单") @ApiOperation("微信支付充值订单")
@GetMapping("/wx/{billNo}") @GetMapping("/wx/{billNo}")
public AjaxResult wxPay(@PathVariable @ApiParam("订单编号") String billNo) { public AjaxResult wxPay(@PathVariable @ApiParam("订单编号") String billNo) {
@ -185,7 +191,7 @@ public class AppPayController extends BaseController {
// 微信支付通知 // 微信支付通知
if (SybTrxCode.WX_PAY.equalsCode(trxCode)) { 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)) { else if (SybTrxCode.WX_REFUND.equalsCode(trxCode)) {
@ -233,18 +239,42 @@ public class AppPayController extends BaseController {
@ApiOperation("支付宝支付通知") @ApiOperation("支付宝支付通知")
@PostMapping("/notify/ali") @PostMapping("/notify/ali")
@Anonymous @Anonymous
public ResponseEntity<Boolean> aliPayNotify(HttpServletRequest request) { public String aliPayNotify(HttpServletRequest request) {
try { try {
log.info("收到支付宝支付通知{}", request); log.info("收到支付宝支付通知{}", request);
String body = HttpUtils.getBody(request); Map<String, String> params = HttpUtils.convertRequestParamsToMap(request);
log.info("收到支付宝支付通知{}", body); aliPayService.checkNotifySign(params);
// todo
} catch (ValidationException e) { log.info("收到支付宝支付通知{}", params);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
// 交易状态
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) { } catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); return "fail";
} }
return ResponseEntity.status(HttpStatus.OK).body(null);
return "success";
} }
@ApiOperation("支付宝转账通知") @ApiOperation("支付宝转账通知")
@ -255,7 +285,8 @@ public class AppPayController extends BaseController {
log.info("收到支付宝转账通知{}", request); log.info("收到支付宝转账通知{}", request);
String body = HttpUtils.getBody(request); String body = HttpUtils.getBody(request);
log.info("收到支付宝转账通知{}", body); log.info("收到支付宝转账通知{}", body);
// todo
// TODO 似乎不生效
} catch (ValidationException e) { } catch (ValidationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
} catch (Exception e) { } catch (Exception e) {

View File

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