第5巻 処理方式設計書(1) ― 全体アーキテクチャと業務フロー
目次
第5巻 処理方式設計書(1) ― 全体アーキテクチャと業務フロー
本巻は
docs/specs/20_method_design.mdの序章〜第2章(本書の役割・機能別索引/全体アーキテクチャ/業務フロー〔正常系・例外系・障害系〕)を収める。続きは→第6巻。
序章 本書の役割・機能別索引
本書の役割:本書は処理方式設計書である。ZCが要件をどのアーキテクチャ・業務フロー・状態機械・整合性・運用で実現するかを定める。要件は 10_requirements.md、内部設計・I/F契約は 30_internal_design.md/別紙 31_schema.md・32_api_contracts.md を参照。ウォークスルーは 11_walkthrough.md を参照。
目次(TOC)
- 序章 本書の役割・機能別索引
- 第1章 全体アーキテクチャ
- 第2章 業務フロー(正常系・例外系・障害系)
- 第3章 取引ライフサイクルと状態遷移
- 第4章 メッセージング方式とI/F設計思想
- 第5章 冪等性・順序制御・再送制御
- 第6章 整合性モデルとファイナリティ設計
- 第7章 セキュリティ・認証・署名・否認防止
- 第8章 監査・ログ・証憑設計
- 第9章 障害時・災害時の動作
- 第10章 運用設計(監視・エスカレーション・事務対応/SLA・障害対応Runbook/CASE運用テンプレ)
- 第11章 ベンダ実装観点での妥当性整理
- 第12章 移行・切替・共存
- 第13章 ZC Communicator / ZC Adopter 導入モデル
- 第14章 参加要件・適合性評価
- 第15章 相互運用と段階導入ロードマップ
- 第16章 試験戦略
- 第17章 クロスカレンシーFX 処理方式
- 第18章 レガシー勘定系アダプタ 処理方式
機能別索引
- クロスカレンシーFX:要件=
10_requirements.md(問題定義・リスク/コンプラ)/処理方式=本書 第17章/内部設計=30_internal_design.md(スキーマ・API・既存コードへの接地) - レガシー勘定系アダプタ:要件=
10_requirements.md(分離理由・要求仕様)/処理方式=本書 第18章/内部設計=30_internal_design.md(冪等性・適合性・監査是正) - 継続収納(口座振替):要件=
10_requirements.md§3.2.8(三層構造・モード類型・費目・累計枠・追加認可・責任分界)/処理方式=本書 §2.2.7/スキーマ=31_schema.md/API=32_api_contracts.md
第1章 全体アーキテクチャ
要旨
本章では、Finality Logを唯一の正として据え、非同期配送とRead Model派生で全国規模の可用性・コストを両立する全体アーキテクチャを示す。多拠点合意(Raft)とRead-only縮退の考え方、データ分離、外部清算(DNS/IGS)との接続ポイントを整理する。
1.1 アーキテクチャの基本方針(規範)
- 正(Source of Truth) :ZCの Finality Log群(シャード単位の追記ログ) を唯一正とする。
- 通信 :ZC⇔参加行は非同期(Kafka/MQ等、at-least-once)。同期APIは「受付結果(受理/拒否)」までに限定。
- 状態 :Decision(ZC決定)とExecution(銀行実施確認)を分離し、Read Modelで分離表示する。
- レーン :
EXPRESS/STANDARD/BULK/DEFERRED/RTP/HTLC/HIGH_VALUEの 7 レーンを受付時の契約として分離し、SLA・運用・例外をレーン単位で固定する。加えて、多者協調GTIDはレッグを実体化する際に内部生成される第 8 のlane値、HTLC_AUTHは HTLC レーン上のフローである(区別は10_requirements.md序章「レーンとlane列の関係」を正とする)。 - 安全弁 :H(仕向超過限度)は 絶対超過禁止 。 H_reserved/H_locked をFinality Logで管理する。
1.2 コンポーネント構成
flowchart LR C["Client / Channel"] -->|sync| B["Participant Edge/API"] B --> IG["ZC Ingress / Validation"] IG --> LO["Lane Orchestrators"] LO --> FL1["Finality Log (Raft) Payer Shards"] LO --> FL2["Finality Log (Raft) GTID Shards"] LO --> BUS["Command/Event Bus (Kafka/MQ)"] BUS --> RMB["Read Model Builder"] RMB --> Q["Query API / Dashboard"] LO --> VA["Vault (short-lived)"] LO --> OPS["Ops Workflow / CASE"] LO --> TR["Trust Registry"] LO --> CAP["Express Capability Registry"] BUS -->|async cmd| AD["Bank Adapter / Connector"] AD --> CORE["Bank LMS/Core"] CORE --> AD AD -->|async event| BUS
1.3 多拠点・可用性(規範)
1.3.0 参照アーキテクチャ(全国システム:合意ログ+バックアップセンター永続化)
本システムの強みは、 Finality Log(Decision を記録する追記ログ)を単一の正(SoT) とし、これを 地理分散の合意(Raft/代替として Multi-Paxos) により確定させる点にある。
したがって「全国システムとしての永続化」は、一般論ではなく Decision の確定条件そのもの である。
固定点(規範)
Decisionは、当該シャードの 合意ログがコミット(quorum 到達) した時点でのみ成立する(以後、再起動・再構築でも揺らがない)。- 合意ログの投票メンバーは、 主系拠点とバックアップ拠点(DR)を跨いで配置 し、単一拠点喪失でも quorum を維持できること。
- quorum 喪失時は、誤決定を避けるため Read-only(照会のみ)へ縮退 し、新規 Decision は行わない(レーン別の拒否規範は §2.1・§9.2)。
参照配置(例)
- 3 拠点(東日本DC/西日本DC/DR)× 各拠点 1 投票ノード(合計 3)
- 1 拠点喪失でも 2/3 の quorum を維持
- 5 ノード構成の場合は、主系 2 拠点 + DR に跨る配置とし、常に 過半数が“地理的に分散” していることを保証
flowchart LR
subgraph East["東日本DC"]
E1["Voting Node (Shard k)"]
end
subgraph West["西日本DC"]
W1["Voting Node (Shard k)"]
end
subgraph DR["バックアップセンター(DR)"]
D1["Voting Node / Witness (Shard k)"]
end
E1 <-- "AppendEntries / Propose" --> W1
W1 <-- "AppendEntries / Propose" --> D1
D1 <-- "AppendEntries / Propose" --> E1
E1 --> Q["Commit (Quorum)"]
W1 --> Q
D1 --> Q
補足
合意方式の教科書的説明は行わず、本書では「 Decision は地理分散 quorum のコミットでのみ成立 」という規範を固定する。実装詳細(membership 変更、snapshot、log compaction 等)は
30_internal_design.md第16章(Raft運用)を正とする。
- 3地域×3AZ(例:東京・大阪・福岡×各3AZ)Active-Active を前提とし、任意の1地域喪失でも継続可能に設計する。
- Raftは シャードごと に構成し、過半数に到達できない場合は Read-onlyへ遷移 (両側書込み禁止)する。
- Express(POS用途)は Read-only時に原則REJECT(誤決定防止)。Standard/Bulkも 永続化不可(過半数喪失) の場合はREJECTし、参加行側での再送・繰越で吸収する。
1.4 データの分離(ログ/照会/秘匿)
- Finality Log :規範上の確定記録。監査・復旧の根拠。
- Read Model :照会用派生ビュー。破棄してもログから再構築できる。
- Vault :短期秘匿(AML評価等)。本体は削除しても、参照証跡(vault_ref、参照ハッシュ等)はログに残す。
1.5 高額即時レーン/IGS/DNSの整理(設計上の境界)
本基盤は、 取引の完了(顧客視点の完了) と、 参加主体間の資金精算(参加主体視点の精算) を混同しない。
特に「高額即時レーン」における清算は、日次ネット清算(DNS)とは別物であり、用語・状態・照会応答を明確に分離する。
1.5.1 IGS(Immediate Gross Settlement:高額即時清算)
- IGS = Immediate Gross Settlement(即時グロス清算) :高額即時レーンにおいて、ZCが 元取引の一部として 清算依頼を起動し、結果を前提条件として b を成立させる。
- 高額即時レーンの規範順序は a_HV → IGS確定 → b (事故抑止のため固定)。
- a_HV :支払銀行内で「顧客口座→清算専用中継勘定」へ資金を隔離した完了点(状態はaと同一、証憑の
proof_type=PAYER_HV_ISOLATION_PROOFで区別)。 - IGS確定 :清算サービス側での振替確定(
boj_settle_ref等で証跡化)。 - b :受取銀行での入金完了(弁済完了の外形)。
- a_HV :支払銀行内で「顧客口座→清算専用中継勘定」へ資金を隔離した完了点(状態はaと同一、証憑の
1.5.2 DNS(Daily Netting Settlement:日次ネット清算)
- DNS = Daily Netting Settlement(日次ネット清算) :低コスト・運用整合を両立する基盤の基本。ZCは当日取引群を日次でネッティングし、参加主体の ネット勝ち負け(純受取/純支払) を確定する。
- Kick(起動)はZCが実施(初回のみ) :営業日ごとに定めたカットオフ(例:16:30)で、ZCが当日分DNSサイクルを閉じ、 DNS清算依頼(ネット明細+digest) を発行する。
- 再処理(再Kick相当)は清算サービス側で自動 :資金手当て(市場調達/貸付)を最速に検知できる主体が清算サービス側であるため、HOLD解除後の再処理は清算サービス側で自動的に実行され、その結果のみがZCへ通知される。
- 中断(HOLD)の実体(内部要因) :参加主体が清算サービス側に保持する残高不足等により、DNS清算が完了できない状態として表現する。
- 事務必須の照会 :参加主体は当日DNSについて、ZC照会で以下を取得できなければ運用が回らない。
- 当日ネットポジション(純支払/純受取、金額)
- DNSサイクル状態(OPEN / KICKED / SETTLED / HOLD_ACTIVE)
- as_of(鮮度)/ watermark / next_action_hint(次アクション)
1.5.3 重要(顧客影響の整理:規範)
参照:本節は顧客影響の要旨。DNS HOLD/IGS HOLD 時のレーン別の照会応答の正準規範は §9.4.1.1 を正とする(本節と重複する詳細は同節に集約)。
- 高額即時レーン(IGS) :IGSは元取引の一部である。
- IGSがHOLDの場合、元取引は
SUSPENDED(b未成立)であり、顧客視点でも「未完了」として扱う。
- IGSがHOLDの場合、元取引は
- 通常レーン(Express/Standard/Bulk/RTP/HTLC/gtid) :DNSは元取引の完了後に実行される参加主体間精算である。
- DNSがHOLDの場合でも、元取引は
SETTLEDのままであり、顧客視点で「未完了」に見せてはならない。
- DNSがHOLDの場合でも、元取引は
設計規範(対外説明の安全性)
DNS HOLD/IGS HOLD を理由に、顧客向け画面で「資金不足」等の表現を用いてはならない。
ZCは
public_message_idを参加主体へ提示するのみで、顧客への直接表示は行わない。顧客向け表示は参加主体が行い、必要に応じて以下のようにぼかしたテンプレートを用いる。
例:「現在、一部のお振込処理が一時的に遅延しています」
第2章 業務フロー(正常系・例外系・障害系)
要旨
本章では、各レーンの正常系フローと、例外/障害時にどう状態化して収束させるかを図解する。同期応答の語義(INGRESS_ACCEPTED / DECISION_ACCEPTED)を固定し、取消と失敗、DNS_HOLD/IGS_HOLDと顧客影響の混同を防ぐ。
2.1 共通前提(規範)
設計規範(Read-only時の再送嵐防止)
Read-only(過半数喪失等)では永続化できないため、ZCは
REJECT_UNAVAILABLEを返す。ただし参加行が即時リトライすると再送嵐となるため、応答には必ず
retry_after_msとrecommended_backoff_policyを含め、参加行はCircuit Breakerに従う。
- 受付APIは 同期で受理/拒否 を返すが、 決定(Decision)と実施確認(Execution)は原則非同期 。
- 同期応答は2段階に分けて語義を固定する。
- INGRESS_ACCEPTED :入力が妥当で、かつ RECEIVED(受領記録)がFinality Logへ永続化済み であること(= 返した参照が消えない)。
ingress_refを返す。Decisionは未確定。 - DECISION_ACCEPTED : Decision(Raftコミット)まで完了 し、
decision_proof_refが発行済みであること(= 決定の立証が可能)。
- INGRESS_ACCEPTED :入力が妥当で、かつ RECEIVED(受領記録)がFinality Logへ永続化済み であること(= 返した参照が消えない)。
- Expressは原則
DECISION_ACCEPTEDを返す(店舗要件)。Standard/Bulk/Deferred/RTP/HIGH_VALUE はINGRESS_ACCEPTEDを返し、Decisionは後続イベント/照会で確定する。 - 専用入口を持つレーンは応答値も専用である(規範):HTLC・HTLC_AUTH・gtid は
POST /api/transfersを通らないため、同期応答は上記 2 語ではなく各エンドポイント固有の値を返す——HTLC はCREATED(POST /api/htlc/create)、HTLC_AUTH はAUTH_REQUESTED、gtid はGTID_ACCEPTED(POST /api/gtid/register、state: GT_RECEIVED)。語義(受理までを同期で返し Decision は未確定)はINGRESS_ACCEPTEDと同一であり、異なるのは値だけである。値の正は32_api_contracts.mdの各エンドポイント節とする。 - Read-only(シャード過半数喪失/基盤不可用)では永続化できないため、INGRESS_ACCEPTEDは返さず
REJECT_UNAVAILABLE/REJECT_READONLYを返す(参照番号なし、リトライ前提)。 - 外部照会待ち・窓口保留・高額即時レーン介在等の 業務都合の停止 は、INGRESS_ACCEPTED後に
PRECHECKED_SUSPENDED/SUSPENDEDへ遷移し、reason_codeとnext_action_hintで説明する。 - 顧客向け表示は a/b(第6章)を分離し、 取消(CANCELLED)と失敗(FAILED_EXECUTION)を混同しない 。
- 例外は CASE として起票し、番号で照会導線を固定する(第10章)。
監査上の狙い
同期応答の語義を固定し、「いつ受理し」「いつ決め」「いつ実施が確定したか」を時系列で説明できるようにする。
2.2 正常系フロー一覧(レーン別)
2.2.0 図解:基本送金類型(5類型)【規範補助図】
以下は、文章だけでは理解しづらい「典型フロー」を 5類型(Express / Standard / Bulk / HTLC / gtid) に整理したものである。
本図は 外部設計の理解補助 を目的とし、厳密な契約は I/F契約(30_internal_design.md 第12章(I/F契約))と状態機械(第3章) を正とする。
各シーケンス図中のメッセージ名(
PAYER_EXEC_REQUEST等の UPPER_SNAKE 略記やCMD/EVT接頭辞)は説明用の略記であり、cmd/event の正式名(PayerExecRequested等)と正本の一覧は30_internal_design.md§12.1 に従う(略記と正式名の対応表は §12.1.5)。
記号:
a=PAYER不可逆確定(PAYER_EXEC_CONFIRMED)/b=PAYEE不可逆確定(PAYEE_EXEC_CONFIRMED)
next_action_hintの予約(規範):next_action_hintは照会応答(GET /api/transactions/:txid)専用の閉じた 4 値であり、窓口の定型文言に 1:1 対応する(値域の正は30_internal_design.md§13.6)。cmd/event のペイロードが「次に何をすべきか」を運ぶ場合は別名(例:next_step)を用いる。同名で別値域を持ち込むと、窓口が「知らない hint を受け取る」経路が生まれ、文言固定という目的が壊れる。
(1) Express(店舗・即時課金:PSPR参照で宛先確定/受付結果を同期で返す)
sequenceDiagram autonumber participant POS as PayeePOS(加盟店端末) participant RB as PayeeBank(Edge/API) participant C as PayerClient(App) participant PB as PayerBank(Edge/API) participant Z as ZC(API/Orch) POS->>RB: PSPR_REGISTER(pspr_payload) RB-->>POS: pspr_ref(短寿命) POS-->>C: pspr_ref提示(QR/NFC等) C->>PB: TransferRequest(txid, lane=EXPRESS, pspr_ref) PB->>PB: 顧客認証/限度/与信/AML(高速)(参加主体責任) PB->>Z: TransferAccept(txid, lane=EXPRESS, pspr_ref, payee_bank_id) Z->>Z: PSPR署名検証 + Capability確認 Z->>Z: Precheck + H予約 Z->>Z: Decision確定(Raftコミット)+ decision_proof_ref採番 Z-->>PB: DECISION_ACCEPTED(decision_proof_ref)/REJECT(理由コード) ※同期 PB-->>C: 受付結果(参照番号) Z-->>PB: CMD PAYER_EXEC_REQUEST(txid, decision_proof_ref) PB-->>Z: EVT PAYER_EXEC_CONFIRMED(txid, bank_proof_ref) (a) Z-->>RB: CMD PAYEE_EXEC_REQUEST(txid, decision_proof_ref, pspr_ref) RB-->>Z: EVT PAYEE_EXEC_CONFIRMED(txid, bank_proof_ref) (b) Z->>Z: Finalize + ReadModel更新 POS->>RB: Query(txid) RB-->>POS: 入金可否/状態(個人情報は最小化)
要点(規範)
- Expressは 受付結果(Decisionまで)を同期 し、店舗導線での再試行コストを最小化する。
- POSが支払側銀行へ直結する設計は採らず、 支払人は自分の銀行アプリ等(Client)から起動 する。
- 名義照会の代替として、受取側が提示する 署名付き受取人情報(pspr_ref) を宛先の正とする。
- ただし 確定(a/b)は非同期 で進むため、照会で「今どこか/次に何をすべきか」を説明できることが必須。
(2) Standard(名義確認 → 支払人最終認可 → 実施:最も説明可能)
sequenceDiagram
autonumber
participant C as PayerClient(App)
participant PB as PayerBank(Edge/API)
participant Z as ZC(API/Orch)
participant RB as PayeeBank
participant A as Authority(AML/制裁)
C->>PB: DraftTransfer(payee_account, amount, purpose)
PB->>Z: TransferAccept(txid, lane=STANDARD, payee_account, amount)
Z->>Z: Precheck(形式)
Z-->>PB: INGRESS_ACCEPTED(txid, ingress_ref)/REJECT(理由コード) ※同期
Z-->>RB: CMD NAMECHECK_REQUEST(txid, payee_account)
RB-->>Z: EVT NAMECHECK_RESULT(txid, payee_name_masked, OK/NG, signed)
Z-->>PB: EVT NAMECHECK_PRESENT(txid, payee_name_masked, next_step=AUTHORIZE)
PB-->>C: 名義表示 + 最終認可UI
PB->>Z: CMD AUTHORITY_CHECK_REQUEST(txid, evidence)
Z-->>A: CMD AUTHORITY_CHECK(txid, evidence)
A-->>Z: EVT AUTHORITY_RESULT(txid, OK/NG, signed)
Z-->>PB: EVT AUTHORITY_PRESENT(txid, OK/NG)
alt OK
C->>PB: Authorize(txid)
PB->>Z: TransferAuthorize(txid, auth_sig)
else NG
Z-->>PB: REJECT(txid, reason=AUTHORITY_NG)
end
Z->>Z: H予約 + Decision確定(Raftコミット)+ decision_proof_ref採番
Z-->>PB: CMD PAYER_EXEC_REQUEST(txid, decision_proof_ref)
PB-->>Z: EVT PAYER_EXEC_CONFIRMED(txid, bank_proof_ref) (a)
Z-->>RB: CMD PAYEE_EXEC_REQUEST(txid, decision_proof_ref)
RB-->>Z: EVT PAYEE_EXEC_CONFIRMED(txid, bank_proof_ref) (b)
Z->>Z: Finalize + ReadModel更新
C->>PB: Query(txid)
PB-->>C: a/b + reason_code + 見通し(as_of/watermark)
要点(規範)
- Standardは 誤送金抑止 と 説明可能性 を最優先し、「名義確認の結果を見てから支払人が最終認可」する。
- 名義確認は 被仕向側(PayeeBank)の署名付き応答が正 。ZCは証跡を固定し、判断基準は参加主体責任。
TransferAuthorizeを設けることで、受理→名義確認→最終認可→Decision→実施、が一貫して追跡可能になる。
(3) Bulk/Deferred(締切ウィンドウ+LSM:名義確認→最終認可も可能にする)
flowchart TD
IN["BatchAccept<br/>(締切/優先度/金額/宛先一覧)"] --> PRE["前処理<br/>(形式検証 + 名義確認 + AML/制裁)" ]
PRE -->|OK| AUTH["BatchAuthorize<br/>(支払人最終認可/署名)" ]
PRE -->|NG| REJ["差戻し/却下<br/>(理由コード提示)" ]
AUTH --> Q[Window Queueing]
Q --> LSM["LSM最適化<br/>(同時実行集合探索)" ]
LSM -->|採択| RES[H予約コミット]
LSM -->|不採択/不足| DEFER[繰越/分割/優先度調整]
RES --> EXEC["実施要求(非同期)" ]
EXEC --> A["a成立(Payer実施証憑)" ]
A --> B["b成立(Payee実施証憑)" ]
B --> DONE[Finalize + ReadModel]
DEFER --> Q
LSM -. 失敗時 .-> FB["フォールバック<br/>(FIFO/期限優先/間引き)" ]
FB --> RES
要点(規範)
- Bulkは「最短確定」より、 締切までに最大数を成立させる ことが価値。
- 名義確認(宛先確認)は 前処理で実施可能 とし、差戻しを理由コードで説明できること。
- 支払人の最終認可は
BatchAuthorize(ファイル署名・承認ワークフロー等)として表現し、後日監査で立証できること。- LSMの採否はブラックボックスにせず、 採択理由(目的関数・制約・追跡情報)を証跡化 する。
- 典型的な確定時間の目安:ウィンドウ内で 数十秒〜数分 (締切・混雑に依存)。確定見込みは
next_action_hintとSLA分類で提示する。
(4) 条件付き決済(HTLC:hashlock+timelock:preimageは受取側が提示)
sequenceDiagram
autonumber
participant C as PayerClient
participant PB as PayerBank(Edge/API)
participant Z as ZC(API/Orch)
participant A as Authority(AML/制裁)
participant RB as PayeeBank
participant P as PayeeSystem
C->>PB: HTLC_CREATE(htlc_id, hashlock, timelock, amount, payee)
PB->>Z: HTLC_ACCEPT(htlc_id, hashlock, timelock, amount, payee)
Z-->>A: CMD AUTHORITY_CHECK(htlc_id, evidence)
A-->>Z: EVT AUTHORITY_RESULT(htlc_id, OK/NG, signed)
alt OK
Z->>Z: H予約 + HTLC_LOCKED(Raftコミット)
Z-->>PB: CREATED(htlc_id, hashlock)/REJECT(理由コード) ※同期
else NG
Z-->>PB: REJECT(htlc_id, reason=AUTHORITY_NG)
end
alt 条件成立(受取側がpreimage提示)
P->>RB: 提供完了 → preimage発行
RB->>Z: HTLC_CLAIM(htlc_id, preimage)
Z->>Z: preimage検証OK(hash一致)
Z-->>A: CMD AUTHORITY_RECHECK_IF_NEEDED(htlc_id, as_of)
A-->>Z: EVT AUTHORITY_RESULT(htlc_id, OK/NG, signed)
alt OK
Z->>Z: Decision確定(Raftコミット)+ decision_proof_ref採番
Z-->>PB: CMD PAYER_EXEC_REQUEST(txid, decision_proof_ref)
PB-->>Z: EVT PAYER_EXEC_CONFIRMED(txid, bank_proof_ref) (a)
Z-->>RB: CMD PAYEE_EXEC_REQUEST(txid, decision_proof_ref)
RB-->>Z: EVT PAYEE_EXEC_CONFIRMED(txid, bank_proof_ref) (b)
Z->>Z: Finalize
else NG
Z->>Z: DECIDED_CANCEL(理由=AUTHORITY_NG)
end
else 期限到来
Z->>Z: HTLC_EXPIRE → DECIDED_CANCEL(理由コード)
end
要点(規範)
- HTLCは「取消」ではなく 成立前の不確実性を構造化 する仕組み。
- preimageは 受取側(PayeeBank側)が提示 し、ZCは検証して「条件が満たされた」事実のみ証跡化する。
- Authority(AML/制裁)チェックは lock時に必須 。timelockが長い場合は claim時に再チェック し、経路上の規制変更・凍結指示に追随できること。
HTLC_LOCKED中の取引は DNS清算対象に入れない (条件未成立)。条件成立後に通常のa/bへ進む。- ZCはpreimageを永続保持しない(hashと検証証跡のみ)。timelock超過は 必ずDECIDED_CANCELで収束 する。
(5) gtid(多者協調グループ:Decision一体)
sequenceDiagram
autonumber
participant C as Client
participant PB as PayerBank(Edge/API)
participant Z as ZC(API/Orch)
participant R1 as PayeeBank#1
participant R2 as PayeeBank#2
participant A as Authority(AML/制裁)
C->>PB: GroupRequest(gtid, legs[])
PB->>Z: GroupAccept(gtid, legs[])
Z->>Z: leg前検証(形式/名義/上限)
Z->>Z: GT_H_RESERVED(gtid単位H予約)
Z-->>PB: GTID_ACCEPTED(gtid, state=GT_RECEIVED)/REJECT(理由コード) ※同期
PB-->>C: 受付結果(参照番号)
par 事前レディネス(協調の誤解防止)
Z-->>PB: CMD LEG_READY_REQUEST(gtid)
PB-->>Z: EVT LEG_READY_ACK(gtid, OK)
and
Z-->>R1: CMD LEG_READY_REQUEST(gtid)
R1-->>Z: EVT LEG_READY_ACK(gtid, OK)
and
Z-->>R2: CMD LEG_READY_REQUEST(gtid)
R2-->>Z: EVT LEG_READY_ACK(gtid, OK)
end
alt 全leg READY_OK
Z-->>A: CMD AUTHORITY_CHECK(gtid, legs_digest)
A-->>Z: EVT AUTHORITY_RESULT(gtid, OK/NG, signed)
Z->>Z: GT_DECIDED_TO_SETTLE(Raftコミット)+ decision_proof_ref採番
par leg#1
Z-->>PB: CMD PAYER_EXEC_REQUEST(gtid/leg1, decision_proof_ref)
PB-->>Z: EVT PAYER_EXEC_CONFIRMED(gtid/leg1, bank_proof_ref) (a)
Z-->>R1: CMD PAYEE_EXEC_REQUEST(gtid/leg1, decision_proof_ref)
R1-->>Z: EVT PAYEE_EXEC_CONFIRMED(gtid/leg1, bank_proof_ref) (b)
and leg#2
Z-->>PB: CMD PAYER_EXEC_REQUEST(gtid/leg2, decision_proof_ref)
PB-->>Z: EVT PAYER_EXEC_CONFIRMED(gtid/leg2, bank_proof_ref) (a)
Z-->>R2: CMD PAYEE_EXEC_REQUEST(gtid/leg2, decision_proof_ref)
R2-->>Z: EVT PAYEE_EXEC_CONFIRMED(gtid/leg2, bank_proof_ref) (b)
end
Z->>Z: GT_FINALIZE(全leg終端(bまたは救済)で確定)
else READY_NG / 期限到来
Z->>Z: GT_DECIDED_CANCEL(Raftコミット)+ reason_code
end
C->>PB: Query(gtid)
PB-->>C: leg別 a/b + 遅延理由 + 収束見込み
要点(規範)
- gtidは強力だが、無制限に適用すると制度・資金・説明責任の破綻を招くため、 受付規範(上限・レーン制約) を固定する。
- gtidの正は gtidシャードのFinality Log(=SoTの一部) 。各参加行は参照追記で整合する。
- 一部遅延はCASEへ接続し、 leg別に理由と見通しを出す ことを規範とする。
- GT_FINALIZE は「全legが終端した」ことを条件とし、終端は PAYEE_EXEC_CONFIRMED(b) または 救済確定(Reversal起票済) のいずれかとする(救済legをゾンビ化させない)。
(6) 高額即時レーン(High-Value Immediate:Standard拡張+中央銀行決済:a→中銀→b)
sequenceDiagram
autonumber
participant C as PayerClient(App)
participant PB as PayerBank(Edge/API)
participant Z as ZC(API/Orch)
participant RB as PayeeBank
participant A as Authority(AML/制裁)
participant ESA as ExternalSettlementAdapter
participant BOJ as 中央銀行(決済)
C->>PB: DraftTransfer(payee_account, amount, purpose)
PB->>Z: TransferAccept(txid, lane=HIGH_VALUE, payee_account, amount)
Z->>Z: 受付規範チェック(閾値/用途/上限)
Z-->>RB: CMD NAMECHECK_REQUEST(txid, payee_account)
RB-->>Z: EVT NAMECHECK_RESULT(txid, payee_name_masked, OK/NG, signed)
Z-->>PB: EVT NAMECHECK_PRESENT(txid, payee_name_masked, next_step=AUTHORIZE)
PB-->>C: 名義表示 + 最終認可UI
Z-->>A: CMD AUTHORITY_CHECK(txid, evidence)
A-->>Z: EVT AUTHORITY_RESULT(txid, OK/NG, signed)
alt OK
C->>PB: Authorize(txid)
PB->>Z: TransferAuthorize(txid, auth_sig)
Z->>Z: Decision確定(Raftコミット)+ decision_proof_ref採番
Z-->>PB: CMD PAYER_EXEC_REQUEST(txid, decision_proof_ref, settlement=IGS)
PB-->>Z: EVT PAYER_EXEC_CONFIRMED(txid, bank_proof_ref{proof_type=PAYER_HV_ISOLATION_PROOF}) (a_HV)
Note over Z: proof_type=PAYER_HV_ISOLATION_PROOF の場合のみ IGS を起動(§1.5.1)
Z->>ESA: BOJ_SETTLE_REQUEST(txid, decision_proof_ref, a_proof_ref)
ESA->>BOJ: 決済依頼(冪等 ext_instruction_id)
alt 中銀決済確定
BOJ-->>ESA: 決済結果(SETTLED)
ESA-->>Z: EVT CB_SETTLED(txid, ext_instruction_id, boj_settle_ref)
Z-->>RB: CMD PAYEE_EXEC_REQUEST(txid, decision_proof_ref, boj_settle_ref)
RB-->>Z: EVT PAYEE_EXEC_CONFIRMED(txid, bank_proof_ref) (b)
Z->>Z: Finalize + ReadModel
else 未了/照会待ち
BOJ-->>ESA: 未了(照会待ち/保留)
ESA-->>Z: EVT BOJ_PENDING(txid, ext_instruction_id)
Z->>Z: SUSPENDED + next_action_hint(照会/再照合)
end
else NG
Z-->>PB: REJECT(txid, reason=AUTHORITY_NG)
end
要点(規範)
- 高額即時レーンは Standardの拡張 であり、名義確認・AML/制裁スクリーニング・支払人最終認可を必須とする。
- a_HV の識別(規範) :支払銀行が返す a の証憑は
proof_type=PAYER_HV_ISOLATION_PROOF(顧客口座→清算専用中継勘定への資金隔離完了)でなければならない。ZC はこの proof_type を受領した場合にのみ中銀決済(IGS)を起動する。外形上の状態はPAYER_EXEC_CONFIRMED(a) と同一であり、区別は proof_type だけが担う(§1.5.1、10_requirements.md§1.2.3、proof_typeの値域は30_internal_design.md§12.3.3)。- H(仕向超過限度)の適用除外:高額即時レーンは即時グロス決済(RTGS/IGS)であり、DNS(ネット清算)のリスク管理枠である H を消費しない。したがって、受付時に H_RESERVED の確保を行わず、完了時にも H_locked の解放操作を行わない。
- 事故抑止の順序規範 :ZCは中央銀行決済を a成立(支払人減額の証憑確定)後にのみ起動 し、中銀決済確定後にb(受取側入金)へ進める。
- これにより「中銀決済したがa減額できない」「中銀決済したがb入金できない」という最悪事故を構造的に抑止する。
- 中央銀行側の内部方式は本書の対象外だが、ZCは 依頼の冪等(ext_instruction_id) と 結果の状態化(CB_*イベント) を規範化する。
- 例外時は
SUSPENDED+CASEへ接続し、照会で「勝ち負け/保留理由/次アクション」を必ず説明可能にする。
2.2.1 Express(店舗決済:pspr_ref+Capability)
目的 :POS用途で「受取不可」事故を極小化し、同期応答で拒否できる条件を明確化する。
- 加盟店/GW(受取側)が PSPR(署名付き受取人情報、短寿命)を生成し、 受取銀行(PayeeBank)へ登録 する
- PayeeBankは PSPR本文の 保管責任(SoT) を負い、参照番号
pspr_refを払い出す(本文は短期保持/監査用にdigestを固定) - 支払銀行CMSが
POST /api/transfers(Express)でZCへ送信(pspr_ref参照+payee_bank_id指定。PSPR本文は原則送らない) - ZCは (a) PSPR署名・digest検証(発行主体=PayeeBankまたは委任先)、(b) CapabilityState確認(宛先受入可否)
- 条件を満たせば DECISION_ACCEPTED(同期) 。満たさなければ 理由コード付きREJECT(同期)
- ZCは Finality Log に受理・H予約・Decisionを記録し、非同期で PayerExecRequested を発行
- 仕向銀行が a(PAYER_EXEC_CONFIRMED)証憑発行
- a成立後のみ 、ZCが PayeeExecRequested を発行(必要情報は
pspr_ref参照で解決)。被仕向銀行が b(PAYEE_EXEC_CONFIRMED)証憑発行 - Read Model更新、照会・通知
Expressは「受付結果まで同期」だが、 ACCEPT=Decision(Raftコミット)完了 を意味する(
decision_proof_refを返す)。a/bは非同期で確定し、照会で説明可能であることを規範とする。
2.2.2 Standard(名義確認+AML/制裁スクリーニング+最終認可:規範)
- 支払銀行CMSが受付(txid採番)
- ZCが名義照会要求 → 被仕向銀行が署名付き応答(NameChecked)
- Authority Check(AML/制裁)を必須実施(実装は参加主体内チェックでも良いが、 Cleared証跡を必ず残す )
- 支払人が名義結果を確認し最終認可(TransferAuthorize)
- H予約 → Decision確定 → 実施要求 → a/b証憑回収
2.2.3 RTP(請求:資金拘束なし→当日複数回 Attempt)
現行の口座振替運用に近い 形で、実行日まで資金拘束しない。
- 実行日当日:
PR-RTP-ATTEMPT-SCHEDULEが定める時刻に Attempt を反復する(Attempt#1 は 0 時目安) - 当日の Attempt 回数上限は
PR-RTP-ATTEMPT-MAX(値は30_internal_design.md§12.9 PR-* パラメータ台帳。公開版では非公開) - 上限到達後はFAILED(翌日持越しは新rtp_id)
sequenceDiagram
autonumber
participant P as PayeeSystem(請求元)
participant RB as PayeeBank
participant Z as ZC
participant PB as PayerBank
participant C as PayerClient
P->>RB: 請求登録(rtp_payload)
RB->>Z: EVT RTP_REQUESTED(rtp_id, payee, amount, due_date, digest)
Z-->>PB: CMD REQUEST_TO_ATTEMPT(rtp_id, attempt=1)
PB-->>C: 請求通知(承認/自動払い)
alt 承認済み + 残高あり
PB->>Z: EVT ATTEMPT_RESULT(rtp_id, attempt=1, SUCCESS, txid)
Z->>Z: Standard相当のDecision/Executionへ進行(txidで追跡)
else 不足/未承認
PB->>Z: EVT ATTEMPT_RESULT(rtp_id, attempt=1, FAIL, reason)
Z-->>PB: CMD REQUEST_TO_ATTEMPT(rtp_id, attempt=2)
end
要点(規範)
- 請求データは Payee→PayeeBank→ZC→PayerBank の経路で配送する(PayerBankが顧客接点のUIを持つため)。
- 実行は常に「支払側の最終認可(または事前合意の自動払い)」を前提とし、未承認はFAILとして説明する。
- Attemptの履歴は照会・帳票で追跡可能とし、事務が回ることを最優先とする。
RTP + Immediate Execution Profile(リアルタイム請求:即時チャージ型)
- 上記の Attempt 反復プロファイル(
PR-RTP-ATTEMPT-MAX回)に加え、ウォレットチャージ等の即時性が必要な用途向けに RTP + Immediate Execution Profile を定義する。 - RTP + Immediate Execution Profileは、(1) Payee側からの請求(rtp_id)を即時配送し、(2) 支払側の最終認可(または事前合意)を得た時点で、(3) ExpressまたはStandard相当の
txidを起動して即時処理する。 - つまりRTP + Immediate Execution Profileは「請求(RTP)」と「実行(Express/Standard)」の組合せで実現し、請求そのものに資金拘束を持ち込まない。
sequenceDiagram
autonumber
participant P as PayeeSystem(請求元)
participant RB as PayeeBank
participant Z as ZC
participant PB as PayerBank
participant C as PayerClient
P->>RB: 請求登録(rtp_payload, profile=REALTIME)
RB->>Z: EVT RTP_REQUESTED(rtp_id, amount, digest, due=NOW)
Z-->>PB: CMD RTP_NOTIFY(rtp_id)
PB-->>C: 即時請求通知(承認/自動払い)
alt 承認済み(または事前合意)
PB->>Z: CMD RTP_EXECUTE_NOW(rtp_id, preferred_lane=EXPRESS/STD)
Z->>Z: txid採番 + ルール選択
Z-->>PB: CMD START_TRANSFER(txid, lane)
PB-->>Z: EVT STARTED(txid)
Z->>Z: Express/Standardの規範フローへ合流(a/bで収束)
else 未承認/不足
PB->>Z: EVT RTP_DEFERRED(rtp_id, reason)
Z->>Z: SCHEDULED_ATTEMPTへフォールバック(当日 PR-RTP-ATTEMPT-MAX 回)
end
2.2.4 Bulk/Deferred(ウィンドウ+LSM)
- window単位でキューイングし、LSM(流動性節約)で実行集合を決定
- 最終確定は H予約コミット成否(安全弁)
- フォールバック(greedy等)を規範化(第11章でRFP化)
2.2.5 gtid(多者協調:DNS領域で提供)
- gtidはDNS領域で提供し、GT_DECIDED_* をgtidシャードで確定
- leg単位でExecution証憑(a/b)を収集
- 期限切れ・一部失敗はGT_SUSPENDED+CASEで収束(補償/再試行で終端へ)
規範(Decision 確定前の不変条件)
以下 2 点は GT-level Decision(
GT_PRECHECKED → GT_DECIDED_TO_SETTLE)確定前に検証し、満たさない場合はGT_DECIDED_CANCELに収束させる。Decision 確定後にこれらの整合性違反を発見した場合、不正な遷移が FinalityLog に残り監査説明が破綻する。1. 金額均衡(PAYER 総額 == PAYEE 総額):
sum(PAYER leg amounts) == sum(PAYEE leg amounts)。違反はreason_code=AMOUNT_BALANCE_MISMATCHで取消。2. ロール完全性(PAYER と PAYEE が共に存在):片側のみの leg 集合は禁止(
reason_code=MISSING_LEG_ROLE)。PAYER ↔ PAYEE 対応付け規範:レッグ間の対応関係は
leg_idの辞書順位で確定する(PAYER 配列・PAYEE 配列をそれぞれleg_idソートし、同一 index で対をなす)。INSERT順やROWID順に依存すると取り違いの着金(A→B を A→C と誤実行)を招く。
2.2.5.1 脚構成の正規化と、受理される脚の形(規範)
PAYER と PAYEE の本数は一致していなくてよい。 1×M(fan-out)も、両側とも複数の一般形
N×M も受理され、決済される。ただしそれは決済経路が任意の N×M を直接扱うからではなく、
受付時に脚集合を正規化してから決済経路へ渡すためである。この正規化は参加者が登録したleg_id を書き換えるので、契約として明示する。
1. 正規化(POST /api/gtid/register の時点で行う)
| 登録された形 | 正規化 | 結果 |
|---|---|---|
| 1×1 / fan-in(N×1) | 何もしない | そのまま |
| fan-out(1×M)(単一通貨・均衡) | 支払人脚を PAYEE ごとの部分脚へ分割 | M×M の整列した組 |
| 一般形(N×M、両側とも複数)(通貨ごとに均衡) | 通貨グループごとに貪欲な waterfall マッチで 1:1 の部分取引へ分解(K ≤ N+M−1 組) | 整列した組 |
| 整列した組(各順位で金額・通貨が一致) | 何もしない(leg_id を無用に変えない) |
そのまま |
| 均衡していない形/真のクロスカレンシー FX(ある通貨が単独で均衡しない) | 何もしない | 下記 3. で安全に取消 |
規範
- 通貨をまたいでネットしない。 分解は通貨グループごとに独立して行う。ある通貨が単独で
均衡しない構成(JPY を払って USD を受け取る等)は正規化の対象外であり、AMOUNT_BALANCE_MISMATCH
として取消される。異種通貨を額面で相殺すれば、非代替な単位を等価とみなすことになる——
クロスカレンシーは FX レーン(第17章、FXP を導管とする脚分解)が扱う。 leg_idは書き換わり得る(規範・参加者向け)。 fan-out の部分脚は{元のPAYER leg_id}~{連番}、一般形の分解は{通し番号}~P~{元のleg_id}/{通し番号}~Q~{元のleg_id}になる。したがってGET /api/gtid/:gtidが返す脚は、
参加者が登録した脚と 1:1 とは限らない(金額の総和は各口座について保存される)。
§6.5 の「一体性の対象は Decision」という規範はこの分解の上でも変わらない——分解は
受付時に一度だけ行われ、Decision は分解後の集合に対して一体として確定する。leg_idは脚ごとのtxid(TX-GT-{leg_id})の素にもなるため、参加者は自分が付けたleg_idがそのまま照会キーになると仮定してはならない。
由来は照会できる(規範):書き換えられた脚はGtidLegs.origin_leg_idに元のleg_idを
持ち、GET /api/gtid/:gtidのlegs[]で返る。由来はleg_idの書式にも現れるが、
書式は契約ではない——「自分のどの脚がここで決済されたのか」に答えるのは列であって
文字列の形ではない。正規化そのものが起きた事実はGtidRegisteredの payload
(fanout_normalized/nm_decomposed/registered_leg_count)にも残る。- 正規化後の残余に対する安全弁(
advanceGtid)。 Decision 確定前に、脚集合が
「fan-in(PAYEE がちょうど 1 本)」または「整列した組(本数一致かつ各順位で金額・通貨が一致)」の
いずれかであることを確認し、満たさない残余は決済せずGT_PRECHECKED → GT_DECIDED_CANCEL
へ収束させる(FinalityLog payload のreason='GTID_SHAPE_UNSUPPORTED'。GTID 側にreason_code列は無い。§3.3.1 のT_gt_deadlineと同じ形)。正規化が効いていれば、ここへ
落ちるのは上表の最終行——均衡していない形と真のクロスカレンシー FX——だけである。
この安全弁は「どちらの支払人の資金がどちらの受取人へ渡ったか」が一意に定まらないまま
資金を動かさないための最後の関門であり、正規化の側を拡張したときに黙って誤配分へ倒れないよう残す。 - 取消は H 予約より前に起きる(この時点で保持しているのは leg-ready の予約確認だけであり、
資金は動いていない)。取消は安全側であり、誤配分より優先する。 POST /api/gtid/registerは 1. の正規化のみを行い、均衡検査・ロール完全性・3. の安全弁は
通らない。 登録は形式が妥当なら常に201 GTID_ACCEPTEDを返し、判定は非同期のadvanceGtid
で行う(32_api_contracts.md § POST /api/gtid/registerのタイミング注記)。クライアントはGET /api/gtid/:gtidで終局状態を確認しなければならない。
検証は test/integration/chaos_gtid.test.ts(#14 fan-out・#18 一般形 N×M・#19 多通貨の一般形・
#18b 真の FX が安全に取消されること)が固定する。
2.2.6 新機能プロファイル(QR決済・エイリアス送金・Rich Data・越境等)
Zenithは決済基盤内部の処理にとどまらず、利用者の利便性とコンプライアンスを最前線で拡張する以下の応用機能群(フロントエンド連携プロファイル)をサポートする。
1. QR決済(動的・静的QRとHMAC署名)
- 動的(DYNAMIC)/静的(STATIC)QR の双方を発行可能とし、生成時に事前共有鍵(
QR_SECRET)を用いたHMAC署名を付与する。 - 決済リクエスト時には中央または参加行エッジで署名を検証して改ざん・なりすましを排除する。検証成功後にのみ
Expressレーンでの支払指示(Event)を即座に起動し、シームレスな店舗決済を実現する。
2. エイリアス送金(Proxy Directory / Alias)
- 利用者の 電話番号 や メールアドレス などの「プロキシ値(エイリアス)」から、実際の銀行・口座情報を解決する機能を提供する。
POST /api/proxy/registerによりエイリアスと口座の紐付けを登録し、送金時にはGET /api/proxy/resolveで宛先を解決することで、利用者は複雑な口座・店番入力を意識せずに送金が可能となる。
3. Rich Data(商流情報・EDIの分離保存構造)
- 金融のコアメッセージ(金額・宛先)と、商流取引による豊富なメタデータ(請求書明細、契約情報等)を分離して管理する。
- 巨大なデータを単一経路に詰め込むのではなく、事前に別パス(
POST /api/richdata/storeやPOST /api/edi/register)へ保存し、取得した参照ID(data_ref/edi_ref)のみをコア決済メッセージに紐づけることで、効率性と完全なトレーサビリティを両立する。
4. 越境送金(Cross-border)のトラベルルール対応
- クロスボーダー送金(
/api/cross-border/send)においては、FATF Recommendation 16(R16)に基づく発信人および受取人の詳細な本人情報検証を必須とする。 - 情報不備がある送金は
FATF_VALIDATION_ERRORとしてプレチェック段階で水際ブロックし、基盤レベルでのAML/CFTコンプライアンスを強制する。
5. 口座確認・名義照会の一括処理(Account Verification Batch)
- 単一の宛先照会だけでなく、企業の給与振込や大量支払などで要求されるバッチ形式の名義照会(
POST /api/account-verify/batch)をサポートし、大量件数時の通信・参照オーバーヘッドを大きく削減する。
2.2.7 継続収納(口座振替:資金拘束なし→振替日に一回きりの残高判定)
制度要件は 10_requirements.md §3.2.8。本節は処理方式を固定する。
2.2.7.1 RTP・HTLC Auth との差分(設計の起点)
三者はいずれも受取人起点(プル型)であるが、統制の置き方が異なる。混同すると資金フローを取り違えるため、先に固定する。
| RTP | HTLC Auth | 継続収納 | |
|---|---|---|---|
| 顧客の同意 | 都度(respondToRtp) |
都度(approveAuthRequest) |
継続(DebitMandate、1 回だけ) |
| 資金の拘束 | しない | する(承認時に reserve-funds) |
しない |
| 拘束の期間 | — | 承認〜Capture | — |
| 残高判定の時点 | 実行時 | 承認時 | 振替日当日、一回きり |
| 受取人の適格性 | 都度同意が代替 | 事前登録 | 継続委任+累計枠 |
規範:継続収納は
reserve-fundsを発行しない。オーソリ型の実装(src/zc/lanes/htlc_auth/approve.ts)を流用してはならない。流用すると顧客の資金が振替日まで拘束され、口座振替の性質そのものを失う。共有できるのは preimage / hashlock / Vault という認可証明の構造のみである。
2.2.7.2 認可の検証と資金の検証を分離する(規範)
資金を拘束しないということは、認可されていることが支払能力を何ら保証しないということである。したがって 2 つのゲートを時間的に分離し、それぞれ独立した証跡を残す。
予告受理時点 振替日当日
───────────── ─────────────
委任状スコープの照合 残高の判定
├ 範囲内 → preimage 解錠 ├ 足りる → 実行 → b
│ (= 顧客が認可した範囲内で └ 足りない → 未確定のまま
│ あることの証明) (24:00 で CONFIRMED_NG)
└ 範囲外 → AWAITING_ADDITIONAL_AUTH
累計枠の予約
- preimage の解錠は予告時点で行う。 解錠された事実そのものが「払出行が委任状の範囲内であることを検証した」証跡となり、FinalityLog に記録される。事後に受取人が「顧客は同意していた」と主張しても、解錠記録が無ければ成立しない。
- HTLC Auth では認可と資金確保が同一イベントに癒着しているため「認可はされていたが払えなかった」を表現できない。継続収納ではそれが正常系であるため、分離が必須である。
2.2.7.3 予告とラダーの状態
ScheduledCollection の状態。Transactions の状態機械には辺を追加しない。
stateDiagram-v2 [*] --> SCHEDULED: 予告受理(スコープ内・窓あり) [*] --> FROZEN: 予告受理(REALTIME。窓の幅が 0) [*] --> AWAITING_ADDITIONAL_AUTH: 予告受理(スコープ超過) AWAITING_ADDITIONAL_AUTH --> SCHEDULED: 顧客が追加認可 AWAITING_ADDITIONAL_AUTH --> DECLINED_BY_PAYER: 顧客が拒否 AWAITING_ADDITIONAL_AUTH --> LAPSED: 振替日まで無応答(既定拒否) SCHEDULED --> FROZEN: amend_freeze_at 到達 FROZEN --> FIRED: 振替日到来 → Transactions 実体化 SCHEDULED --> WITHDRAWN: 受取人が取下げ FROZEN --> WITHDRAWN: 受取人が取下げ(不利益変更でないため凍結後も可) FROZEN --> SUPERSEDED: 同一 charge_ref の先行段が成功 SCHEDULED --> SUPERSEDED: 同上 FIRED --> [*] LAPSED --> [*] DECLINED_BY_PAYER --> [*] WITHDRAWN --> [*] SUPERSEDED --> [*]
規範
REALTIMEはFROZENで入場し、同一リクエスト内でFIREDまで進む。SCHEDULEDを経由しない——変更を受け付ける窓が存在しないため、変更可能な状態を一瞬でも通ることは実態に反する。amend_freeze_atは生成時刻に等しい(「窓が無い」をNULLではなく幅 0 の窓として表現し、凍結判定のコードを分岐させない)。SUPERSEDEDは行削除ではなく状態遷移として記録する。 「5月13日の再請求は 4月27日に成功したため取り消された」と説明できなければならない。- ラダーの段の発火は 2 条件の連言である——前段が
CONFIRMED_NGに確定していること、かつ予定日が到来していること。前段がACCEPTED(窓の中)のあいだ後段は待機する。日付のみで駆動すると、窓の遅延時に二重引落を招く。 AWAITING_ADDITIONAL_AUTH → LAPSEDはラダー全体を終了させる。 後段を発火させてはならない。- 排他は
(mandate_id, charge_ref)の一意制約が担う。 b に到達できる収納は 1 費目につき高々ひとつである。二重収納の防止と、ラダーの「先行段が成功したら後段は成立しない」性質は、同一の制約から導かれる。別々の機構を設けてはならない。
2.2.7.4 振替日の処理と所有権
sequenceDiagram
participant Z as ZC
participant PB as 払出行(勘定系)
Note over Z: 振替日 00:00 — 当日分の収納集合を確定
Z->>Z: 充当順序を算定(§2.2.7.5)+ 採択証跡を FinalityLog へ
Z->>Z: Transactions を RECEIVED で実体化(lane=DIRECT_DEBIT)
Z->>Z: owner = 'CORE:<bank_id>:<business_date>' へ移転
Z->>PB: 順序づけられた収納集合を引き渡し
loop センターカット(勘定系の内部日程・回数は ZC の関知外)
PB->>PB: 順に処理し、残高不足は弾く
PB-->>Z: 試行結果(CollectionAttempt として追記)
alt 成功
Z->>Z: 即 b 成立 → SETTLED。owner を ZC へ返却
else 未成功
Z->>Z: 未確定のまま保持(失敗と判定しない)
end
end
Note over Z: 振替日 24:00 — 未成功のものを CONFIRMED_NG に確定。owner を ZC へ返却
本図は
SCHEDULED/SCHEDULED_LONGの経路である。REALTIMEはセンターカット窓を持たないため、所有権の移転も充当順序の算定も経ない。その処理は EXPRESS レーンそのものである——RECEIVED → PRECHECKED → H_RESERVED → DECIDED_TO_SETTLEを同期で走り(reserveFundsForDebitが残高不足を同期判定する)、DECISION_ACCEPTEDを返し、デビットは enqueue されて a/b は非同期に成立する。実装はprocessExpress(src/zc/lanes/express.ts)と同じヘルパ連鎖を用い、確定点契約を再定義しない。結果として
REALTIMEは当日の整列済みの列に割り込む。しかも単に先に処理されるだけでなく、任意の時刻に H 予約と銀行の資金予約を先取りする。これは本モードに内在する不公平であり、機構では解消できない(10_requirements.md§3.2.8.9-5)。
規範
- 確定は成功と失敗で非対称である。 成功は b の観測時点で確定する(資金が動いた以上それ以上変わらない)。失敗は
confirm_deadline_atをもって確定する。両者を分けるのは「後続の試行がありうるか」の一点であり、同期/非同期ではない——b は全モードで非同期に成立する。SCHEDULEDでは期限が振替日 24:00 であり、日中の残高不足は「まだ b に到達していない」だけで失敗ではない(顧客が日中に入金すれば後続のセンターカットで成功しうる)。REALTIMEでは試行が 1 回きりのため Decision の時点が期限となる。24:00 はモードごとの導出値であってレーンの定数ではないため、掃引はconfirm_deadline_at <= now AND result IS NULLの一様な述語で回す。 - 勘定系への要求は「振替日を超えて記帳しないこと」の一点のみ。 窓スケジュールの宣言も完了通知も要求しない(
10_requirements.md§7.2.2 の Tier を引き上げない)。ZC は 24:00 まで待てばよく、コアの内部日程を知る必要がない。 - 窓のあいだ所有者は勘定系である。
Transactions.owner='CORE:<bank_id>:<business_date>'とし、タイムアウト掃引(第3章 §3.3)が触らないようにする。所有権は最初の成功時、またはconfirm_deadline_atの早い方で ZC に返る。移転は FinalityLog に記録する。単一所有者則の適用は HIGH_VALUE の外部決済 venue と同型である(30_internal_design.md§5)。REALTIMEは窓を持たないため所有権を移転しない(owner='ZC'のまま同期的に決着する)。 - コアの確定応答前に b を成立させない。 HIGH_VALUE における
external_settlement_status='SETTLED'の不変条件(src/zc/orchestrator.ts)と同型の制約を課す。欠くと「受取人に入金済みだが顧客から引き落とせていない」乖離が生じる。 - 試行は追記型の系列(
CollectionAttempt)として保持し、現在状態は導出する。 中間結果は確定ではないが、受取人にとっては行動の根拠となる(0 時に未成功が分かれば当日中に督促できる)。ただし督促そのものは ZC の外側で行われる(10_requirements.md§3.2.8.8-7)。 - 想定内の残高不足を CASE に収束させない。 事前に資金を拘束しないため、レガシーアダプタの
LEGACY_CORE_INSUFFICIENT_FUNDS(シャドウが承認済みなのにコアが拒否=乖離)の判定式は本レーンに適用されない。CASE の対象は真の乖離に限る。
2.2.7.5 同日競合時の充当順序
資金を拘束しないため、同一顧客・同一振替日に複数の収納が競合しうる。
順序は ZC が決定し、勘定系は供給された順に処理する。 ZC は顧客残高を知らないが、順に処理して不足分を弾く動作がそのまま貪欲充当となる。勘定系に新たな能力を要求しない。
比較器は Bulk LSM の
lexicographicOrder(src/zc/liquidity/bulk_lsm.ts)と同一構造とする。順位 項 目的 1 顧客の優先指定 残高不足時にどの債務を優先するかを顧客が決める 2 原請求の古さ(昇順) ラダー滞留の逆進性を打ち消す 3 金額(昇順) 成立件数の最大化 4 collection_id辞書順決定性 第 3 項だけでは逆進する。 同一予算下では小さい金額から充当したほうが成立件数が増えるが、ラダーで滞留した収納は遅延損害金のぶん金額が増えるため、金額のみで並べると最も救うべきものが最も後回しになる。第 2 項がこれを打ち消す。既存 LSM が
due_at → fairness → throughputの順を採るのと同じ理由である。第 4 項は決定的でなければならない。 到着順・
ROWID依存の対応付けを禁ずる(§2.2.5 のleg_id辞書順と同じ理由)。採択順序と理由を FinalityLog に記録する(
LsmRunsと同じ作法)。「なぜ A 社が先で B 社が後だったか」に事後に答えられることが、本節の存在理由である。
2.2.7.6 追加認可(再許諾)の処理
- スコープ超過の検出は予告受理時点であり、振替日まで猶予がある。この猶予が救済可能性そのものである。
- 復帰の機構は既存の mandate サスペンド/再開と同型である——
mandatePrecheckOrSuspend(src/zc/lanes/_mandate_precheck.ts)が違反をハード拒否せずPRECHECKED_SUSPENDEDへ落とし、resume-*が mandate を再検証したうえで CAS で復帰させる。継続収納では、この解決者が運用担当者ではなく顧客本人になる。 - 無応答は拒否とする。 沈黙を同意とみなすと、本機構そのものが過大請求の経路となる。
- 追加認可は単発認可を既定とする。
registerMandateをそのまま用い、当該charge_ref・当該金額・1 回限りにスコープした独立の委任状として登録する。子 mandate では表現できない——assertMandateValidは委任チェーンを逓減方向にのみ検証するため、子が親の上限を緩めることはできない(src/shared/mandate.ts)。 - 単発認可は枠を迂回せず、枠に加算する。 累計枠が「この受取人に通算いくら渡したか」の唯一の正であり続けなければならない。
REALTIMEモードは猶予を持たないため、この救済経路を持たない。スコープ超過は即時拒否となる。
2.2.7.7 累計枠の直列化(規範)
累計上限を「収納のたびに集計して判定する」方式で実装してはならない。
✗ SELECT SUM(amount) ... → 判定 → INSERT
これは check-then-act であり、並行する収納が上限を突き抜ける(本書群は同種の競合をtest/integration/concurrent_races.test.ts で明示的に検証対象としている)。
規範
枠は予約可能な予算として状態で持つ(設計思想 8「H は状態として管理し、絶対超過を許さない」と同じ理由)。予約は予告受理時点に行う。枠は資金ではなく認可の配分であるため、予告時点で押さえても顧客は何も失わない。
全条件を単一行の条件付き UPDATE で同時に検査する。
MandateBudget(dd_mandate_id, period_key)の 1 行に各カウンタを持たせ、meta.changesを唯一の裁定者とする。これはtransitionWithLog・H 予約・FxTransfers.statusと同一の CAS 作法であり、直列化点を単一行に閉じることでデッドロックを構造的に排除する(複数の枠を個別に予約する設計は、取得順序の管理を要求してしまう)。上限値は
DebitMandateから相関副問合せで読む。 カウンタ側に写しを持たせない——上限は契約の途中で変更されうるため(10_requirements.md§3.2.8.4-10)、写しを置くと伝播漏れが「宣言された上限と実際に効いている上限の食い違い」を生む。副問合せは同一文の中で評価されるため、変更との競合も生じない。UPDATE MandateBudget SET month_amount = month_amount + :amt, month_count = month_count + 1, lifetime_amount = lifetime_amount + :amt, lifetime_count = lifetime_count + 1, updated_at = :now WHERE dd_mandate_id = :ddm AND period_key = :pk AND month_amount + :amt <= (SELECT month_amount_cap FROM DebitMandate WHERE dd_mandate_id = :ddm) AND month_count + 1 <= (SELECT month_count_cap FROM DebitMandate WHERE dd_mandate_id = :ddm) AND lifetime_amount + :amt <= (SELECT lifetime_amount_cap FROM DebitMandate WHERE dd_mandate_id = :ddm) AND lifetime_count + 1 <= (SELECT lifetime_count_cap FROM DebitMandate WHERE dd_mandate_id = :ddm) AND ...上限が未設定(
NULL)の枠は制約として働かないため、比較はcap IS NULL OR ... <= capの形を採る。拒否理由の特定は
changes = 0の後に行う。 どの条件で落ちたかは CAS の戻り値から判らないため、拒否時のみ SELECT して理由を確定する二段構えとする。異常系であり、性能上の懸念はない。
上限変更は消費済みカウンタをリセットしない(引き上げ→引き下げの往復による消費の洗浄を防ぐ)。カウンタの回復は下記 6 の規則にのみ従う。period_keyを行の一部とすることで、リセット型は自然にリセットされる(月が変われば新しい行が立つ)。消尽型はperiod_key='LIFETIME'の固定行に置き、回復させない。移動窓は連続 2 暦月の合計上限で近似する。 厳密な移動窓は履歴集計を要し TOCTOU が復活するため、単一行 CAS の枠内に収まる近似を採る。これで暦月枠の境界攻撃(月末・月初にまたがって 2 か月分を 2 日で消費する)は塞がる。
枠の回復:収納失敗時は解放する(未収であるため)。返金時も解放する。消尽型は解放しない——使い切りをもって契約が終了する(「12 回払い」はこれで表現される)。
2.3 例外系フロー(規範:取消・失敗・救済)
2.3.1 Decision前:取消(CANCELLED)で収束可能
- 受付後、Decision前の取消:
DECIDED_CANCEL - 証跡:Cancel Proof(
30_internal_design.md第12章(I/F契約))
2.3.2 a到達後:原則取消不可、救済はREVERSAL(別取引)
- 誤送金等は Reversal(反対取引) を別txidで起票
- 因果リンク(correlation/causation)を必須とし、監査で辿れること
規範
b(受取利用可能化)成立後の取消は禁止。対外説明・監督・訴訟で最も問題になるのは「後から取り消した」類型であるため。
2.4 障害系フロー(代表3類型)
類型A:ZC/通信障害(再送・重複・欠番)
- at-least-once前提。idempotency_key と seq で収束(第5章)。
- DLQ落ちはCASE起票(第10章)。
類型B:DNS HOLD(当座預金残高不足による中断)
状態:DNS_HOLD_REQUESTED → DNS_HOLD_ACTIVE → DNS_RESUMED
HOLD中は取消せず 保留として積上げ 、再開時に順序制御して処理(第9章)。
影響範囲の固定(重要):DNSはカットオフまでの清算債務を対象とするため、カットオフ後に到来した少額DNS取引は当該サイクルに追加計上せず、
scheduled_cycle_id = next_cycleとして次サイクルへ繰延計上する(受付は継続)。IGS(高額即時)の扱い:
igs_modeの値域はNORMAL | STOP | RINGFENCED | RINGFENCED_PLUS(平時はNORMAL)。HOLD への入り方は 原因行集合が既に判明しているかで 2 経路に分岐する(優先クラス分類は持たず全件 normal 扱い)。解消時は→ NORMALへ復帰する。NORMAL → STOP:原因が未特定のまま HOLD を宣言した場合(外部通知等)。IGS は全件停止する。NORMAL → RINGFENCED:清算実行でショートフォールを検出し、その場で原因行集合が確定した場合。全件停止を経ずに直接リングフェンスへ入る(原因が判っているのに全件を止めないため)。STOP → RINGFENCED:原因行集合が確定(cause_identified=true)RINGFENCED → RINGFENCED_PLUS:dns_recovery_reserveが算定済みで、説明可能な形で成立(reserve_explain_hash生成、reserve_confidence閾値以上)- Accept条件(例)
- Mode1(RINGFENCED):
prefunded==trueまたはigs_ringfenced_pool==trueかつavailable_liquidity_in_ringfence >= amount(超過分はRejectではなく Defer) - Mode2(RINGFENCED_PLUS):Mode1条件に加え、
counterparty ∉ HOLD_CAUSING_PARTICIPANTSかつavailable_liquidity_after_tx >= dns_recovery_reserveかつrisk_flags==none(公平性のためigs_throttle_budget超過分は Defer)
- Mode1(RINGFENCED):
リザーブ算定(方式):
dns_recovery_reserveは制度の議論に委ねつつ、方式としては ZCが算式に基づきリアルタイムに自動計算し、監査再現性のためreserve_explain_hash(入力・算式・出力のダイジェスト)とreserve_confidenceを必須記録する。RINGFENCED → RINGFENCED_PLUSの昇格は、この算定が説明可能な形で成立した場合に限る(reserve_confidenceが閾値以上)。Defer は捨てない(規範):Accept 条件を満たさない IGS は reject せず、優先度と
scheduled_execution_windowを持つ専用キュー(IgsDeferQueue)へ退避する。実行ウィンドウ到来分は優先度順に再投入する。リングフェンスも Defer である(規範):原因行に触れる IGS の隔離は、拒否でも「ただの park」でもなく、上記と同一のキューへの登録とする。優先度は次の順(小さいほど先)——
IGS_DEFER_PRIORITY_THROTTLED=200(Mode2 の公平性スロットル。本来 admissible な取引が予算超過で待たされているだけ)、IGS_DEFER_PRIORITY_RINGFENCED=300(隔離。最劣後。通せば原因行の中銀ポジションが動いてしまう、リングフェンスがまさに防いでいる事象である)。- 同一キューに載せることが、両者の順序関係を定義可能にしている。かつて隔離は
PRECHECKED_SUSPENDEDに置くだけでキューに載せず、毎分の再評価スイープ(resumeRingfencedIgs)だけが拾っていた。これは順序が未定義であるばかりか、実効的には通せない取引を通せる取引より頻繁に再試行する動作だった。毎分のスイープは liveness のバックストップとして残すが、成功時はmarkDeferResumedでキュー行も閉じる(さもないと step 15 が回収できない DEFERRED 行が残る)。 STOP(全面停止)はキューに載せない。原因が未特定である以上、個別の順序付けの対象ではなく、レール全体の保留だからである。
- 同一キューに載せることが、両者の順序関係を定義可能にしている。かつて隔離は
解除後の再開は exactly-once(規範):リングフェンスで
PRECHECKED_SUSPENDEDに積まれた IGS の再開は、その時点の admission を再評価したうえで CAS により exactly-once で行う。「HOLD 中に積んだものを解除時に一括で流す」実装は禁止する——解除時点で条件を満たさない取引まで通ってしまう。
制度合意待ち:
dns_recovery_reserveの算式そのもの(何をバッファとして積むか)は制度設計に委ねられており確定していない。30_internal_design.md§10.3 で追跡する。
類型C:高額即時レーン介在で「決済完了/未了」が不整合
- ExternalSettlement Adapter は
ext_instruction_idで冪等 - 結果再照合によりRead Modelを再構築(第9章)。
- 返し決済(高額即時レーン)は別ID・別証跡(Reversalではなく「返し決済」)として扱う(
10_requirements.md第4章 法制度・契約構造との整合性)。