跳至正文

API对接,我写了一下午的屎山代码

事情是这样的,上周接了个需求,要把我们系统里的订单数据同步到合作方的一个旧平台。对方给了一份PDF文档,几十页,全是表格和接口定义。我大概扫了一眼,觉得不就是POST JSON嘛,能有多坑。

结果是我太年轻了。

第一个坑就出在鉴权上。对方要求用HMAC-SHA256签名,参数按字典序排序拼接,然后加一个时间戳和nonce。文档里写得很清楚,但关键示例代码是Java的,我这边用的是Python 3.9。我照着逻辑写了一遍,本地跑通了,一上测试环境就报401。查了半天,发现是时间戳的精度问题——Java那边用的是毫秒级的时间戳,而Python的time.time()默认是秒级浮点数。我乘了1000转成整型,但对方服务器校验的时候,居然还要求时间戳和nonce组合起来不能超过5分钟。我本地调试的时候时间对不上,因为我的系统时间和对方服务器差了大概30秒。最后手动在代码里加了个offset,硬写死了-30秒才过。

你以为这就完了?真正的噩梦在数据格式上。

对方接口接收的字段,文档里写的是”phone”,但实际调接口发现返回”手机号不能为空”。我查了日志,确认自己传了phone字段,值也是正常的手机号。反复沟通后,对方技术说字段名应该是”mobile”,文档没更新。我改了之后,又报”签名验证失败”,因为签名的拼接字段里用的是原始字段名”phone”,而实际传参是”mobile”。这个签名逻辑是固定的,我必须把签名用的字段名和实际请求的字段名保持一致。最后我不得不在签名环节用一个别名映射表,把”phone”映射成”mobile”,再拼接签名。代码瞬间变得又臭又长。

更离谱的是,对方接口对金额的处理是分单位,传整数。我们的系统存的是元,两位小数。我写了个转换函数,乘以100取整,看起来没问题。但测试的时候发现有一笔订单金额是99.99元,转成分后是9999,对方系统返回”金额格式异常”。我反复排查,最后发现对方的整形字段长度是4位,最大只能存9999,而9999刚好等于99.99元转分后的值。但实际接口文档里写的字段类型是int(10),我以为是10位长度。对方说他们数据库里这个字段是smallint,最大32767,但文档写错了。我特么……最后只能把这笔订单单独走线下处理。

到这我已经有点崩溃了,但最恶心的是回调通知。

对方说订单同步成功后,会回调我们的接口,给我们一个状态更新。文档里说回调地址是POST方式,Content-Type是application/json。我写了个接收端点,上线后第二天发现有一批订单状态没更新。查日志发现回调请求确实进来了,但body是空的。我以为是解析问题,打印了原始请求体,发现对方传的居然是application/x-www-form-urlencoded格式,而且参数名是”data”,值是JSON字符串。也就是说,他们把JSON序列化后塞进了一个表单字段里。文档里写的”JSON格式”其实是”JSON字符串作为表单值”。我不得不加了一层判断,如果Content-Type不是JSON,就手动解析表单字段再反序列化。

这一系列操作下来,代码里充满了各种if-else和版本兼容分支。我觉得自己不是在写API对接,是在给对方的文档擦屁股。

最后总结几个血泪经验,兄弟们记住:

第一,对接任何外部API,千万别只依赖文档。一定要先在测试环境真实调一次,确认字段名、数据类型、签名逻辑全部对得上。哪怕文档写得再漂亮,也要怀疑一切。

第二,时间戳、签名、字段映射这些基础但容易出细节偏差的东西,最好写一个专门的测试函数,把对方示例里的请求和签名结果逐一比对。我当时就是偷懒没写,多花了两个小时排查。

第三,金额、手机号、日期这些常见字段,主动确认对方的单位、格式、长度限制。别信文档里的”int(10)”,直接问对方”这个字段最大能传多少”。

第四,回调通知一定要做幂等处理。因为对方可能重复回调,而且他们回调的Content-Type都可能变。我在接收端加了一个请求体日志,把所有原始请求原样打印到文件里,方便事后排查。这个习惯救了我好几次。

第五,也是最重要的,对接过程中所有不一致的地方,一定要邮件或者工单确认留痕。口头沟通说完就忘,最后出了问题扯皮的时候,你拿不出证据就只能自己背锅。我这次被坑了三次,每次都是”文档没更新”、”之前是这样写的”这种话,如果没有邮件记录,对方根本不认。

现在这堆代码还在线上跑着,每次看到那段处理表单回调的逻辑我都想重构,但又怕改了出问题。先这样吧,等下一个版本迭代再说。反正对接外部接口这事,做一次,长一次记性。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注