PAYATHON 2026

支付宝H5支付使用certificateExecute报错

支付小周

结论

java.lang.IllegalArgumentException: expected value at column 1 通常并非证书内容有误,而是响应的解析方式与实际格式不匹配。

alipay.trade.wap.pay 是页面类支付接口,支付宝网关返回 HTML 支付页面。certificateExecute(request) 会按照普通开放 API 的响应结构进行解析,通常期待 JSON。当解析器读取第 1 个字符时,如果遇到 <html>、<!DOCTYPE html> 或 <form>,就会报 expected value at column 1。

证书模式只影响验签和证书链配置,不会将 H5 支付接口返回的 HTML 转换成 JSON。WAP 支付应使用 pageExecute(request) 获取页面内容。

正确处理方式

使用证书参数初始化 AlipayClient,然后调用 pageExecute:

AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
alipayConfig.setAppId(appId);
alipayConfig.setPrivateKey(appPrivateKey);
alipayConfig.setFormat("json");
alipayConfig.setCharset("UTF-8");
alipayConfig.setSignType("RSA2");

// 应用公钥证书
alipayConfig.setAppCertPath(appCertPath);

// 支付宝公钥证书
alipayConfig.setAlipayPublicCertPath(alipayPublicCertPath);

// 支付宝根证书
alipayConfig.setRootCertPath(alipayRootCertPath);

AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);

AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(outTradeNo);
model.setTotalAmount(totalAmount);
model.setSubject(subject);
model.setProductCode("QUICK_WAP_WAY");

request.setBizModel(model);

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String html = response.getBody();

在 Spring MVC 中,可以直接将 HTML 写入响应:

@GetMapping(value = "/pay", produces = "text/html;charset=UTF-8")
@ResponseBody
public String pay() throws AlipayApiException {
    AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
    return response.getBody();
}

也可以按照实际前端架构返回 HTML 字符串,再由浏览器写入页面或执行跳转。不要对这段 HTML 再做 JSON 反序列化。

为什么报错后仍然能看到支付页面

异常日志下方打印的内容能够正常显示支付页面,说明支付宝返回的主体很可能是有效的 HTML。问题出在 SDK 或业务代码将这段 HTML 当成 JSON 解析,而非支付页面生成失败。

例如,下面的响应开头对 JSON 解析器来说并不合法:

<form name="punchout_form" method="post" action="...">
    ...
</form>

解析器在第 1 列期待 {、[、字符串或其他合法的 JSON 值,实际却读到了 <,于是抛出:

java.lang.IllegalArgumentException: expected value at column 1

建议的排查顺序

1. 确认调用方法

页面类接口应使用:

alipayClient.pageExecute(request);

不要使用:

alipayClient.certificateExecute(request);

也不要将 pageExecute 返回的 body 交给 JSON 解析器。

2. 查看响应正文的第一个字符

临时记录响应正文开头的一小段即可。不要在生产日志中完整输出包含支付参数的页面:

String body = response.getBody();

if (body != null) {
    String preview = body.substring(0, Math.min(body.length(), 200));
    log.debug("Alipay response preview: {}", preview);
}

如果内容以 <form、<html 或 <!DOCTYPE 开头,基本可以确认 HTML 被误当成了 JSON。

如果响应以 { 开头,则需要继续检查业务代码是否进行了二次解析,并根据异常堆栈确认错误来自支付宝 SDK、JSON 库,还是项目自己的封装层。

3. 确认证书模式初始化完整

证书模式通常需要配置:

  • 应用私钥
  • 应用公钥证书
  • 支付宝公钥证书
  • 支付宝根证书
  • RSA2
  • 与证书所属环境一致的支付宝网关地址

沙箱环境和正式环境的应用、证书及网关不能混用。还要确认应用进程有权限读取证书路径。

支付宝 Java SDK 不同版本的配置方式可能略有区别,应以当前依赖版本提供的 AlipayConfig 或 DefaultAlipayClient 构造方式为准。

4. 查看完整异常堆栈

不要只看最外层的 IllegalArgumentException。应重点检查堆栈中最先出现的业务类或 SDK 解析类:

Caused by: java.lang.IllegalArgumentException: expected value at column 1

如果异常来自 JSON 解析组件,而原始响应是 HTML,就可以进一步确认问题在于调用方式或二次解析。

5. 检查 SDK 版本和封装代码

如果已经使用 pageExecute(request),但仍然出现相同错误,需要检查:

  • 项目是否对 AlipayClient 做了统一代理或二次封装;
  • 是否有拦截器强制将响应转换为 JSON;
  • 是否混用了多个版本的支付宝 SDK;
  • 是否存在旧版 SDK 与当前证书模式配置不兼容的情况;
  • AlipayTradeWapPayResponse#getBody() 是否被再次反序列化。

升级 SDK 前,应先核对当前版本的接口签名和升级说明,不能只靠更换版本来掩盖调用方式错误。

注意事项

pageExecute 返回支付页面,只说明已经生成了用于拉起收银台的 HTML,并不代表订单最终支付成功。支付结果应主要以支付宝异步通知为准,并在必要时主动调用交易查询接口核验状态。

处理异步通知时,还要使用证书模式对应的验签配置,校验签名、应用 ID、商户订单号、金额和交易状态。不要仅凭浏览器的同步跳转更新订单状态。

备注:内容仅供参考。