Chase Payment Tech EUDD 请求不符合 DTD
结论
Request does not adhere to the DTD 通常说明请求的 XML 结构不符合 Chase 指定的 DTD,并不代表测试账号或测试 URL 有问题。
目前提供的内容只有字段值,没有 XML 元素名、层级和属性,暂时无法判断具体违反了哪条 DTD 规则。排查时需要重点确认:
- 请求是否为完整且格式合法的 XML
- 根元素和子元素的名称是否完全正确
- 元素的嵌套关系和排列顺序是否符合 DTD
- 是否缺少必填元素,或者包含 DTD 未声明的元素
- 空元素、字符编码和特殊字符是否符合要求
- HTTP 请求体是否被表单编码、转义,或者混入了额外内容
为什么字段值看起来正确,仍然会被拒绝
DTD 主要检查 XML 结构。假设 DTD 定义如下:
<!ELEMENT payment (type, account, amount, currency)>
下面的请求符合这项定义:
<payment>
<type>ED</type>
<account>8888888888</account>
<amount>4679</amount>
<currency>978</currency>
</payment>
即使字段和值都没有缺失,只要交换两个元素的位置,也可能导致验证失败:
<payment>
<type>ED</type>
<account>8888888888</account>
<currency>978</currency>
<amount>4679</amount>
</payment>
DTD 中的元素名通常区分大小写,元素顺序也经常是固定的。多一个字段、少一个字段,或者使用了错误的父元素,都可能触发同类错误。
问题中展示的数据也很像 XML 标签在复制或网页显示时被过滤后留下的内容。如果实际发送的请求同样只有这些纯文本值,就一定无法通过 XML DTD 验证。
建议的排查步骤
1. 保存实际发送的原始请求体
不要只看程序里的对象或拼接前的字符串。应记录最终写入 HTTP 连接的字节内容,并确认请求体类似下面这样:
<?xml version="1.0" encoding="UTF-8"?>
<RootElement>
<!-- Chase 文档要求的元素 -->
</RootElement>
根元素和字段名称必须使用 Chase EUDD 文档或示例规定的名称,不能根据字段含义自行命名。
记录请求时,需要遮盖账号、商户号和银行账号等敏感信息,但应保留以下内容:
- XML 标签
- 标签顺序
- 层级关系
- 属性
- 空元素
- XML 声明
只有字段值,无法分析 DTD 错误。
2. 使用 Chase 提供的 DTD 进行本地验证
如果已经拿到 DTD 文件,可以先用 xmllint 验证:
xmllint --noout --dtdvalid chase-eudd.dtd request.xml
验证器一般会返回比接口更具体的错误信息,例如:
Element payment content does not follow the DTD
No declaration for element ...
Element ... was declared EMPTY but has content
如果 XML 通过 DOCTYPE 引用 DTD,也可以执行:
xmllint --noout --valid request.xml
前提是 XML 中的 DOCTYPE 地址可以访问,或者本地实体目录已经正确配置。不要自行添加 DOCTYPE,只有 Chase 的接口规范明确要求时才应发送。
3. 严格比较元素顺序
逐项对照 Chase 提供的成功请求示例,检查:
- 根元素名称
- 请求头节点
- 商户信息节点
- 交易节点
- 付款人和银行账户节点
- 地址节点
- 金额、币种和备注节点
XML 子元素不一定能随意换序。如果 DTD 使用 (A,B,C),请求就必须按照 A、B、C 的顺序发送。只有定义为 (A|B|C)* 等形式时,顺序才可能不受限制。
4. 检查空值和可选字段
下面几种写法在 DTD 或接口实现中可能表示不同的含义:
<field></field>
<field/>
<!-- 完全不发送 field -->
如果字段可选,通常应根据文档要求将其省略,而不是发送空元素。如果字段必填,则不能因为没有值就直接删除。
还要避免生成以下内容:
<field>null</field>
<field>undefined</field>
5. 检查 HTTP 发送方式
XML 一般应直接作为请求体发送,而不是放在表单参数中。典型请求形式如下:
curl --request POST 'https://test-endpoint.example' \
--header 'Content-Type: application/xml; charset=UTF-8' \
--data-binary '@request.xml'
实际 URL、认证头和媒体类型仍须以 Chase 文档为准。
下面这种发送方式可能改变请求内容:
xml=<RootElement>...</RootElement>
如果接口要求接收原始 XML,使用这种表单格式后,服务器收到的内容就不会以 XML 根元素开头。
同时检查是否误用了:
Content-Type: application/x-www-form-urlencoded
也不要把 XML 当作 JSON 字符串发送:
{
"xml": "<RootElement>...</RootElement>"
}
除非接口文档明确要求这样封装。
代码中应使用 XML 库生成请求
不要直接拼接 XML。以 Java 为例,可以使用 DOM 创建并序列化请求:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
Element root = document.createElement("RootElement");
document.appendChild(root);
Element currency = document.createElement("Currency");
currency.setTextContent("978");
root.appendChild(currency);
Element description = document.createElement("Description");
description.setTextContent("This is a test");
root.appendChild(description);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
StringWriter output = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(output));
String requestBody = output.toString();
这里的 RootElement、Currency 和 Description 只用于演示如何安全地生成 XML,不能直接用作 Chase 的真实字段名。实际代码应改用 DTD 声明的元素,并严格按照规定顺序添加。
XML 库还会正确转义备注中的特殊字符。例如:
Test & verification
会输出为:
<Description>Test & verification</Description>
如果直接拼接 XML 时遗漏了这种转义,整个请求都会变成格式错误的 XML。
还需要检查字段内容
示例中有两项需要核对:
978是 ISO 4217 中欧元的数字代码。如果 Chase 要求字母代码,则可能需要使用EUR,具体应以 EUDD 的字段定义为准。- 国家值是
DE,但邮编94107看起来不像常规德国邮编。它在格式上是五位数字,不一定会导致 DTD 验证失败,但后续业务校验可能拒绝该值。
金额 4679 的单位也需要确认。支付接口可能将它解释为最小货币单位,也可能要求使用带小数点的金额格式。仅凭现有内容,无法确定 Chase EUDD 的具体要求。
严格来说,标准 DTD 通常只能验证元素结构和属性规则,不能完整校验邮编、账号长度或金额格式。Chase 也可能把 XML 解析、DTD 验证以及部分业务格式错误统一返回为 05。因此,修正结构后还要继续检查各字段的业务规则。
最有效的处理方式
先取得与测试端点完全匹配的 DTD、字段说明和一份官方成功请求示例,然后按以下顺序处理:
- 输出程序实际发送的完整 XML。
- 使用 XML 解析器确认格式正确。
- 使用对应的 DTD 在本地验证。
- 对照成功示例,逐项检查元素名称、顺序、层级和属性。
- 确认请求使用正确的
Content-Type,并以原始 XML 形式发送。 - DTD 验证通过后,再检查币种、地址、金额和账户字段。
如果 Chase 没有提供实际使用的 DTD,仅凭错误码 05 无法可靠判断具体原因。这种情况下,应向 Chase 技术支持索取测试环境对应的 DTD、请求样例和服务器端详细验证日志。诊断这类问题最有用的材料是完整的脱敏 XML,而不是单独列出的字段值。
备注:内容仅供参考。
