为什么测试地址过不了 AVS 和 ZIP+城市+州 校验
你为 QA 夹具生成了一个看上去很整洁的美国地址 — 街道名合理,城市真实,邮编开头数
字也对得上,电话区号也匹配。你把它粘到 staging 的结账页,结果表单拒绝它:"您输入
的邮编与城市/州不匹配。"更糟的是支付网关返回了 AVS_MISMATCH,
扣款失败。
这两种失败其实都在正常工作。本文解释背后的两套系统 — USPS 驱动的"邮编·城市·州" 校验,与"地址验证系统" (AVS) — 让你能围绕它们的规则设计测试数据,而不是和它们对着干。
两层校验,先简短概览
美国寄送表单通常会让地址通过两层独立校验:
- USPS 可投递性校验。确认城市、州、邮编指向同一个 USPS 认可的邮区, 可选地确认街道号和街道名出现在真实的 USPS 投递路线上。这就是"邮编与城市/州不匹配" 这条错误的来源。
-
AVS — Address Verification System。卡组织(Visa、Mastercard、
Amex、Discover)的校验:把账单街道地址的数字部分和账单邮编与持卡人开户行的记录
做比对。这就是支付网关响应里
AVS_MISMATCH、AVS_ZIP_ONLY_MATCH等码的来源。
注意这是两套针对不同数据源的不同校验。一个合成地址可以通过第 1 层,但仍然过 不了第 2 层,因为银行从来没记录过持卡人住在那里。
USPS 邮编匹配怎么工作
USPS 维护着一个数据库,每个邮编都关联一个或多个"首选"城市名以及一个州。比如裸邮编
19801 唯一归属于特拉华州 Wilmington。如果你把
19801 配上 Dover, DE,校验就会失败,因为 Dover 的邮编是
19901 和 19904 — 那是另一个邮区。
USPS 数据库处理的几个边界情况,自己写匹配的常常踩坑:
-
可接受的备用城市名。
19711官方对应 Newark, DE,但 USPS 也接受 University of Delaware 作为投递地址中的备用名。大多数第三方 校验器会拒绝备用名,即便 USPS 实际上会投。 - 跨社区的邮编边界。一个邮编可能横跨多个城市名甚至多个 county。 结账表单常常接受任一被认可的名称;CRM 有时会用"首选"名覆盖用户输入。
- ZIP+4 精度。4 位后缀把一个邮编再细分到街区甚至单栋建筑。多数结账 表单接受但忽略它;一些物流 API 要求填它以拿到商业折扣价。
对测试夹具的现实结论是:城市、州、邮编要从同一个州的记录中采样。 美国免税州地址生成器就是这么做的 — 如果它挑了特拉华, 城市就是特拉华的城市,邮编也是那个城市真实的邮编。第 1 层永远过。
AVS 怎么工作
AVS 工作在支付网络层。商户向卡组织发起授权时,账单地址里的两段会被发卡行与自己记录 比对:
-
街道地址中的数字部分。
123 Main St和123 Maple Ave在 AVS 看来都是同一个 token123。 一些发卡行会忽略数字之后的所有字符。 - 5 位邮编。
响应是一个字母码,随授权返回。常见值:
| 码 | 含义 |
|---|---|
Y | 街道和邮编都匹配。 |
A | 街道匹配,邮编不匹配。 |
Z | 5 位邮编匹配,街道不匹配。 |
N | 都不匹配。 |
U | 发卡行未响应 / 信息不可得。 |
G | 非美国发卡行,不支持 AVS。 |
Stripe、Adyen、Braintree、Authorize.Net 等支付网关都会暴露这个码,由商户决定每个值
如何处理 — N 自动拒、A 或 Z 软拒,依此
类推。
因为 AVS 比对的是银行里真实持卡人的记录,任何合成地址都不可能通过真实的 AVS 校验。随机生成的街道和邮编只可能在恰好真有持卡人住那的情况下匹配 — 而这恰恰是 AVS 要识别的情况。
针对每种环境设计能用的测试夹具
一个实战指南:在不和校验对着干的情况下使用合成地址。
1. 单元测试和 UI 演示用"州一致"生成器
表单校验、布局测试、截图夹具、种子数据这些场景,你要的是看上去一致的地址,并不需要 通过任何外部 API。本站这样的小型生成器提供的地址,城市、州、邮编永远内部匹配。
2. 在支付沙箱里用官方测试卡 + AVS 测试邮编
Stripe、Braintree、Adyen 都发布了测试卡与"魔法地址",在沙箱环境下能稳定
触发指定的 AVS 响应。比如 Stripe 测试模式下,账单邮编 42424 一定返回
N(不匹配),其他任何 5 位邮编都返回 Y。AVS 相关场景用
这些有文档的魔法值,而不是泛用合成地址。
// Stripe 测试模式 —— 确定性 AVS 结果
const billing = {
line1: '1 Test Lane',
postal_code: '42424', // 强制返回 AVS code N
};
const intent = await stripe.paymentIntents.create({ ... });
3. 单元测试里 mock 掉地址校验调用
如果代码会调用 USPS Address Information API、Smarty(前 SmartyStreets)、Lob、 Google Address Validation 之类,单元测试里把这些客户端 stub 掉。合成地址不需要真的 通过 — 它们只是用来跑通你的代码路径。要验证的是你处理每种文档化响应(包括 partial-match 和 not-found)的代码是否正确。
4. 生产端到端测试用真实银行地址
如果一定要在生产环境用真卡跑端到端测试,请用持卡人真实的账单地址。绕不过银行的记录。 生产环境用合成地址做测试是出名的踩雷点 — 商户账户可能被风控标记。
结账还提示"邮编不匹配"时怎么排查
如果你的地址来自免税州地址生成器但结账仍报"不匹配", 十有八九是下面三种之一:
- 结账接入了商用地址校验 API(Smarty、Lob、USPS Web Tools),它们比邮编-城市 -州走得更深,会要求街道号必须出现在 USPS 该邮编的真实投递清单。 合成街道号过不了。
- 结账在"提交订单"步骤跑 AVS,把 AVS 响应解码成了表单错误。修法要么沙箱里关 AVS, 要么把"预期失败"写进测试团队的文档。
- 表单的邮编数据过时,识别不了最近新增或重命名的邮编。该升级数据集,而不是改测试地址。
小结
两层校验、两套数据源、两种失败模式。USPS 邮编-城市-州 校验关心内部一致性 — 一个州一致的合成地址就能满足。AVS 关心匹配银行里真实持卡人的真实记录 — 任何合成地址都过不了。按照这两个原则去设计测试夹具,一大批莫名其妙的测试失败 工单就不会再出现了。
资料来源与延伸阅读
本文引用的 AVS 响应码、USPS 数据库字段、支付网关文档来自以下官方资料。AVS 码语义因 网关而异,请以你实际接入的网关为准:
- Stripe — 拒付码与 AVS 结果 — 发卡行 AVS 响应的网关侧参考。
- Braintree — AVS 与 CVV 规则 — AVS 响应的可编程规则。
- Authorize.Net — 响应码参考 — 经典 AVS 字母码表(Y, A, Z, N, U, R, S, E, G)。
- USPS Web Tools APIs — ZIP+4、地址标准化、city/state 查询。
- USPS PostalPro — 可投递性与 CASS 认证文档。
- Smarty — US Street Address API — 文中提到的商用 CASS 认证校验 服务参考。