fix(adapay): 回调改用表单参数 data/sign, 修复 body 为空导致回调失败

Adapay 异步通知为 application/x-www-form-urlencoded 表单格式(data+sign), 原实现 getBody() 因 Servlet 已消费 inputStream 读到空串。

- CallbackParseReq: body 字段 -> data + sign 两字段
- AdapayPayCallbackService/AdapayRefundCallbackService: getBody -> request.getParameter 提取 data+sign
This commit is contained in:
daxpay
2026-08-05 10:00:23 +08:00
parent 0072fb050d
commit 7fabc67fce
3 changed files with 33 additions and 21 deletions

View File

@@ -6,13 +6,20 @@ import lombok.Data;
/// # Adapay 回调验签解析请求(主应用 → 子应用)
///
/// 与子应用 dax-pay-channel-two 的 `AdapayCallbackParseReq` 镜像。
/// 主应用组装凭证(publicKey 为空时子应用用全局平台公钥) + 原始 body 下发, 子应用验签并解析。
/// 主应用从异步通知 HTTP 请求中提取表单参数 data + sign, 连同通道凭证下发, 子应用验签并解析。
///
/// Adapay 异步通知为 application/x-www-form-urlencoded 表单格式, 含两个字段:
/// - data: 业务数据 JSON 明文(待验签内容)
/// - sign: data 的 SHA1withRSA 签名(Base64)
@Data
public class AdapayCallbackParseReq {
/// 通道调用凭证(用于获取平台公钥验签, publicKey 为空时子应用用默认公钥)
private AdapaySdkCredential credential;
/// 回调原始 body(Adapay 异步通知 JSON: {data, signature})
private String body;
/// 回调业务数据 JSON 明文(表单参数 data, 待验签内容)
private String data;
/// 回调签名(表单参数 sign, 对 data 的 SHA1withRSA Base64 签名)
private String sign;
}

View File

@@ -10,7 +10,6 @@ import cn.daxpay.open.payment.trade.record.service.PayCallbackRecordService;
import cn.daxpay.open.payment.trade.runtime.service.callback.PayCallbackService;
import cn.daxpay.open.platform.core.enums.pay.notice.CallbackStatusEnum;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -38,22 +37,24 @@ public class AdapayPayCallbackService {
/// 支付回调处理
public String payHandle(String channelMchNo, HttpServletRequest request) {
// 1. 提取回调原始 body
String body = JakartaServletUtil.getBody(request);
// 1. 提取回调表单参数(Adapay 异步通知为 application/x-www-form-urlencoded, 含 data + sign)
String data = request.getParameter("data");
String sign = request.getParameter("sign");
Map<String, Object> notify = new HashMap<>();
notify.put("body", body);
if (StrUtil.isBlank(body)) {
log.error("Adapay 支付回调: body 为空");
notify.put("data", data);
notify.put("sign", sign);
if (StrUtil.isBlank(data) || StrUtil.isBlank(sign)) {
log.error("Adapay 支付回调: data 或 sign 为空");
CallbackData failData = new CallbackData();
failData.setCallbackData(notify);
failData.setCallbackStatus(CallbackStatusEnum.FAIL);
failData.setCallbackErrorMsg("Adapay 支付回调: body 为空");
failData.setCallbackErrorMsg("Adapay 支付回调: data 或 sign 为空");
payCallbackRecordService.savePay(channelMchNo, failData);
return AdapayCode.NOTIFY_FAIL;
}
// 2. 转发子应用验签解析(publicKey 为空, 子应用用全局默认平台公钥)
AdapayCallbackParseResp resp = parse(body, false);
AdapayCallbackParseResp resp = parse(data, sign, false);
if (resp == null || !Boolean.TRUE.equals(resp.getSuccess())) {
log.error("Adapay 支付回调验签失败");
CallbackData failData = new CallbackData();
@@ -93,11 +94,12 @@ public class AdapayPayCallbackService {
}
/// 转发子应用验签解析
private AdapayCallbackParseResp parse(String body, boolean refund) {
private AdapayCallbackParseResp parse(String data, String sign, boolean refund) {
AdapaySdkCredential credential = new AdapaySdkCredential();
AdapayCallbackParseReq req = new AdapayCallbackParseReq();
req.setCredential(credential);
req.setBody(body);
req.setData(data);
req.setSign(sign);
var result = refund
? adapayChannelClient.parseRefundCallback(req)
: adapayChannelClient.parsePayCallback(req);

View File

@@ -10,7 +10,6 @@ import cn.daxpay.open.payment.trade.record.service.PayCallbackRecordService;
import cn.daxpay.open.payment.trade.runtime.service.callback.RefundCallbackService;
import cn.daxpay.open.platform.core.enums.pay.notice.CallbackStatusEnum;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -22,7 +21,7 @@ import java.util.Objects;
/// # Adapay 退款回调处理服务
///
/// Adapay 退款异步通知 → 主应用接收原始 body → 转发子应用验签与解析 →
/// Adapay 退款异步通知(表单参数 data + sign) → 主应用提取表单参数 → 转发子应用验签与解析 →
/// 构建 [RefundCallbackData] 交由 [RefundCallbackService] 更新退款单状态。
///
/// 验签只需全局平台公钥(子应用内置默认), 主应用零加密代码。
@@ -37,15 +36,18 @@ public class AdapayRefundCallbackService {
/// 退款回调处理
public String refundHandle(String channelMchNo, HttpServletRequest request) {
String body = JakartaServletUtil.getBody(request);
// 提取回调表单参数(Adapay 异步通知为 application/x-www-form-urlencoded, 含 data + sign)
String data = request.getParameter("data");
String sign = request.getParameter("sign");
Map<String, Object> notify = new HashMap<>();
notify.put("body", body);
if (StrUtil.isBlank(body)) {
log.error("Adapay 退款回调: body 为空");
notify.put("data", data);
notify.put("sign", sign);
if (StrUtil.isBlank(data) || StrUtil.isBlank(sign)) {
log.error("Adapay 退款回调: data 或 sign 为空");
RefundCallbackData failData = new RefundCallbackData();
failData.setCallbackData(notify);
failData.setCallbackStatus(CallbackStatusEnum.FAIL);
failData.setCallbackErrorMsg("Adapay 退款回调: body 为空");
failData.setCallbackErrorMsg("Adapay 退款回调: data 或 sign 为空");
payCallbackRecordService.saveRefund(channelMchNo, failData);
return AdapayCode.NOTIFY_FAIL;
}
@@ -54,7 +56,8 @@ public class AdapayRefundCallbackService {
AdapaySdkCredential credential = new AdapaySdkCredential();
AdapayCallbackParseReq req = new AdapayCallbackParseReq();
req.setCredential(credential);
req.setBody(body);
req.setData(data);
req.setSign(sign);
var result = adapayChannelClient.parseRefundCallback(req);
if (result.getCode() != 0) {
log.error("Adapay 退款回调: 子应用解析失败: {}", result.getMsg());