Skip to content

RFC-005 v4: Nexus Payment Core Specification (Gasless Escrow Settlement)

MetadataValue
TitleNexus Payment Core Specification
Version4.0.0
StatusStandards Track (Draft)
SupersedesRFC-005 v3.0.0 (Escrow Settlement)
AuthorCipher & Nexus Architect Team
Created2026-02-24
Updated2026-04-01
ScopeOrchestration, State Management, Gasless Escrow, Group Payments, AP2 Protocol, Cancel/Dispute, Multi-Token
ChainPlatON Devnet (chain_id: 20250407)
TokenERC-3009 compatible stablecoins (XSGD, USDC)

1. Abstract

本 RFC 定义 Nexus Core 的完整实现规范。v4 在 v3 Escrow Settlement 基础上新增 Gasless DepositCancel 机制AP2 Credential ProviderMulti-Token 支持。

核心变更(v3 → v4):

  1. Gasless Deposit: 用户仅签署 EIP-3009 授权,Relayer 代付 Gas 提交 batchDepositGasless()
  2. Cancel 机制: 商户/运营可在任何非终态取消支付,链上 ESCROWED 状态通过 cancelByOperator() 即时退款
  3. AP2 Protocol: 作为 Credential Provider 为 Agent Protocol 2 签发 PaymentMandate Verifiable Credential
  4. Multi-Token: 通过 setToken() 支持多种 ERC-3009 兼容代币 (当前: XSGD)
  5. 13-State Machine: 新增 CANCELLED 状态
  6. Auto-Refund Late Deposits: 过期支付收到延迟链上存款时自动触发退款

2. Design Principles

  1. Escrow Settlement: 资金通过智能合约担保,商户履约后释放,超时自动退款
  2. Gasless UX: 用户只需签署 EIP-3009 授权,不持有任何原生代币;Relayer 代付全部 Gas
  3. Batch Efficiency: 多笔支付聚合为一组,共享一个链上交易
  4. MCP-First: 所有能力通过 MCP Protocol 暴露,同时提供 REST API
  5. Event Sourcing: 每次状态变更生成不可变事件记录
  6. Anti-MITM: Core Operator 签署 Group 指令,链上验证防止篡改
  7. Protocol Agnostic: 同时支持 NUPS 原生协议和 AP2 Agent Protocol

3. Architecture

UA --[MCP/REST]--> Nexus Core --[Webhook]--> MA
                       |
                       +-- Security Module (EIP-712, DID Resolver, Group Sig)
                       +-- Order State Machine (13 states)
                       +-- Group Manager (batch orchestration)
                       +-- AP2 Protocol Router (NUPS / AP2 detection)
                       +-- AP2 Credential Provider (PaymentMandate VC issuance)
                       +-- Chain Watcher (Escrow events: Deposited/Released/Refunded)
                       +-- Relayer (gasless deposit / release / refund / cancel tx submission)
                       +-- Webhook Notifier (HMAC signed, RFC-009)
                       +-- PostgreSQL (payments, payment_groups, events, merchants,
                       |               webhook_logs, mandate_evidence)
                       |
                  PlatON Devnet (chain_id: 20250407)
                  XSGD/USDC (ERC-3009) + NexusEscrow (UUPS Proxy v4.3.0)

4. Payment Flow

4.1 Happy Path (Gasless)

Step 1:  UA obtains merchant Quote (NUPS v1.5, EIP-712 signed)
Step 2:  UA calls Core nexus_orchestrate_payment(quotes[], payer_wallet)
         Core verifies sigs -> DID resolves -> creates payment group
         -> returns 402 with BatchDepositInstruction (user_action: "SIGN_ONLY")
Step 3:  User signs EIP-3009 TypedData (via MetaMask / wallet) — 仅签名,不发送交易
Step 4:  Checkout page POST /api/checkout/:groupId/relay-deposit { payer_address, v, r, s }
         Core Relayer calls batchDepositGasless() on-chain — 用户零 Gas
Step 5:  Core Chain Watcher detects Deposited events -> status = ESCROWED
Step 6:  Core sends Webhook to MA: payment.escrowed
Step 7:  MA confirms fulfillment -> Core Relayer calls release() on escrow
Step 8:  Chain Watcher detects Released event -> status = SETTLED
Step 9:  Core sends Webhook to MA: payment.settled
Step 10: MA confirms completion -> status = COMPLETED

4.2 Checkout Page Flow (Browser Gasless)

Step 1: UA receives 402 response with checkout_url
Step 2: User opens checkout_url in browser with MetaMask
Step 3: Checkout page displays payment summary (SSE real-time updates)
Step 4: User clicks "Pay" → MetaMask prompts EIP-3009 signature (签名,非交易)
Step 5: Checkout page POST relay-deposit with signature components
Step 6: Relayer submits batchDepositGasless() on-chain
Step 7: Page polls /api/checkout/:token until receipt verified
Step 8: Receipt verified → ESCROWED transition

4.3 AP2 Protocol Flow

当检测到 AP2 协议请求时 (body.protocol === "ap2" 或包含 cart_mandates):

Step 1:  UA sends orchestrate request with CartMandate VCs
Step 2:  Core validates CartMandate signatures, extracts payment intent
Step 3:  Core creates payment group, returns 402 (same as NUPS)
Step 4:  User completes gasless deposit (same as 4.1/4.2)
Step 5:  On ESCROWED transition → Core issues PaymentMandate VC
         (W3C Verifiable Credential, signed with ECDSA P-256 / JCS)
Step 6:  PaymentMandate stored in mandate_evidence table
Step 7:  Webhook includes mandate_vc in payload for AP2 orders

4.4 Cancel Flow

Pre-Escrow (CREATED / AWAITING_TX):
  -> nexus_cancel_payment → database status = CANCELLED
  -> Webhook: payment.cancelled

ESCROWED:
  -> nexus_cancel_payment → Relayer calls cancelByOperator() on-chain
  -> Chain Watcher detects Refunded → status = CANCELLED
  -> Webhook: payment.cancelled

Group-level:
  -> nexus_cancel_order → cancels all child payments in group
  -> Group status → GROUP_CANCELLED

4.5 HTTP 402 Payment Required Response

typescript
interface PaymentRequired402 {
  readonly nexus_version: string;
  readonly group_id: string;
  readonly status: "PAYMENT_REQUIRED";
  readonly checkout_url: string;
  readonly instruction: BatchDepositInstruction;
  readonly nexus_group_sig: Hex;
  readonly core_operator_address: Address;
}

4.6 BatchDepositInstruction Schema

typescript
interface BatchDepositInstruction {
  readonly group_id: string;
  readonly chain_id: 20250407;
  readonly chain_name: "PlatON Devnet";
  readonly rpc_url: string;
  readonly payment_method: "ESCROW_CONTRACT";
  readonly escrow_contract: Address;        // UUPS proxy address
  readonly token_address: Address;          // ERC-3009 token contract
  readonly token_symbol: string;            // "XSGD" | "USDC"
  readonly token_decimals: 6;
  readonly total_amount_uint256: string;
  readonly total_amount_display: string;
  readonly payments: readonly GroupPaymentDetail[];
  readonly eip3009_sign_data: EIP3009SignData;  // EIP-3009 TypedData for signing
  readonly user_action: "SIGN_ONLY";        // v4: user only signs, relayer submits
  readonly gas_paid_by: "RELAYER";          // v4: relayer pays all gas
  readonly nexus_group_sig: Hex;
  readonly core_operator_address: Address;
}

v3 → v4 关键变更:

  • user_action: "SIGN_AND_SEND""SIGN_ONLY"
  • gas_paid_by: "USER""RELAYER"
  • token_symbol: 固定 "USDC" → 动态 (当前 "XSGD")
  • 移除 deposit_tx 字段 (用户不再直接发送交易)

4.7 GroupPaymentDetail Schema

typescript
interface GroupPaymentDetail {
  readonly nexus_payment_id: string;
  readonly merchant_did: string;
  readonly merchant_order_ref: string;
  readonly merchant_address: Address;
  readonly amount_uint256: string;
  readonly amount_display: string;
  readonly summary: string;
  readonly payment_id_bytes32: Hex;    // keccak256(nexus_payment_id)
  readonly order_ref_bytes32: Hex;     // keccak256(merchant_order_ref)
  readonly merchant_did_bytes32: Hex;  // keccak256(merchant_did)
  readonly context_hash: Hex;          // keccak256(JSON.stringify(context))
}

5. State Machine

5.1 States (13-state payment machine)

StatusDescriptionTrigger
CREATEDQuote verified, payment createdorchestrate_payment success
AWAITING_TXLegacy: UA has PaymentInstructionDirect Transfer mode (deprecated)
BROADCASTEDLegacy: UA submitted tx_hashDirect Transfer mode (deprecated)
ESCROWEDFunds deposited in escrow contractChain Watcher (Deposited event)
SETTLEDEscrow released to merchantChain Watcher (Released event)
COMPLETEDMerchant confirmed fulfillmentconfirm_fulfillment call
EXPIREDPayment timed outTimeout Handler
TX_FAILEDOn-chain transaction revertedChain Watcher
RISK_REJECTEDSecurity check failedSecurity Module (future)
REFUNDEDEscrow refunded to payerChain Watcher (Refunded event)
DISPUTE_OPENPayer opened disputeChain Watcher (Disputed event)
DISPUTE_RESOLVEDArbiter resolved disputeChain Watcher (Resolved event)
CANCELLEDPayment cancelled by merchant/operatornexus_cancel_payment (v4 新增)

5.2 Transition Rules

(none)        -> CREATED          [valid quote + signature verified]
CREATED       -> ESCROWED         [Chain Watcher: Deposited event]
CREATED       -> EXPIRED          [timeout]
CREATED       -> RISK_REJECTED    [security check failed]
CREATED       -> CANCELLED        [merchant/operator cancel]

ESCROWED      -> SETTLED          [Chain Watcher: Released event]
ESCROWED      -> REFUNDED         [Chain Watcher: Refunded event (timeout)]
ESCROWED      -> DISPUTE_OPEN     [Chain Watcher: Disputed event]
ESCROWED      -> CANCELLED        [cancelByOperator → Refunded on-chain]

SETTLED       -> COMPLETED        [merchant confirms fulfillment]

DISPUTE_OPEN  -> DISPUTE_RESOLVED [Chain Watcher: Resolved event]

EXPIRED       -> REFUNDED         [auto-refund late deposits]

Legacy (Direct Transfer, deprecated):
CREATED       -> AWAITING_TX      [UA requests PaymentInstruction]
AWAITING_TX   -> BROADCASTED      [UA submits tx_hash]
AWAITING_TX   -> CANCELLED        [cancel]
BROADCASTED   -> SETTLED          [on-chain Transfer confirmed]
BROADCASTED   -> TX_FAILED        [on-chain revert]

Terminal statuses: COMPLETED, EXPIRED, TX_FAILED, RISK_REJECTED, REFUNDED, DISPUTE_RESOLVED, CANCELLED

5.3 Group Statuses

Group StatusDescription
GROUP_CREATEDGroup created, payments pending
GROUP_AWAITING_TX402 returned, waiting for on-chain tx
GROUP_DEPOSITEDOn-chain deposit confirmed (receipt verified)
GROUP_ESCROWEDAll child payments transitioned to ESCROWED
GROUP_SETTLEDAll child payments released
GROUP_COMPLETEDAll child payments completed
GROUP_EXPIREDGroup timed out
GROUP_PARTIALMixed states (some settled, some disputed)
GROUP_CANCELLEDAll child payments cancelled (v4 新增)

5.4 Timeout Rules

ScenarioTimeoutAction
Quote expiryquote.expiry timestampCREATED -> EXPIRED
Awaiting deposit30 minutesCREATED -> EXPIRED
Escrow release deadline24 hours (on-chain, ms)refund() callable by anyone
Dispute window72 hours (on-chain, ms)dispute() no longer callable
Arbitration timeout7 days (on-chain, ms)refundUnresolvedDispute() callable

PlatON Devnet 注意: block.timestamp 返回毫秒而非秒。所有链上超时参数均以毫秒为单位配置。

5.5 Auto-Refund Late Deposits

当 EXPIRED 状态的支付收到延迟的链上 Deposited 事件时:

  1. Chain Watcher 检测到 Deposited event
  2. 自动调用 Relayer.submitRefund() 触发链上退款
  3. 状态不变 (保持 EXPIRED),但 Refunded 事件被记录
  4. 防止用户资金被锁定在已过期的订单中

6. Security Specification

6.1 EIP-712 Quote Verification

typescript
const NEXUS_QUOTE_DOMAIN = {
  name: "Nexus",
  version: "1",
  chainId: 20250407,
  verifyingContract: "0x0000000000000000000000000000000000000000",
} as const;

const NEXUS_QUOTE_TYPES = {
  NexusQuote: [
    { name: "merchant_did", type: "string" },
    { name: "merchant_order_ref", type: "string" },
    { name: "amount", type: "uint256" },
    { name: "currency", type: "string" },
    { name: "chain_id", type: "uint256" },
    { name: "expiry", type: "uint256" },
    { name: "context_hash", type: "bytes32" },
  ],
} as const;

验证步骤:

  1. 从 quote 字段重建 EIP-712 TypedData
  2. 从 signature 恢复签名者地址
  3. 从 merchant_registry 解析 merchant_did 获取注册签名者
  4. 比对恢复地址与注册签名者
  5. 验证 quote 未过期

6.2 Group Signature (Anti-MITM)

Core 生成 BatchDepositInstruction 后,使用 Core Operator 密钥签署 EIP-712 GroupApproval:

typescript
const GROUP_APPROVAL_TYPES = {
  NexusGroupApproval: [
    { name: "groupId", type: "bytes32" },
    { name: "entriesHash", type: "bytes32" },
    { name: "totalAmount", type: "uint256" },
  ],
} as const;

链上 batchDepositGasless() 验证此签名,确保交易参数未被篡改。

6.3 Receipt Verification

Checkout 确认时,Core 验证链上交易回执:

  • HTTP 200: receipt success → transition to ESCROWED
  • HTTP 202: receipt not available yet → frontend polls (5s interval, max 24 attempts = 120s)
  • HTTP 422: receipt reverted → error, no state change

6.4 DID Resolution (MVP)

MVP uses a local merchant_registry table:

sql
merchant_did       TEXT PRIMARY KEY
signer_address     TEXT NOT NULL    -- signing key address
payment_address    TEXT NOT NULL    -- receiving address (escrow release target)
webhook_url        TEXT             -- callback URL
webhook_secret     TEXT             -- HMAC key
ap2_did            TEXT             -- AP2 agent DID (optional)

6.5 Payment Address Trust

CRITICAL: Core MUST resolve payment_address from the merchant_did registry. It MUST NOT trust any address passed in the quote or by the UA. This prevents payment redirection attacks.

7. Interface Specification

7.1 MCP Tools (11 tools)

ToolCallerDescription
nexus_orchestrate_paymentUAVerify quotes, create payment group, return 402 with BatchDepositInstruction
nexus_get_payment_statusUA/MAQuery payment or group status
nexus_confirm_fulfillmentMAConfirm merchant delivery, trigger escrow release
nexus_release_paymentMA/CoreExplicit escrow release
nexus_dispute_paymentPayerOpen dispute within dispute window
nexus_resolve_disputeArbiterResolve dispute with bps split
nexus_cancel_paymentMACancel single payment (pre-escrow or on-chain)
nexus_cancel_orderMACancel all payments in a group
nexus_get_merchantUAGet merchant profile + marketplace metadata
nexus_discover_agentsUASearch agent catalog by keyword
nexus_get_agent_skillUAFetch agent's skill.md

7.2 REST API

MethodPathDescription
POST/api/orchestrateVerify quotes, return 402 with payment instruction
GET/api/checkout/:tokenGet group instruction JSON
POST/api/checkout/:token/relay-depositGasless deposit (v4): submit EIP-3009 sig, relayer submits on-chain
POST/api/checkout/:token/confirmLegacy: confirm user-submitted tx_hash
GET/api/paymentsQuery payments (by nexus_payment_id, merchant_order_ref, group_id)
GET/api/payments/:idGet payment details
GET/api/merchant/paymentsMerchant portal: list payments (filter by did, status, since)
POST/api/merchant/confirm-fulfillmentMerchant confirms fulfillment (triggers release)
POST/api/merchant/cancel-paymentCancel single payment
POST/api/merchant/cancel-orderCancel all payments in group
GET/api/agentsList all registered agents
GET/api/agents/:did/skillFetch agent's skill.md

7.3 Checkout Page

PathDescription
/checkout/:groupIdBrowser checkout page (MetaMask integration, Gasless)
/portalCore management portal

7.4 Rate Limiting

  • 30 requests per minute per IP (token bucket)
  • Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

8. AP2 Protocol Integration

8.1 Protocol Detection

typescript
function detectProtocol(body): "ap2" | "nups" {
  if (body.protocol === "ap2") return "ap2";
  if (body.cart_mandates) return "ap2";
  return "nups";
}

8.2 Verifiable Credential Types

Nexus 作为 AP2 Credential Provider,处理以下三种 W3C Verifiable Credential:

CartMandate VC (由 Merchant Agent 签发)

typescript
interface CartMandateSubject {
  merchant_did: string;
  cart_id: string;
  total: string;
  currency: string;
  line_items: CartLineItem[];
  expires_at: string;
}

IntentMandate VC (由 User Agent 签发)

typescript
interface IntentMandateSubject {
  agent_did: string;
  budget: string;
  currency: string;
  merchant_whitelist: string[];
  ttl: number;  // seconds
}

PaymentMandate VC (由 Nexus Credential Provider 签发)

当支付转为 ESCROWED 状态时,Core 签发 PaymentMandate 作为支付凭证:

typescript
interface PaymentMandateSubject {
  credential_provider_did: string;
  nexus_payment_id: string;
  group_id: string;
  payer: Address;
  total_amount: string;
  currency: string;
  chain_id: number;
  escrow_contract: Address;
  deposit_tx_hash: Hex;
}

8.3 Proof Structure (Data Integrity)

typescript
interface JCSProof {
  type: "DataIntegrityProof";
  cryptosuite: "ecdsa-jcs-2022";
  verificationMethod: string;  // DID URL
  proofPurpose: "assertionMethod";
  created: string;             // ISO 8601
  proofValue: string;          // base64url-encoded r||s
}

签名算法: ECDSA P-256, JSON Canonicalization Scheme (JCS)

8.4 Mandate Storage

sql
CREATE TABLE mandate_evidence (
  evidence_id       TEXT PRIMARY KEY,
  nexus_payment_id  TEXT NOT NULL,
  group_id          TEXT NOT NULL,
  mandate_type      TEXT NOT NULL,  -- 'cart_mandate' | 'intent_mandate' | 'payment_mandate'
  mandate_vc        JSONB NOT NULL, -- complete W3C VC JSON
  issuer_did        TEXT NOT NULL,
  subject_hash      TEXT NOT NULL,
  created_at        TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

9. Chain Watcher Specification

9.1 Polling Strategy

  • Poll PlatON RPC every 3 seconds for new blocks
  • Filter NexusEscrow contract logs for:
    • Deposited(paymentId, payer, merchant, amount, orderRef)
    • BatchDeposited(groupId, payer, totalAmount, count)
    • Released(paymentId, merchant, merchantAmount, feeAmount)
    • Refunded(paymentId, payer, amount)
    • Disputed(paymentId, payer, reason)
    • Resolved(paymentId, merchantBps, merchantAmount, payerAmount)

9.2 Event Processing

On Deposited event:
  1. Match paymentId to payment record (payment_id_bytes32)
  2. Check if payment is EXPIRED → if so, auto-refund (submit refund tx)
  3. Transition payment: CREATED -> ESCROWED
  4. Record deposit_tx_hash, release_deadline, dispute_deadline
  5. If protocol === "ap2": issue PaymentMandate VC
  6. Send webhook: payment.escrowed

On Released event:
  1. Match paymentId to payment record
  2. Transition payment: ESCROWED -> SETTLED
  3. Record release_tx_hash, protocol_fee
  4. Send webhook: payment.settled

On Refunded event:
  1. Match paymentId to payment record
  2. Check cancel context → CANCELLED or REFUNDED
  3. Record refund_tx_hash
  4. Send webhook: payment.refunded or payment.cancelled

10. Relayer Service

The Relayer is a server-side service that submits transactions on behalf of the Core:

OperationTriggerGas Payerv3v4
batchDepositGasless(...)User signs EIP-3009RelayerN/ANEW
release(paymentId)Merchant confirms fulfillmentRelayerYesYes
refund(paymentId)Timeout auto-refundRelayerYesYes
cancelByOperator(paymentId)Merchant/operator cancelRelayerN/ANEW
resolve(paymentId, bps)Arbiter resolves disputeRelayerN/ANEW

Relayer wallet: 0xf7EA5d3f0Bf8185c4f3C2F405D9a71009CF4D920 (also coreOperator on contract)

Retry logic: exponential backoff (1s, 3s, 9s), max timeout 120s per transaction.

11. Webhook Notification

See RFC-009 v1.2 for full specification. Key points:

11.1 Event Types (11)

EventTrigger
payment.createdPayment created
payment.escrowedFunds deposited in escrow
payment.settledEscrow released to merchant
payment.completedMerchant confirmed fulfillment
payment.expiredPayment timed out
payment.failedOn-chain transaction reverted
payment.refundedEscrow refunded to payer (timeout/dispute)
payment.cancelledPayment cancelled by merchant/operator
dispute.openedPayer opened dispute
dispute.resolvedArbiter resolved dispute

11.2 Security

  • HMAC-SHA256 signature in X-Nexus-Signature header
  • Exponential backoff retry (6 attempts: 10s, 30s, 2min, 10min, 30min)
  • Idempotent via event_id
  • Delivery logged in webhook_delivery_logs table

12. Database Schema

Core tables:

  • payments: Payment order records (13-state machine, escrow fields, protocol field: 'nups' | 'ap2')
  • payment_groups: Group aggregation records (batch deposits)
  • payment_events: Event sourcing (append-only, 15 event types)
  • merchant_registry: Merchant identity + marketplace metadata + ap2_did
  • webhook_delivery_logs: Webhook delivery tracking with retry state
  • mandate_evidence: AP2 Verifiable Credential storage (v4 新增)

13. Deployed Addresses

ContractAddressType
NexusEscrow (Proxy)0xeB33a9C2b4c7D3F44Fd5514F90C355AF6bb79236UUPS Proxy
NexusEscrow (Impl v4.3.0)0xF6ED311f8ea594572872E78DB945277ab37ECE37Implementation
XSGD0x0Fd437613dE3d14F4dDaB8331DC0f2C0C543BdD0ERC-3009 Token
USDC (legacy)0xFF8dEe9983768D0399673014cf77826896F97e4dERC-3009 Token
Relayer / Core Operator0xf7EA5d3f0Bf8185c4f3C2F405D9a71009CF4D920EOA

14. Contract Version History

VersionDateKey Changes
v2.0.02026-02-24Initial escrow (depositWithAuthorization, release, refund, dispute, resolve)
v3.0.02026-02-24Batch deposits, RESOLVED_SPLIT, non-upgradeable
v4.0.02026-02-26UUPS proxy, batchDepositWithGroupApproval, refundUnresolvedDispute, MAX_BATCH_SIZE=20, feeBps snapshot
v4.1.02026-03-12cancelByOperator() — immediate refund by operator/merchant
v4.3.02026-04-01batchDepositGasless() — relayer-submitted gasless deposits, setToken() for multi-token

15. Upgrade Path

Current (this RFC)Future
Gasless Escrow (single chain)Hub-Spoke cross-chain (PlatON + Base + ETH)
Local merchant_registryOn-chain NexusMerchantRegistry
Basic signature verificationFull RiskGatekeeper with Permit (RFC-006)
PlatON Devnet onlyMulti-chain production
XSGD/USDC stablecoinsMulti-asset (any ERC-3009 token)
AP2 Credential ProviderFull AP2 ecosystem participation
Browser checkout + MCP@nexus/buyer-skills SDK (RFC-011)

Copyright (c) 2026 Nexus Protocol. All Rights Reserved.