前言ChatGPT、Codex趋势:为什么AI越来越强以后,“一次把任务交代清楚”反而越来越重要?-CSDN博客

想到的问题

一、Goal:先定义 "为什么对接",而不是 "对接什么"

模糊的目标:

"对接一下支付宝支付 API。"

清晰的目标:

"在订单结算页接入支付宝手机网站支付(alipay.trade.wap.pay),用户支付成功后回调更新订单状态并触发发货流程,支付超时时间 30 分钟。"

Goal 需要回答的问题:

  • 这个 API 解决什么业务问题?(支付?登录?消息推送?数据同步?)
  • 调用时机是什么?(用户触发 / 定时任务 / 事件驱动)
  • 同步还是异步?(实时等待结果 vs 提交后轮询 / 回调)
  • 最终交付给业务方的是什么?(一个内部服务方法?一个 SDK?一个消息事件?)

二、Scope:明确 "接哪些" 和 "不接哪些"

一个三方平台通常有几十个 API,不要全接。

Scope 内:

  • 明确列出需要调用的端点(endpoint 清单)
  • 例如:只接统一下单 + 查询订单 + 退款,不接对账下载和分账
  • 哪些业务模块会调用这个对接层

Scope 外(Non-goal):

  • 不封装该平台的全部 API
  • 不做通用 SDK(除非明确要求)
  • 不处理与当前业务无关的高级特性(如商家转账、电子发票)

文章里的 Scope Creep 在 API 对接中极其常见:接支付时顺手把退款、对账、分账全做了,结果项目周期翻倍。


三、Constraints:对接前必须锁定的约束条件

这是最容易被忽略、但出问题后代价最大的部分。

3.1 安全约束

  • 密钥管理:AppID/Secret/ 私钥不能硬编码,必须走配置中心或密钥管理服务,支持热更新和轮换
  • 签名验签:请求签名、回调验签的算法和密钥分开管理
  • 数据脱敏:日志中不能打印完整的银行卡号、身份证、token 等敏感字段
  • 传输安全:强制 HTTPS,校验证书(不要为了方便关 SSL 校验)

3.2 稳定性约束

  • 超时:连接超时(如 3s)+ 读取超时(如 10s)必须分开设置,不能用默认的无限等待
  • 重试:哪些错误可以重试(网络超时、5xx),哪些绝对不能重试(参数错误、余额不足)
  • 幂等:重试和回调都可能重复到达,必须用业务唯一键(订单号 / 请求 ID)保证幂等
  • 限流:尊重三方的 QPS 限制,本地做令牌桶或信号量隔离

3.3 架构约束

  • 不直接暴露三方 API 给前端:所有三方调用必须经过后端代理
  • 不把三方 SDK 直接侵入业务层:用 Adapter 模式隔离,业务层依赖自己定义的接口
  • 不共享三方连接:每个三方服务独立的连接池 / HTTP 客户端,避免互相影响

四、Done Criteria:什么状态算 "对接完成"

很多项目 "联调通过" 就上线了,然后线上各种问题。完整的完成标准应该包括:

4.1 功能验收

  • 主流程(成功场景)端到端跑通
  • 三方返回的每一类错误码都有对应处理(不是统一 catch 后报 "系统异常")
  • 回调 / 通知场景覆盖:正常回调、重复回调、伪造回调(验签失败)
  • 超时场景:三方响应慢时,本地有超时降级而非线程阻塞

4.2 非功能验收

  • 超时配置已生效(可通过 Mock 延迟验证)
  • 重试不会导致重复扣款 / 重复下单(幂等验证)
  • 三方不可用时,服务不会雪崩(熔断 / 降级生效)
  • 密钥可以在不重启服务的情况下更新

4.3 可观测性

  • 每次三方调用都有日志:请求时间、耗时、状态、错误码、traceId
  • 有监控指标:调用量、成功率、平均耗时、P99 耗时
  • 有告警:成功率下降、耗时突增、错误码集中

五、具体的代码结构设计

用 Adapter 模式隔离三方依赖,这是最关键的架构决策:

业务层(OrderService)
    ↓ 依赖
内部接口(PaymentGateway)
    ↓ 实现
三方适配器(AlipayPaymentGateway)
    ↓ 调用
三方SDK / HTTP Client

为什么这样设计:

  1. 业务层不认识AlipayClient、WxPayService这些三方类,只认识自己定义的PaymentGateway接口
  2. 以后换支付渠道(支付宝→微信),只加一个适配器,业务代码零改动
  3. 单元测试时可以 Mock 内部接口,不需要启动三方 SDK

六、对接前的五问法(直接套用文章第十五节)

在写第一行代码之前,先回答这五个问题:

表格

问题示例回答
最终交付什么?一个PaymentGateway接口的支付宝实现,包含下单、查询、退款三个方法,以及回调处理 Controller
允许改哪里?新增infrastructure/alipay包和相关配置,订单服务注入新接口
明确不能改什么?不改现有订单状态机,不修改其他支付渠道代码,不把支付宝 SDK 暴露到业务层
怎么判断完成?沙箱环境全流程跑通,5 类异常场景测试通过,监控面板可看到调用指标
发现其他问题要不要处理?记录为技术债,本次不处理(如对账文件下载、分账接口)

七、常见的坑(对应文章中的 "错误放大")

三方 API 对接中,一个模糊点会被执行链放大成线上事故:

  1. "超时随便设一下" → 三方抖动时线程池被打满,整个服务雪崩
  2. "回调应该不会重复吧" → 网络抖动导致重复回调,订单被发货两次
  3. "错误码先统一处理" → 用户余额不足被显示成 "系统异常",客服电话被打爆
  4. "密钥先写配置里" → 代码提交到 Git,密钥泄露
  5. "日志先全打出来方便调试" → 敏感信息进日志,合规审计不过

总结

对接三方 API 时,真正昂贵的不是 "没人写代码",而是 "一个很能干的开发者,花了很长时间,把一个没定义清楚的对接做得非常漂亮 —— 然后线上出问题了。"

对接前花 30 分钟把 Goal、Scope、Constraints、Done Criteria 写清楚,比写 3 天代码然后返工划算得多。

例如对接下面的API

通过网盘分享的文件:天威认证平台对外接口文档(完整版)-V1.6.0(1).pdf
链接: https://pan.baidu.com/s/1JFJdJfCQ0KSejyrz6opKUg 提取码: 6666


一、先把对接任务定义清楚(Goal / Scope / Constraints / Done Criteria)

用文章的四要素,先把"对接天威诚信认证平台"这个模糊任务拆成可执行的Spec:

要素内容
Goal业务系统集成天威诚信CA能力,实现:个人/机构用户实名认证 → 云证书签发 → 基于托管私钥的电子签名/数据解密。首期覆盖证书申请 + 签名主流程
Scope接入8个核心接口:getTemplateCodes、order/enroll、order/getDetail、cert/enroll、willingness/signContract、signing/create、file/upload、回调接收。不接续期、撤销、密钥恢复、证据查询、PIN码、企业授权、大B API模式
Constraints① 请求签名必须用 HMAC-SM3(国密,不是HMAC-SHA256);② 回调验签用 HMAC-SHA1(和请求签名算法不一样,这是大坑);③ HTTP回调时回调体是 SM4加密的,需要解密;④ 回调必须 3秒内返回200;⑤ 所有图片≤2M,二进制Base64传输;⑥ 证书签发前必须确认工单状态为PASS
Done Criteria① 测试环境完整跑通"实名认证→证书签发→意愿认证→数据签名"全链路;② 回调重复通知幂等处理验证通过;③ 三方超时/5xx时本地降级不阻塞;④ 密钥可通过配置中心热更新;⑤ 监控面板可看到调用量/成功率/耗时/错误码分布

二、整体架构设计(Adapter 隔离三方依赖)

┌─────────────────────────────────────────────────────┐
│                   业务层 (OrderService)               │
│         只认识内部接口,不认识任何天威诚信类           │
└──────────────────────┬──────────────────────────────┘
                       │ 依赖
┌──────────────────────▼──────────────────────────────┐
│            内部接口 (CertificationGateway)            │
│   applyCertificate() / signData() / decryptData()   │
└──────────────────────┬──────────────────────────────┘
                       │ 实现
┌──────────────────────▼──────────────────────────────┐
│         ITrusCertificationGateway (适配器)           │
│  ┌─────────────┐ ┌─────────────┐ ┌───────────────┐ │
│  │ 请求签名拦截器 │ │ 回调验签解密 │ │ 错误码映射器   │ │
│  │ (HMAC-SM3)  │ │ (HMAC-SHA1+ │ │ (status→异常) │ │
│  │             │ │  SM4)       │ │               │ │
│  └─────────────┘ └─────────────┘ └───────────────┘ │
└──────────────────────┬──────────────────────────────┘
                       │ HTTP POST
┌──────────────────────▼──────────────────────────────┐
│              天威诚信认证平台 (三方)                   │
└─────────────────────────────────────────────────────┘

为什么必须这样分层:

  • 业务层代码里不能出现 HMAC-SM3、orderSn、certRequestUniqueId 这些三方概念
  • 以后如果换CA厂商(比如换成CFCA、数字认证),只加一个适配器实现,业务代码零改动
  • 单元测试时Mock内部接口即可,不需要启动三方SDK

三、安全设计(最容易踩坑的部分)

3.1 请求签名:HMAC-SM3

文档明确要求:以 appSecretKey 作为 HMAC 密钥,对请求体原始字节数组进行 HMAC-SM3 计算,结果 Base64 编码。

// 关键点:用国密算法,不是标准JDK的HmacSHA256
// 推荐用 Hutool 的 SmUtil 或 BouncyCastle
public class ITrusSignInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                         ClientHttpRequestExecution execution) throws IOException {
        // 1. 对请求体原始字节做 HMAC-SM3
        byte[] signatureBytes = SmUtil.hmacSm3(secretKey.getBytes(UTF_8)).digest(body);
        String signature = Base64.encode(signatureBytes);

        // 2. 组装请求头
        request.getHeaders().add("appId", appId);
        request.getHeaders().add("Content-Signature", "HMAC-SM3 " + signature);
        request.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        return execution.execute(request, body);
    }
}

注意事项:

  • 签名内容是请求体原始字节数组,不是JSON字符串再getBytes——必须确保序列化后的字节和签名用的字节完全一致(建议先序列化成byte[],再用这个byte[]既签名又发请求)
  • 国密算法JDK原生不支持,需要引入依赖:cn.hutool:hutool-crypto(内置BouncyCastle)或直接用org.bouncycastle:bcprov-jdk15on

3.2 回调验签:HMAC-SHA1(和请求签名算法不一样!)

文档原文:"header 头使用HMAC-SHA1协议,采用appKey生成,使用base64编码"

// 回调Controller
@PostMapping("/callback/itrus")
public Map<String, Object> handleCallback(
        @RequestHeader("Content-Signature") String signatureHeader,
        @RequestBody String rawBody) {

    // 1. 验签:HMAC-SHA1,注意是SHA1不是SM3!
    String expectedSignature = "HMAC-SHA1 " + 
        Base64.encode(HmacUtil.hmacSha1(secretKey.getBytes(), rawBody.getBytes(UTF_8)));
    if (!signatureHeader.equals(expectedSignature)) {
        log.warn("回调验签失败, orderSn={}", extractOrderSn(rawBody));
        return Map.of("status", 0, "msg", "signature invalid");
    }

    // 2. 如果回调地址是HTTP,body是SM4加密的,需要先解密
    // 解密逻辑:SM3(SecureKey + orderSn) 取前16字节为SM4密钥,SM4_CBC解密
    // ... (见3.3)

    // 3. 幂等处理:根据orderSn去重
    // 4. 异步处理业务,3秒内必须返回
    callbackEventPublisher.publish(rawBody);
    
    return Map.of("status", 1, "code", 0, "msg", "success");
}

3.3 回调解密:SM4(仅HTTP回调时)

文档的加密流程:

  1. 读取 SecureKey 和认证流水号(orderSn)
  2. SM3(SecureKey + orderSn) → 取前16字节作为SM4密钥
  3. 生成SM4 IV
  4. SM4_CBC 加密 → Base64编码
public String decryptCallbackBody(String encryptedBase64, String secureKey, String orderSn) {
    // 1. 派生SM4密钥:SM3(SecureKey + orderSn) 取前16字节
    byte[] keyMaterial = SmUtil.sm3(secureKey + orderSn).getBytes(UTF_8);
    byte[] sm4Key = Arrays.copyOf(keyMaterial, 16);
    
    // 2. 解密(IV通常在加密数据中携带,或按文档约定生成)
    // 具体IV生成方式需要和天威诚信确认,文档中写的是"生成SM4 IV"
    SymmetricCrypto sm4 = SmUtil.sm4Cbc(sm4Key, ivBytes);
    return new String(sm4.decrypt(Base64.decode(encryptedBase64)), UTF_8);
}

建议:回调地址直接用HTTPS,就不需要处理SM4解密,少一个出错点。 文档明确说"如果业务系统部署HTTPS,认证系统回调时推送明文信息"。


四、核心流程设计(证书申请 + 签名 完整时序)

这是最核心的业务链路,对应文档6.2.2节,共7步:

用户          浏览器         业务系统(你)           天威诚信平台
 │              │               │                      │
 │──1. 发起认证──>│               │                      │
 │              │──2. 请求认证页─>│                      │
 │              │               │──3. getTemplateCodes─>│  (查询可用模板)
 │              │               │<──4. 模板列表─────────│
 │              │               │                      │
 │              │               │──5. order/enroll────>│  (创建认证工单)
 │              │               │<──6. orderSn+certUrl─│
 │              │<──7. 302跳转到认证页──────────────────│
 │<──8. 展示认证页──────────────────────────────────────│
 │              │               │                      │
 │──9. 填写身份+人脸/短信认证──>│                      │
 │              │               │                      │
 │              │               │<──10. 回调(PASS)──────│  ★ 异步回调
 │              │               │   (验签→幂等→存库)    │
 │              │               │                      │
 │              │<──11. 轮询/跳转returnUrl─────────────│
 │              │──12. 查询结果──>│                      │
 │              │               │──13. order/getDetail─>│  (确认工单PASS)
 │              │               │<──14. 工单详情────────│
 │              │               │                      │
 │              │               │──15. cert/enroll─────>│  ★ 证书签发
 │              │               │<──16. 证书(buf/certSn)│
 │              │               │   (存证书信息)         │
 │              │               │                      │
 │              │<──17. 引导签署意愿认证────────────────│
 │              │               │──18. willingness/signContract─>│
 │              │               │<──19. 意愿认证页URL───│
 │<──20. 展示意愿认证页(短信/人脸)──────────────────────│
 │              │               │                      │
 │              │               │<──21. 意愿认证回调────│
 │              │               │   (获取signId+token)  │
 │              │               │                      │
 │              │<──22. 待签署文件─────────────────────│
 │──23. 确认签署──>│               │                      │
 │              │               │──24. signing/create──>│  ★ 应用数据签名
 │              │               │   (传signId+token+hash)│
 │              │               │<──25. 签名结果(P7)────│
 │<──26. 返回签署完成───────────────────────────────────│

关键设计决策:

  1. Step 5 创建工单时必须传 metaJson:把你的业务ID(如userId、orderNo)放进去,回调时原样回传,这样你才能把回调和业务关联起来。不要依赖orderSn作为业务关联键,orderSn是三方生成的。
  2. Step 13 工单查询是兜底:回调可能延迟或丢失(虽然有5次重试),前端跳转returnUrl后必须主动调用getDetail确认最终状态,不能只靠回调。
  3. Step 15 证书签发有前置条件:必须确认工单状态为PASS(或AUTO_APPROVED/MANUAL_APPROVED)才能调用cert/enroll。如果是AUDITING状态,需要等待审核,不能直接签发。
  4. Step 18 意愿认证的 certUsageType:签名传SIGN,解密传DECRYPT,这个字段决定了后续返回的是signId还是decryptId。
  5. Step 24 签名接口传的是文件hash,不是文件本身:文档说"业务侧传入的文件hash字符串,认证平台原样存储并返回,不解释其编码格式"。所以你需要在本地计算文件hash(建议SM3),然后传给平台。

五、回调处理设计(最容易出线上事故的部分)

文档明确要求:

  • 必须 3秒内返回 HTTP 200
  • 重试机制:最多5次,间隔 1, 2, 4, 8, 16分钟(指数退避)
  • 5次都失败后不再重试(意味着你会永久丢失这个回调)
@PostMapping("/callback/itrus")
public Map<String, Object> handleCallback(
        @RequestHeader(value = "Content-Signature", required = false) String signature,
        @RequestBody(required = false) String rawBody) {
    
    // 1. 快速失败:验签不通过直接返回,不进入业务逻辑
    if (!verifySignature(signature, rawBody)) {
        return Map.of("status", 0, "msg", "invalid signature");
    }

    // 2. 解析出orderSn(用于幂等和日志)
    CallbackEvent event = parseCallback(rawBody);
    String orderSn = event.getData().getOrderSn();

    // 3. 幂等:用Redis SETNX 或 数据库唯一索引
    //    key = "itrus:callback:" + orderSn + ":" + event.getData().getOrderStatus()
    if (!callbackIdempotentService.tryLock(orderSn, event.getOrderStatus())) {
        log.info("回调重复, orderSn={}, status={}", orderSn, event.getOrderStatus());
        return Map.of("status", 1, "code", 0, "msg", "success"); // 重复也返回成功
    }

    // 4. 持久化原始回调(用于审计和问题排查)
    callbackLogService.saveRaw(orderSn, rawBody);

    // 5. 异步处理业务(发布事件,不阻塞回调线程)
    //    业务处理包括:更新工单状态、触发证书签发、通知前端等
    applicationEventPublisher.publishEvent(new ITrusCallbackEvent(event));

    // 6. 必须在3秒内返回成功
    return Map.of("status", 1, "code", 0, "msg", "success");
}

回调状态机处理:

回调状态业务动作
PASS认证通过 → 触发证书签发(如果是自动签发模式)或通知用户
AUDITING审核中 → 更新状态,等待后续回调,不触发签发
REJECT审核拒绝 → 更新状态,记录拒绝原因(在remark字段),通知用户
EXPIRED已过期 → 更新状态,通知用户重新发起
REJECT_EXPIRED审核拒绝已过期 → 同REJECT

注意:同一个orderSn可能收到多次回调(比如先AUDITING后PASS),幂等key必须包含状态,不能只按orderSn去重。


六、错误处理与重试策略

6.1 三方响应错误码处理

文档统一响应格式:{status: 1, msg: "success", data: {}},status≠1即为失败。

错误码分类处理策略:

错误类型示例错误码处理策略
参数错误10001, 10103, 20169, 20170, 20176不重试,直接抛出业务异常,记录请求参数用于排查
业务状态错误30016(重复签发), 31018(证书不可续期), 31022(已绑定)不重试,映射成具体业务异常(如"证书已签发")
权限/配置错误31002, 31003, 31020, 31026, 31028不重试,告警通知运维检查appId配置和白名单
系统错误/网络错误5xx, 连接超时, 读取超时重试,最多3次,指数退避(1s, 2s, 4s)
限流(如果有429或特定错误码)重试,退避时间更长(5s, 10s, 20s)
public class ITrusErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        ITrusResponse<?> body = parseBody(response);
        int status = body.getStatus();
        
        if (status == 1) return null; // 成功
        
        // 参数错误 → 不重试
        if (status >= 10000 && status < 20000) {
            throw new ITrusParamException(status, body.getMsg());
        }
        // 业务状态错误 → 不重试
        if (status >= 30000 && status < 32000) {
            throw new ITrusBizException(status, body.getMsg());
        }
        // 其他 → 可重试
        throw new RetryableITrusException(status, body.getMsg());
    }
}

6.2 HTTP 超时配置

// 连接超时:3秒(建立TCP连接)
// 读取超时:10秒(等待响应)
// 注意:天威诚信的某些接口(如人脸认证)可能响应较慢,
//       但大部分接口应该在5秒内返回
@Bean
public RestTemplate itrusRestTemplate() {
    HttpComponentsClientHttpRequestFactory factory = 
        new HttpComponentsClientHttpRequestFactory();
    factory.setConnectTimeout(3000);
    factory.setReadTimeout(10000);
    factory.setConnectionRequestTimeout(3000);
    
    RestTemplate template = new RestTemplate(factory);
    template.setInterceptors(List.of(new ITrusSignInterceptor()));
    template.setErrorHandler(new ITrusResponseErrorHandler());
    return template;
}

七、配置与密钥管理

itrus:
  # 环境切换:测试用demo地址,正式用eaivc地址
  base-url: https://demo-eaivc.itrus.com.cn/apigate/platform-eaivc
  # 密钥从配置中心/密钥管理服务读取,不写在代码里
  app-id: ${ITRUS_APP_ID}
  secret-key: ${ITRUS_SECRET_KEY}
  # 回调SM4解密用的SecureKey(仅HTTP回调时需要)
  secure-key: ${ITRUS_SECURE_KEY}
  # 认证模板编码(在天威诚信平台注册应用后分配)
  template-code: ${ITRUS_TEMPLATE_CODE}
  # 超时配置
  connect-timeout: 3000
  read-timeout: 10000
  # 重试配置
  max-retry: 3
  retry-backoff: 1000
  # 回调配置
  callback-url: https://your-domain.com/api/callback/itrus
  return-url: https://your-domain.com/cert/return

密钥管理要点:

  • appId和secretKey必须通过配置中心(如Nacos/Apollo)或密钥管理服务(KMS)注入,不能硬编码
  • 支持密钥热更新:天威诚信平台支持密钥轮换,更新后不需要重启服务
  • secretKey是请求签名(HMAC-SM3)和回调验签(HMAC-SHA1)的共同密钥
  • secureKey仅用于HTTP回调的SM4解密,如果回调地址用HTTPS则不需要这个

八、可观测性设计

8.1 日志规范

// 每次三方调用必须记录:traceId、接口名、请求体(脱敏)、响应状态、耗时、错误码
public class ITrusLogInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                         ClientHttpRequestExecution execution) throws IOException {
        long start = System.currentTimeMillis();
        String apiPath = request.getURI().getPath();
        String traceId = MDC.get("traceId");
        
        try {
            ClientHttpResponse response = execution.execute(request, body);
            long cost = System.currentTimeMillis() - start;
            
            // 记录成功日志:请求体要脱敏(证件号、手机号、银行卡号)
            log.info("ITRUS调用成功, api={}, cost={}ms, status={}, traceId={}", 
                apiPath, cost, response.getStatusCode(), traceId);
            
            // 指标上报
            metrics.recordSuccess(apiPath, cost);
            return response;
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - start;
            log.error("ITRUS调用失败, api={}, cost={}ms, error={}, traceId={}", 
                apiPath, cost, e.getMessage(), traceId);
            metrics.recordFailure(apiPath, cost);
            throw e;
        }
    }
}

敏感字段脱敏清单:

  • 证件号(idNumber/idNo):保留前3后4
  • 手机号(mobile):保留前3后4
  • 银行卡号(bankCardNo):保留前4后4
  • 人脸图片(imgBase64):不打印,只记录长度
  • 证书buf(buf/bufP7):不打印,只记录长度

8.2 监控指标

指标说明告警阈值
itrus_api_request_total按接口名+状态码计数-
itrus_api_request_duration_seconds按接口名的耗时直方图P99 > 10s
itrus_api_error_rate按接口名的错误率> 5% 持续3分钟
itrus_callback_received_total回调接收量(按状态)-
itrus_callback_verify_failed_total回调验签失败量> 0 立即告警
itrus_callback_process_failed_total回调业务处理失败量> 0 告警
itrus_cert_issue_pending待签发证书数(工单PASS但未签发)> 0 持续10分钟

九、测试策略

9.1 单元测试(不依赖三方)

  • 签名工具类测试:给定appSecret和请求体,验证HMAC-SM3签名结果和文档示例一致
  • 回调验签测试:模拟回调请求,验证HMAC-SHA1验签逻辑
  • 回调解密测试:模拟SM4加密的回调体,验证解密逻辑
  • 错误码映射测试:给定各种status,验证映射到正确的异常类型
  • 幂等测试:重复回调同一orderSn+状态,验证只处理一次

9.2 集成测试(测试环境)

  • 主流程联调:完整跑通"创建工单→模拟用户认证→回调→证书签发→意愿认证→数据签名"
  • 回调异常测试:
    • 回调延迟5秒返回 → 验证平台重试机制
    • 回调返回非200 → 验证平台重试(1,2,4,8,16分钟)
    • 重复回调 → 验证幂等
    • 伪造签名回调 → 验证验签拒绝
  • 超时测试:Mock三方接口延迟15秒 → 验证本地10秒超时触发
  • 证书状态测试:AUDITING状态下调用cert/enroll → 验证被拒绝并给出友好提示

9.3 契约测试

  • 用WireMock模拟天威诚信平台的各个接口响应
  • 确保你的请求格式(headers + body)和文档完全一致
  • 特别是签名头:Content-Signature: HMAC-SM3 {base64}

十、常见坑和注意事项(基于文档细节提炼)

#坑说明规避方式
1请求签名和回调签名算法不一样请求用HMAC-SM3,回调用HMAC-SHA1,很多人统一用一个算法导致验签失败分别实现两个签名方法,命名明确区分
2HTTP回调体是SM4加密的文档说"回调接口为http时,返回加密的回调结果"回调地址直接用HTTPS,跳过解密
3certRequestUniqueId的作用创建工单时传的这个ID,后续证书签发时必须用同一个业务系统生成并持久化,和orderSn关联存储
4认证页面URL只有1分钟有效期文档写"expireTime默认1分钟"用户拿到URL后必须立即跳转,不能存着慢慢用
5高级证书必须双录错误码30223"高级证书必须选择双录认证方式"如果用基础级证书(BASIC),不需要双录;确认模板配置的证书等级
6authMethod和模板取交集传的认证方式必须和模板配置有交集,否则拒绝(20176)先调getTemplateCodes查模板支持的认证方式,再传authMethod
7回调5次重试后永久丢失1,2,4,8,16分钟后不再重试回调处理必须高可用,同时前端轮询getDetail作为兜底
8签名接口传hash不传文件"业务侧传入的文件hash字符串,认证平台原样存储并返回"本地计算文件SM3 hash,传给平台;平台不验证hash和文件的对应关系
9idType/orgIdType是字符串不是数字V1.3.0.2改造:"由数字改为字符串编码"证件类型用字符串"0"(身份证),不是数字0
10metaJson是字符串不是JSON对象文档定义metaJson是String类型,回调时原样传回传入时要JSON序列化后作为字符串,接收时再反序列化
11证书签发不是自动的工单PASS后,需要业务系统主动调用cert/enroll签发回调PASS后,在异步处理中调用cert/enroll,不要等
12图片限制2M"所有图片数据限制在2M以内"上传前压缩/校验图片大小,超了先压缩

十一、落地建议(分阶段实施)

第一阶段(1-2周):核心链路打通

  • 实现请求签名拦截器(HMAC-SM3)
  • 接入3个接口:order/enroll、order/getDetail、cert/enroll
  • 实现回调接收(验签+幂等+异步处理)
  • 测试环境跑通"认证→签发"主流程

第二阶段(1周):签名能力上线

  • 接入willingness/signContract和signing/create
  • 实现意愿认证回调处理(获取signId+token)
  • 完整跑通"认证→签发→意愿认证→签名"全链路

第三阶段(1周):生产就绪

  • 完善监控告警、日志脱敏
  • 异常场景测试(超时、重试、回调丢失)
  • 密钥管理接入配置中心
  • 灰度上线

总结

回到你发的那篇文章的核心观点:AI越强,Task Specification越重要。对接这个CA平台也是一样——

  • 如果你只说"把天威诚信的接口接上",开发者可能会把30个接口全接了,包括你根本用不到的续期、撤销、密钥恢复、证据查询、PIN码、企业授权……
  • 但如果你按上面的Spec来:首期只接8个接口,覆盖证书申请+签名主流程,请求用HMAC-SM3、回调用HMAC-SHA1、回调地址用HTTPS跳过SM4解密、回调3秒内返回+幂等+异步处理——开发空间立刻缩小,不会跑偏。

对接三方API真正的工作量,不在写调用代码,而在把签名算法、回调机制、错误码、状态机这些"边界条件"定义清楚。 上面的设计已经把这份文档里最关键的坑都标出来了,你可以直接照着落地。

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐