接入
接入贷记闸门
每笔入金在贷记之前调用一次,带一个稳定的幂等键,并为每一种结果准备好分支。
先读 fail-closed 客户端契约。这一页是顺路径;那一页是顺路径不可用时保住你的那一页。
这次调用放在哪
放在「一笔入金在链上确认」与「用户余额发生变化」之间。不是之后。之后做的任何事都不是控制措施——回滚是道歉,不是闸门。
1 · 构造请求
{
"deposit_ref": "deposit-EXAMPLE-0001",
"chain": "example-chain",
"tx_hash": "0xEXAMPLEtxhash000000000000000000000000000000000000000000000000000",
"from_address": "0xEXAMPLEaddress0000000000000000000000001",
"to_address": "0xEXAMPLEaddress0000000000000000000000002",
"asset": "EXAMPLEUSD",
"amount_usd": "25000.00",
"user_ref": "user-EXAMPLE-0001",
"kyc_ref": "kyc-EXAMPLE-0001"
}
上面每一个值都是合成占位符,在任何链上都不成立。
amount_usd 刻意是十进制字符串,以免浮点丢精度。user_ref 与 kyc_ref 是你自己的标识符;Foreshock 不做也不评估 KYC,只把它们当引用存下来。
在冻结原语作用于 token account 而非钱包的链上,状态按 token account 计。同一钱包的两个 token account 可以处于不同状态,必须分别提交。
2 · 带一个稳定的幂等键发出
POST /v1/decide/deposit HTTP/1.1
Host: api.foreshock.org
Authorization: Bearer fsk_EXAMPLE_0000000000000000
Idempotency-Key: 22222222-2222-4222-8222-222222222222
Content-Type: application/json
键从 deposit_ref 派生,不要从尝试次数派生,这样这笔入金的每次重试都收敛到同一条已存储的裁决。
3 · 读裁决
{
"decision_id": "11111111-1111-4111-8111-111111111111",
"verdict": "HOLD",
"risk_level": "R1",
"pool": "quarantine",
"reasons": [
{ "code": "ACTIVE_ENFORCEMENT_RELATION", "severity": "R1" },
{
"code": "VENDOR_HIGH_RISK",
"vendor": "example-vendor",
"raw_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000003"
}
],
"vendor_disagreement": false,
"rule_version": "rules-EXAMPLE-2026.08.01",
"required_actions": ["COLLECT_SOURCE_OF_FUNDS", "L2_REVIEW"],
"sla_deadline": "2026-08-24T13:05:00Z",
"evidence_ref": "cas:sha256:EXAMPLE00000000000000000000000000000000000000000000000000000004",
"billable_units": 1
}
4 · 分支
verdict | 你的系统做什么 |
|---|---|
CREDIT | 贷记。把 decision_id 存在账本条目旁边。 |
HOLD | 不贷记。按 pool 路由资金。按 required_actions 开出待办,以 sla_deadline 计时。 |
REJECT | 不贷记。按 pool 路由。开案。 |
然后,无论裁决是什么,检查 Foreshock-Coverage-Health 响应头。为 degraded 时,转 GET /v1/coverage 取明细,并记录这条裁决是在可见性下降的情况下做出的。
5 · 路由到资金池,并且让池子彼此隔开
pool 取值 clean、tagged、quarantine。隔离本身就是全部意义所在:一个被扫进和其他资金同一个归集地址的 quarantine 分类,什么都没做到。required_actions 里的 QUARANTINE_ROUTE 是显式指令,但 pool 字段在每条裁决上都带着它。
6 · 完成待办动作,然后重新裁决
required_actions 是你的工作流要完成的步骤,不是我们执行的步骤。当挂起的原因是降级时,待办动作是 RE_DECIDE_AFTER_RECOVERY——降级恢复后再调一次这个接口。
那一次要用一个新的幂等键。你是在用新的输入问一个新问题;复用旧键会把陈旧的答案重放回来。
7 · 留住审计链
decision_id 用于从 GET /v1/decisions/{decision_id}/evidence 取证据包。rule_version 记录是你规则集的哪个版本产生了这个结果,因此这条裁决可以按当时生效的规则重放。
两者都存在账本条目旁边。裁决记录与快照存储,是这道闸门可审计而不只是「存在」的原因。
先跑影子模式
先上调用,再上执行:把每笔入金都过一遍闸门,记录裁决,照旧贷记,然后比对。跑一周,你就知道自己的挂起率、降级率,以及运营团队能不能吃下这个队列——在一个错误假设变成一个卡住的用户余额之前。