You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Spring集成Adyen Webhook报错:不支持application/x-www-form-urlencoded类型

Spring接收Adyen Webhook的application/x-www-form-urlencoded请求并映射到对象

问题分析

你遇到的核心问题有两个:

  1. Spring 默认不支持直接将application/x-www-form-urlencoded请求体自动转换为POJO,直接用DTO作为接口参数会触发Content type not supported错误;
  2. 用Map接收后无法通过ObjectMapper转换为DTO,是因为Adyen Webhook的notificationItems参数是JSON字符串格式,而非直接的表单字段,ObjectMapper无法直接将字符串映射为List类型字段。

解决方案

下面提供两种可行的处理方式,推荐优先使用第一种结合Adyen官方SDK的方案,更稳定可靠。

方式一:使用Adyen官方SDK处理(推荐)

Adyen官方Java SDK已经封装了Webhook相关的DTO类,无需自行定义,避免字段匹配错误:

  1. 引入SDK依赖(Maven示例):
<dependency>
    <groupId>com.adyen</groupId>
    <artifactId>adyen-java-api-library</artifactId>
    <version>21.0.0</version> <!-- 替换为最新版本 -->
</dependency>
  1. 修改控制器方法,手动解析表单参数:
import com.adyen.model.notification.NotificationRequest;
import com.adyen.model.notification.NotificationRequestItem;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.Response;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/webhooks")
public class AdyenWebhookController {
    private static final Logger log = LoggerFactory.getLogger(AdyenWebhookController.class);
    private final AdyenService adyenService;

    public AdyenWebhookController(AdyenService adyenService) {
        this.adyenService = adyenService;
    }

    @PostMapping(value = "/notifications", consumes = "application/x-www-form-urlencoded")
    public Response handleWebhook(HttpServletRequest request) {
        try {
            // 从表单参数中获取核心的JSON字符串参数
            String notificationItemsJson = request.getParameter("notificationItems");
            String merchantAccount = request.getParameter("merchantAccount");

            // 将JSON字符串解析为SDK定义的对象列表
            ObjectMapper mapper = new ObjectMapper();
            java.util.List<NotificationRequestItem> itemList = mapper.readValue(
                notificationItemsJson,
                new TypeReference<java.util.List<NotificationRequestItem>>() {}
            );

            // 组装成官方的NotificationRequest对象
            NotificationRequest notificationRequest = new NotificationRequest();
            notificationRequest.setNotificationItems(itemList);
            notificationRequest.setMerchantAccount(merchantAccount);

            // 处理Webhook逻辑
            adyenService.webhooks(notificationRequest);
            return Response.ok("accepted").build();
        } catch (Exception ex) {
            log.error("Adyen Webhook处理失败", ex);
            return Response.serverError(ex.getMessage()).build();
        }
    }
}

方式二:自定义DTO并处理JSON字符串字段

如果不想依赖官方SDK,可自行定义DTO,并通过自定义setter解析JSON字符串:

  1. 定义DTO类,为notificationItems字段添加自定义setter:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;

public class NotificationRequestDTO {
    private List<NotificationItemDTO> notificationItems;
    private String merchantAccount;

    // 其他字段的getter/setter...

    public List<NotificationItemDTO> getNotificationItems() {
        return notificationItems;
    }

    // 自定义setter,将JSON字符串转换为List
    public void setNotificationItems(String notificationItemsJson) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        this.notificationItems = mapper.readValue(
            notificationItemsJson,
            new TypeReference<List<NotificationItemDTO>>() {}
        );
    }

    public String getMerchantAccount() {
        return merchantAccount;
    }

    public void setMerchantAccount(String merchantAccount) {
        this.merchantAccount = merchantAccount;
    }
}

// 对应的NotificationItemDTO
public class NotificationItemDTO {
    private String eventCode;
    private boolean success;
    private String pspReference;
    // 其他字段及getter/setter...
}
  1. 控制器方法使用@ModelAttribute绑定DTO:
@PostMapping(value = "/notifications", consumes = "application/x-www-form-urlencoded")
public Response handleWebhook(@ModelAttribute NotificationRequestDTO notificationRequest) {
    try {
        adyenService.webhooks(notificationRequest);
        return Response.ok("accepted").build();
    } catch (Exception ex) {
        log.error("Adyen Webhook处理失败", ex);
        return Response.serverError(ex.getMessage()).build();
    }
}

关键注意事项

  • 确保DTO字段名与Adyen Webhook的表单参数名完全一致(大小写敏感),比如notificationItems、merchantAccount;
  • notificationItems是JSON字符串,必须先解析为对象列表再使用,不能直接映射;
  • 处理Webhook时,务必验证Adyen的签名,避免恶意请求(官方SDK提供了签名验证工具类)。

内容的提问来源于stack exchange,提问作者Matteo Sperti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:00:45