实现你的服务
- 状态
- 生效中
- 更新
- 2026-09-13
- 适用范围
- API Platform 提供方实现与一致性检查
你的服务需要暴露四个入口。平台调用它们,你返回一个固定形状的信封。
本页讲怎么实现,以及提交通道后平台会检查什么、每条不通过意味着什么。
四个入口
GET /health/live 进程活着
GET /health/ready 自身配置与依赖可用
POST /v1/executions 执行一次调用
POST /v1/reconciliations 查询某次调用的真实结局
健康检查不要求凭据。另外两个必须校验平台带来的 Authorization: Bearer 凭据——那把凭据只属于你这一条通道,泄露它等于把执行入口对外开放。
就绪检查表示你自己可用,不要在里面反查平台。平台不可用时你无需也不应报告未就绪。
执行
POST /v1/executions
Authorization: Bearer <平台配置给你的凭据>
Idempotency-Key: <平台生成的稳定标识>
Content-Type: application/json
{ "payload": { "你在商品契约里定义的字段": "值" } }
payload 是一个非空 JSON 对象,内容由你的商品契约决定。信封本身只有这一个顶层字段,出现未知顶层字段必须拒绝。
返回信封
无论成功失败,都返回同一个形状:
{
"outcome": "SUCCEEDED",
"correlationId": "你自己的任务号",
"result": { "业务结果": true },
"errorCode": "",
"httpStatus": 0
}
不适用的字段省略。四种结局,HTTP 状态码必须与之匹配:
| outcome | HTTP 状态 | 含义 | 平台会怎么做 |
|---|---|---|---|
SUCCEEDED | 任意 2xx | 业务已完成且结果已持久化 | 结算扣费 |
DEFINED_FAILURE | 与 httpStatus 相同的 4xx/5xx | 业务确定失败 | 释放冻结的积分,不扣费 |
RETRYABLE_BEFORE_ACCEPT | 精确 503 | 你能确证尚未受理这次调用 | 有限次重发 |
UNKNOWN_OUTCOME | 精确 202 | 可能已受理,结局未定 | 只对账,绝不重发 |
DEFINED_FAILURE 需要一个稳定的 errorCode(不超过 64 字符)。
不要用 DEFINED_FAILURE 表达基础设施的不确定。 它的含义是「这次业务请求确定失败了」,平台据此释放积分;如果实际上你已经执行了业务,这笔就白做了。不确定时用 UNKNOWN_OUTCOME。
超时、连接中断、非法 JSON、超长响应、状态码与 outcome 不匹配、跳转——平台一律按 UNKNOWN_OUTCOME 处理。所以凡是你已经受理的调用,都必须能通过对账查到。
幂等
平台每次调用带一个稳定的 Idempotency-Key。你需要:
- 对收到的
payload原始字节算一个指纹,和这个 key 绑定存起来; - 同 key 同指纹再来时,返回已存的结果,不要重新执行业务;
- 同 key 不同指纹时,返回
DEFINED_FAILURE,httpStatus与 HTTP 状态都用409,并且不修改原记录。
存储必须是持久的。进程重启后,同一个 key 的对账仍然要能返回之前受理的结果——内存里的 map 空了不等于没受理过。
顺序也重要:在执行业务副作用之前(或在同一个原子操作里)先落盘「已受理」标记,返回响应之前落盘最终结局,事务提交完成之后再写 HTTP 响应。
对账
POST /v1/reconciliations
{ "providerIdempotencyKey": "平台之前用过的那个 key" }
对账永远不启动业务执行,它只读你已经存下来的记录。
- 查到了:返回当前存着的结局。
- 查不到,且你能确证这个 key 从未被受理:
RETRYABLE_BEFORE_ACCEPT。 - 查不到,但存储不可用或你不确定:
UNKNOWN_OUTCOME。
查不到就返回成功是最危险的实现。 那等于凭空宣称完成了一次没发生的业务,平台会据此扣走调用方的积分。
提交后平台会检查什么
通道提交后,平台会调用你的端点跑一次契约一致性检查,结果直接显示在你的通道页面上。你可以自己反复运行——改实现、重跑、看哪条还红,不必等任何人。
检查会在你的服务上产生一次真实调用。它使用与正式调用不同的 Idempotency-Key 前缀(ekko-conformance:),你可以据此识别;这次调用不产生回执、不冻结也不扣任何积分。
检查不了解你的业务:它发一个明确无意义的 payload,SUCCEEDED 和 DEFINED_FAILURE 都算合格。它验的是信封形状、幂等语义和凭据校验,不是业务成功——你能用带 errorCode 的 DEFINED_FAILURE 干净地拒绝垃圾输入,本身就是合规的证明。
九项检查
| 检查 | 不通过意味着 |
|---|---|
| 存活探测 | /health/live 不可达或非 2xx |
| 就绪探测 | /health/ready 不可达或非 2xx。注意它不该依赖平台 |
| 执行面校验凭据 | 不带凭据也能调用你的执行入口。 任何人都能直接驱动你的业务 |
| 执行响应信封合规 | 返回的 outcome 不在四种之内,或 HTTP 状态与 outcome 不匹配 |
| 同 key 重放稳定 | 同 key 同 payload 重放返回了不同结果,说明业务被重复执行了 |
| 同 key 换 payload 冲突 | 没有按 409 拒绝,说明幂等记录没有真正绑定 payload 指纹 |
| 拒绝未知 envelope 字段 | 你接受了契约之外的顶层字段。将来协议演进时,你会照单全收却不理解新字段 |
| 已知 key 可对账 | 刚受理过的 key 对账查不到,或返回了 RETRYABLE_BEFORE_ACCEPT——后者会让平台重复执行 |
| 未知 key 不谎报成功 | 从未提交过的 key 对账返回了 SUCCEEDED |
关于「已跳过」
某一项显示已跳过时,它既不是通过也不是失败,而是没有验过。
比如执行入口本身没跑通,后面三项幂等检查就没有可比对的基线,会全部跳过。这时不要去查幂等实现——先修好前面那一项,重跑之后它们才会给出真实结论。
检查与上架的关系
技术核验通过是上架的前置条件。检查未通过的通道不会被批准。
通道的地址或标识变更后,之前的检查结果失效,需要重新运行。
检查通过不等于已上架。 它只说明技术契约没问题;商品是否适合上架是另一道业务判断。
一份最小实现的骨架
以下用伪代码表达顺序,语言不限:
POST /v1/executions:
校验 Authorization,不通过 → 401
解析信封,出现未知顶层字段 → 400
fingerprint = 对 payload 原始字节取哈希
事务开始
record = 按 key 加锁读取
若 record 存在:
若 record.fingerprint != fingerprint → 409 DEFINED_FAILURE
否则 → 返回 record 里存着的结局 // 不重新执行
否则:
写入「已受理」标记(key, fingerprint)
事务提交
执行业务
持久化最终结局与 correlationId
返回响应
对账只做「按 key 读取并返回」,没有任何写入。
检查覆盖不到的部分
检查验的是信封形状、幂等语义与凭据校验。重试、超时、结果超限这类失败路径无法从外部触发,因此不在检查范围内——九项全绿不代表这些路径也对。它们同样是契约要求,仍需你自行保证。
一致性检查没有可下载的离线版本,提交通道后在通道页面触发即可。