Python SDK 参考#

Python SDK 参考(适用于 exact、aggr_deferred)#

包名:okxweb3-app-x402pip install okxweb3-app-x402。 全异步(async def),另提供 *Sync 同步变体。当前文档聚焦卖家(resource server)+ facilitator 能力。

Packages#

模块导入路径描述
核心x402x402ResourceServer / x402Facilitator / x402Client(及 *Sync)、schema 类型、错误
HTTPx402.httpOKXFacilitatorClient / OKXFacilitatorConfig / OKXAuthConfigx402HTTPResourceServerPaymentOption / RouteConfig、header 工具
FastAPIx402.http.middleware.fastapiPaymentMiddlewareASGIpayment_middleware
Flaskx402.http.middleware.flaskPaymentMiddlewarepayment_middleware
EVM 机制x402.mechanisms.evm.exact.serverExactEvmScheme(exact 方案,server 端)
EVM 机制x402.mechanisms.evm.deferred.serverAggrDeferredEvmScheme(aggr_deferred 方案,server 端;server 侧逻辑同 exact)
适配器x402.adaptersX402Adapter(Payment Router 集成)

核心组件#

x402ResourceServer#

服务端组件,保护资源、构造 payment requirements、转发 facilitator 验证/结算。

python
from x402 import x402ResourceServer

class x402ResourceServer:
    def __init__(
        self,
        facilitator_clients: FacilitatorClient | list[FacilitatorClient] | None = None,
    ) -> None: ...

    def register(self, network: str, scheme) -> Self: ...
    def initialize(self) -> None: ...

    def on_before_verify(self, hook) -> Self
    def on_after_verify(self, hook) -> Self
    def on_verify_failure(self, hook) -> Self
    def on_before_settle(self, hook) -> Self
    def on_after_settle(self, hook) -> Self
    def on_settle_failure(self, hook) -> Self

network 用 CAIP-2 标识(X Layer = "eip155:196")。

x402Facilitator / FacilitatorClient#

python
from x402 import x402Facilitator

class x402Facilitator:
    def register(self, networks: list[str], scheme) -> Self: ...
    async def verify(self, payload, requirements) -> VerifyResponse: ...
    async def settle(self, payload, requirements) -> SettleResponse: ...

x402Client#

python
from x402 import x402Client

class x402Client:
    def __init__(self, payment_requirements_selector=None) -> None: ...
    def register(self, network: str, client_scheme) -> Self: ...
    async def create_payment_payload(self, payment_required) -> PaymentPayload: ...

选择器辅助:default_payment_selectorprefer_networkprefer_schememax_amount


OKX Facilitator 客户端(x402.http)#

对接 OKX /api/v6/pay/x402/*(verify / settle / supported),HMAC-SHA256 认证。同步变体为 OKXFacilitatorClientSync

python
from x402.http import OKXAuthConfig, OKXFacilitatorClient, OKXFacilitatorConfig

@dataclass
class OKXAuthConfig:
    api_key: str
    secret_key: str
    passphrase: str

@dataclass
class OKXFacilitatorConfig:
    auth: OKXAuthConfig
    base_url: str = "https://web3.okx.com"
    sync_settle: bool = True
    timeout: float = 30.0
    http_client: Any = None

class OKXFacilitatorClient:
    def __init__(self, config: OKXFacilitatorConfig) -> None: ...
    async def verify(self, payload, requirements) -> VerifyResponse: ...
    async def settle(self, payload, requirements) -> SettleResponse: ...
    def get_supported(self) -> SupportedResponse: ...
    async def aclose(self) -> None: ...

base_url 默认值即常量 OKX_DEFAULT_BASE_URLsync_settle=True 为同步结算;False 为异步结算(通过 GET /settle/status 轮询)。

认证头:OK-ACCESS-KEY / OK-ACCESS-SIGN / OK-ACCESS-TIMESTAMP / OK-ACCESS-PASSPHRASE;签名 = Base64(HMAC-SHA256(secretKey, timestamp + METHOD + path + body))


EVM 支付方案(schemes)#

python
from x402.mechanisms.evm.exact.server import ExactEvmScheme
from x402.mechanisms.evm.deferred.server import AggrDeferredEvmScheme

class ExactEvmScheme:
    scheme = "exact"
    def __init__(self) -> None: ...
    def register_money_parser(self, parser) -> "ExactEvmScheme": ...
    def parse_price(self, price, network) -> AssetAmount: ...
    def enhance_payment_requirements(self, requirements, supported_kind, extension_keys) -> PaymentRequirements: ...

class AggrDeferredEvmScheme:
    scheme = "aggr_deferred"
    def __init__(self) -> None: ...

register_money_parser 用于自定义 USD→atomic 金额换算。AggrDeferredEvmScheme 的 server 侧委托给 ExactEvmScheme,仅 facilitator 结算模式不同。

注册:server.register("eip155:196", ExactEvmScheme())


路由配置类型(x402.http)#

python
from x402.http import PaymentOption, RouteConfig

@dataclass
class PaymentOption:
    scheme: str
    pay_to: str | DynamicPayTo
    price: Price | DynamicPrice
    network: Network
    max_timeout_seconds: int | None = None
    extra: dict[str, Any] | None = None

@dataclass
class RouteConfig:
    accepts: PaymentOption | list[PaymentOption]
    resource: str | None = None
    description: str | None = None
    mime_type: str | None = None
    custom_paywall_html: str | None = None
    unpaid_response_body: UnpaidResponseBody | None = None
    settlement_failed_response_body: SettlementFailedResponseBody | None = None
    extensions: dict[str, Any] | None = None
    hook_timeout_seconds: float | None = None

RoutesConfig = dict[str, RouteConfig] | RouteConfig

PaymentOption 字段说明:scheme"exact" | "aggr_deferred";pay_to 为收款地址;price"$0.00001";network"eip155:196"RoutesConfig 的 key 形如 "GET /resource"


HTTP 中间件#

FastAPI#

python
from x402.http.middleware.fastapi import PaymentMiddlewareASGI

app.add_middleware(
    PaymentMiddlewareASGI,
    routes=routes,
    server=server,
)

或函数式:payment_middleware(routes, server, paywall_config=None, paywall_provider=None, sync_facilitator_on_start=True)

Flask#

python
from x402.http.middleware.flask import PaymentMiddleware, payment_middleware

中间件行为:无支付头 → 402 Payment Required(PAYMENT-REQUIRED 头携带 requirements);携带 PAYMENT-SIGNATURE / X-PAYMENT → verify(+ settle),成功放行并回 PAYMENT-RESPONSE 头。


响应 schema(x402.schemas / 顶层导出)#

python
class VerifyResponse(BaseX402Model):
    is_valid: bool
    invalid_reason: str | None = None
    invalid_message: str | None = None
    payer: str | None = None

class SettleResponse(BaseX402Model):
    success: bool
    error_reason: str | None = None
    error_message: str | None = None
    status: str | None = None
    payer: str | None = None
    transaction: str
    network: Network

class SettleStatusResponse(BaseX402Model):
    success: bool
    status: str | None = None
    error_reason / error_message / payer / transaction / network: ...

class SupportedKind(BaseX402Model):
    x402_version: int
    scheme: str
    network: Network
    extra: dict[str, Any] | None = None

class SupportedResponse(BaseX402Model):
    kinds: list[SupportedKind]
    extensions: list[str] = []
    signers: dict[str, list[str]] = {}

SettleStatusResponse 用于 GET /settle/status 轮询;其 status"pending" | "success" | "failed"

其他 schema:PaymentRequirements / PaymentRequired / PaymentPayload(及 *V1)、AssetAmountResourceInfoNetwork / Money / Price、常量 X402_VERSION


Header 常量与工具(x402.http)#

python
PAYMENT_SIGNATURE_HEADER = "PAYMENT-SIGNATURE"
PAYMENT_REQUIRED_HEADER = "PAYMENT-REQUIRED"
PAYMENT_RESPONSE_HEADER = "PAYMENT-RESPONSE"
X_PAYMENT_HEADER = "X-PAYMENT"
X_PAYMENT_RESPONSE_HEADER = "X-PAYMENT-RESPONSE"
HTTP_STATUS_PAYMENT_REQUIRED = 402

encode/decode_payment_signature_header(...)
encode/decode_payment_required_header(...)
encode/decode_payment_response_header(...)
detect_payment_required_version(...)
safe_base64_encode / safe_base64_decode

PAYMENT-SIGNATURE 为 v2 client → server 头;X-PAYMENT 为 v1 头;PAYMENT-REQUIRED 为 server 402 → client 头。


X402Adapter(x402.adapters)— Payment Router 集成#

python
from x402.adapters import X402Adapter
from x402.http import x402HTTPResourceServer

class X402Adapter:
    def __init__(self, server: x402HTTPResourceServer, priority: int = 20) -> None: ...
    @property
    def name(self) -> str: ...
    @property
    def priority(self) -> int: ...
    def detect(self, request) -> bool: ...
    async def get_challenge(self, request, cfg) -> dict[str, str]: ...
    async def handle(self, request, cfg) -> Any: ...

name 返回 "x402";priority 默认 20(MPP=10 优先级更高);detect 通过检测 PAYMENT-SIGNATURE / X-Payment 头判断。

构造 x402HTTPResourceServer:

python
from x402.server import x402ResourceServer
from x402.http import x402HTTPResourceServer

resource = x402ResourceServer(facilitator)
resource.register("eip155:196", ExactEvmScheme())
resource.register("eip155:196", AggrDeferredEvmScheme())
http_server = x402HTTPResourceServer(resource, routes={})
x402_adapter = X402Adapter(http_server)

双协议(MPP + x402)组合见《双协议》示例。


Python SDK 参考(适用于 charge、session)#

Packages#

Package导入路径描述
mpp (pympp)mpp协议无关核心层:Mpp 协调器、Challenge / Credential / Receipt、header 解析、Store 协议、错误类型
mpp_evmmpp_evmEVM 支付方法:EvmMethodChargeIntent / SessionIntent、EIP-712 签名/验签、Voucher、Authorization、常量
mpp_evm.saclientmpp_evm.saclientSA-API 客户端:SAClient 协议与 OKXSAClient 实现,HMAC-SHA256 认证
mpp_evm.storempp_evmkey-value store:FileStoreInMemoryChannelStoreChannelState
mpp_evm.signermpp_evm.signerSigner 协议、PrivateKeySigner(基于 eth_account)
mpp_evm.adaptersmpp_evmPayment Router 适配器:MppAdapterMppRouteConfig
HTTP 集成mpp.server.mpp.Mpp.pay()FastAPI 装饰器(verify-or-challenge),或 paymentrouter 多协议网关

Python MPP SDK 当前提供卖家(server-side)能力,全异步(async def)。EvmMethod.create_credential 会抛 NotImplementedError——买家凭证生成需要 EVM 钱包/签名器。

架构与 Go 略有差异:协议无关核心在 pympp(mpp 包)的 Mpp 上,EVM 具体实现在 mpp_evm,二者通过 EvmMethod(intents={...}) 组合,路由保护用 @mpp.pay() 装饰器而非框架中间件。


核心常量(mpp_evm)#

python
from mpp_evm import (
    X_LAYER_CHAIN_ID,
    DEFAULT_ESCROW_CONTRACT,
    DEFAULT_DOMAIN_NAME,
    DEFAULT_DOMAIN_VERSION,
    METHOD_NAME_EVM,
    ACTION_OPEN,
    ACTION_TOP_UP,
    ACTION_VOUCHER,
    ACTION_CLOSE,
    ACTION_SETTLE,
    STATUS_OPEN,
    STATUS_CLOSED,
)

常量取值:X_LAYER_CHAIN_ID = 196(X Layer);DEFAULT_ESCROW_CONTRACT 为默认 Escrow 合约地址;DEFAULT_DOMAIN_NAME = "EVM Payment Channel";DEFAULT_DOMAIN_VERSION = "1";METHOD_NAME_EVM = "evm";action 常量 ACTION_OPEN = "open" / ACTION_TOP_UP = "topUp" / ACTION_VOUCHER = "voucher" / ACTION_CLOSE = "close" / ACTION_SETTLE = "settle";status 常量 STATUS_OPEN = "open" / STATUS_CLOSED = "closed"


mpp 核心层(pympp)#

Mpp#

服务端支付协调器,绑定 method + realm + secret_key,做无状态 challenge 验证。

python
from mpp.server.mpp import Mpp

class Mpp:
    def __init__(
        self,
        method: Method,
        realm: str,
        secret_key: str,
        defaults: dict[str, Any] | None = None,
        store: Store | None = None,
    ) -> None: ...

    @classmethod
    def create(cls, method, realm=None, secret_key=None, store=None) -> "Mpp": ...

参数:method 即支付方法(如 EvmMethod);realm 为 WWW-Authenticate 保护域;secret_key 为 HMAC 密钥,用于签发并验证 challenge ID;store 为可选的 replay 保护 store,会自动注入到 intent。realm / secret_key 省略时从环境变量(MPP_REALM / MPP_SECRET_KEY)自动探测。

charge() 低层 API
python
async def charge(
    self,
    authorization: str | None,
    amount: str,
    *,
    currency: str | None = None,
    recipient: str | None = None,
    expires: str | None = None,
    description: str | None = None,
    memo: str | None = None,
    splits: list[dict[str, str]] | None = None,
    fee_payer: bool = False,
    chain_id: int | None = None,
    extra: dict[str, str] | None = None,
) -> Challenge | tuple[Credential, Receipt]:
    ...

返回 Challenge(需要支付)或 (Credential, Receipt)(验证通过)。参数说明:authorization 为 Authorization header 值(None 表示未带凭证);amount 为人类可读金额(如 "0.50"),按 method.decimals 转 base units;currency / recipient 覆盖 method 默认值;expires 为 ISO 8601 时间,默认 now+5min;memo 为 32-byte hex memo;splits 为分账项;fee_payersplits 互斥。splits 元素形如 {"amount": "20", "recipient": "0x...", "memo": "partner-a"}

pay() 装饰器(推荐)

包裹受保护端点的 verify-or-challenge 流程。Handler 必须credentialreceipt 形参名注入。amount 为人类可读金额;intent"charge""session"

python
def pay(
    self,
    amount: str,
    *,
    intent: str = "charge",
    currency: str | None = None,
    recipient: str | None = None,
    description: str | None = None,
    expires_in: timedelta | None = None,
    chain_id: int | None = None,
    extra: dict[str, str] | None = None,
) -> Callable: ...

注意:pay() 装饰器不支持 splits。需要分账时使用低层 Mpp.charge(..., splits=[...])

事件钩子
python
def on(self, name: str, handler: EventHandler) -> Unsubscribe
def on_challenge_created(self, handler) -> Unsubscribe
def on_payment_success(self, handler) -> Unsubscribe
def on_payment_failed(self, handler) -> Unsubscribe

Challenge / Credential / Receipt#

python
from mpp import Challenge, Credential, Receipt

class Challenge:
    def to_www_authenticate(self, realm: str) -> str: ...

class Receipt:
    status: Literal["success"]
    timestamp: datetime
    reference: str
    method: str = "tempo"
    external_id: str | None = None
    extra: dict[str, Any] | None = None

    @classmethod
    def from_payment_receipt(cls, header: str) -> "Receipt": ...
    def to_payment_receipt(self) -> str: ...
    @classmethod
    def success(cls, reference, timestamp=None, method="tempo", external_id=None) -> "Receipt": ...

Challenge.to_www_authenticate 生成 WWW-Authenticate header 值;Receipt.to_payment_receipt 生成 Payment-Receipt header 值。

错误类型(mpp.errors)#

PaymentError 及子类:BadRequestErrorInvalidChallengeErrorInvalidPayloadErrorMalformedCredentialErrorPaymentActionRequiredErrorPaymentExpiredErrorPaymentInsufficientErrorPaymentMethodUnsupportedErrorPaymentRequiredErrorVerificationFailedError


EvmMethod(mpp_evm)#

把 charge + session intent 组合成单一 method 注册项(实现 pympp Method 协议)。

python
from mpp_evm import EvmMethod

class EvmMethod:
    name: str = "evm"
    def __init__(self, *, intents: dict[str, Any] | None = None) -> None: ...

    @property
    def intents(self) -> dict[str, Any]: ...
    async def create_credential(self, challenge) -> Any: ...

使用时把 currency / recipient / decimals / chain_id 直接挂到实例上,供 Mpp.charge / pay 读取:

python
method = EvmMethod(intents={"charge": charge_intent, "session": session_intent})
method.currency = "0x...token"
method.recipient = "0x...payee"
method.decimals = 6
method.chain_id = 196

Charge — ChargeIntent#

实现 pympp Intent 协议,把 credential 透传给 SA-API。

python
from mpp_evm.charge.intent import ChargeIntent

class ChargeIntent:
    name: str = "charge"
    def __init__(
        self,
        *,
        sa_client: SAClient,
        chain_id: int = X_LAYER_CHAIN_ID,
        recipient: str = "",
        fee_payer: bool = False,
    ) -> None: ...

    def challenge_method_details(self) -> dict[str, Any]: ...
    async def verify(self, credential: Credential, request: dict) -> Receipt: ...

fee_payer=True 时由 seller 主动 broadcast EIP-3009(transaction 模式)。

payload["type"] 路由:

  • "transaction"SAClient.settle(SA-API 链上 broadcast transferWithAuthorization)
  • "hash"SAClient.verify_hash(client 已自行 broadcast,SA-API 验证 tx hash)

也有 EvmChargeMethod(dataclass)封装,字段同上;ChargeIntent 为常用入口。


Session — SessionIntent#

实现 pympp Intent 协议。维护本地 channel state、voucher 本地验签 + 累计扣费、商户主动 settle/close。

构造#

python
from mpp_evm.session.intent import SessionIntent

class SessionIntent:
    name: str = "session"
    def __init__(
        self,
        *,
        sa_client: SAClient,
        recipient: str,
        signer: Signer | None = None,
        store: ChannelStore | None = None,
        chain_id: int = X_LAYER_CHAIN_ID,
        escrow_contract: str = DEFAULT_ESCROW_CONTRACT,
        per_request_cost: int | None = None,
        min_voucher_delta: int | None = None,
        nonce_provider: NonceProvider | None = None,
        deadline: int | None = None,
        domain_name: str = DEFAULT_DOMAIN_NAME,
        domain_version: str = DEFAULT_DOMAIN_VERSION,
        fee_payer: bool = False,
    ) -> None: ...

参数说明:sa_client 必填;recipient 必填,为 Payee 钱包地址;signer 在 settle/close 时必需,传 None 则禁用 settle/close;store 默认 InMemoryChannelStore();per_request_cost 为每次请求扣费(base units);min_voucher_delta 为连续 voucher 的最小递增量,默认 0;nonce_provider 默认 UuidNonceProvider;deadline 默认 U256 MAX(不过期)。

接口与业务方法#

python
def challenge_method_details(self) -> dict[str, Any]: ...
async def verify(self, credential, request: dict) -> Any: ...
def respond(self, credential, receipt) -> Any: ...

async def settle_channel(self, channel_id: str) -> dict: ...
async def close_channel(self, channel_id: str) -> dict: ...

商户主动结算:settle_channel 读取最高 voucher → 签 SettleAuthorization → 调 SA settle;close_channelCloseAuthorization → 调 SA close → 删除本地 store。

Session action 路由(verify 内部)#

payload.action行为
"open"验证 → SA session/open → 写本地 store
"topUp"验证 → SA session/topUp → 累加本地 deposit
"voucher"本地验签 + 升 highest → deduct per_request_cost
"close"验证 voucher sig → 签 CloseAuthorization → SA session/close
"settle"签 SettleAuthorization → SA session/settle

Signer(mpp_evm.signer)#

python
from mpp_evm.signer import Signer, PrivateKeySigner

@runtime_checkable
class Signer(Protocol):
    def sign(self, msg_hash: bytes) -> bytes: ...
    @property
    def address(self) -> str: ...

class PrivateKeySigner:
    def __init__(self, account: LocalAccount) -> None: ...
    @classmethod
    def from_hex(cls, hex_key: str) -> "PrivateKeySigner": ...

sign 输入 32-byte hash,输出 65-byte r||s||v(v∈{27,28});address 为 0x 前缀的 checksummed 地址。PrivateKeySigner.from_hex 接受带或不带 0x 前缀的私钥。


saclient#

SAClient 协议#

python
from mpp_evm.saclient.client import SAClient

@runtime_checkable
class SAClient(Protocol):
    async def settle(self, req: ChargeSettleRequest) -> ChargeReceipt: ...
    async def verify_hash(self, req: ChargeVerifyHashRequest) -> ChargeReceipt: ...
    async def session_open(self, req: SessionOpenRequest) -> SessionReceipt: ...
    async def session_top_up(self, req: SessionTopUpRequest) -> SessionReceipt: ...
    async def session_settle(self, req: SessionSettleRequest) -> SessionReceipt: ...
    async def session_close(self, req: SessionCloseRequest) -> SessionReceipt: ...
    async def session_status(self, channel_id: str) -> SessionStatus: ...

方法分组:settle / verify_hash 为 Charge(client-facing,透传 credential);session_open / session_top_up 为 Session client-facing;session_settle / session_close 为 Session merchant-facing(server 构造请求);session_status 为 Session 只读查询。

OKXSAClient#

python
from mpp_evm.saclient.client import OKXSAClient

class OKXSAClient:
    def __init__(
        self,
        *,
        base_url: str,
        api_key: str,
        secret_key: str,
        passphrase: str,
        http_client: httpx.AsyncClient | None = None,
        timeout: float = 30.0,
    ) -> None: ...

调用的端点#

SAClient 方法OKX 路径
settle()POST /api/v6/pay/mpp/charge/settle
verify_hash()POST /api/v6/pay/mpp/charge/verifyHash
session_open()POST /api/v6/pay/mpp/session/open
session_top_up()POST /api/v6/pay/mpp/session/topUp
session_settle()POST /api/v6/pay/mpp/session/settle
session_close()POST /api/v6/pay/mpp/session/close
session_status()GET /api/v6/pay/mpp/session/status?channelId=...

OKX 响应包装在 {"code": 0, "data": {...}, "msg": ""} 中,客户端自动解包。HMAC 认证头:OK-ACCESS-KEY / OK-ACCESS-SIGN / OK-ACCESS-TIMESTAMP / OK-ACCESS-PASSPHRASE,签名 = Base64(HMAC-SHA256(secretKey, timestamp + METHOD + requestPath + body))


store#

ChannelStore 协议#

python
@runtime_checkable
class ChannelStore(Protocol):
    async def get(self, key: str) -> Any | None: ...
    async def put(self, key: str, value: Any) -> None: ...
    async def delete(self, key: str) -> None: ...

ChannelState#

python
@dataclass
class ChannelState:
    channel_id: str
    chain_id: int
    escrow_contract: str
    payer: str
    payee: str
    token: str = ""
    authorized_signer: str = ""
    deposit: str = "0"
    highest_voucher_amount: str = "0"
    highest_voucher_signature: str = ""
    min_voucher_delta: str = "0"
    spent: str = "0"
    units: int = 0
    finalized: bool = False
    close_requested_at: int = 0
    created_at: str = ""

字段说明:deposit 为十进制整数字符串;highest_voucher_amountspent 均单调非递减,available = highest_voucher_amount - spent。提供 to_dict() / from_dict()(camelCase JSON,跨语言互通)。

FileStore#

python
from mpp_evm import FileStore

class FileStore:
    def __init__(self, directory: str) -> None: ...
    async def get(self, key) / put(self, key, value) / delete(self, key)
    async def put_if_absent(self, key, value) -> bool
  • 构造时自动 mkdir,每个 key 落到 {dir}/{key}.json
  • 写入用 tmp + os.replace 原子落盘,pretty-printed JSON
  • key 做路径越界防护(拒绝 ../ / 绝对路径)
  • 跨语言兼容(Go/Rust/TS 共享同一目录)

InMemoryChannelStore#

SessionIntent 未传 store 时的默认实现。进程内 dict,put/get 做 deep-copy 隔离。重启即丢,不跨进程/worker;生产或多 worker 部署请用 FileStore 或自定义共享后端。


MppAdapter(mpp_evm.adapters)— Payment Router 集成#

python
from mpp_evm import MppAdapter, MppRouteConfig

@dataclass
class MppRouteConfig:
    intent: str = ""
    amount: str = ""
    currency: str = ""
    decimals: int = 6
    description: str = ""
    external_id: str = ""
    realm: str = ""
    unit_type: str = ""
    suggested_deposit: str = ""

class MppAdapter:
    def __init__(self, mpp: Mpp, priority: int = 10) -> None: ...
    @property
    def name(self) -> str: ...
    @property
    def priority(self) -> int: ...
    def detect(self, request) -> bool: ...
    async def get_challenge(self, request, cfg) -> dict[str, str]: ...
    async def handle(self, request, cfg) -> Any: ...

MppRouteConfig 字段:intent"charge""session";amount 为人类可读金额;currency 为 token 合约地址;unit_type 为 session 计费单位(如 "request");suggested_deposit 为 session 建议初始 deposit(base units)。MppAdapter.name 返回 "mpp",priority 默认 10;detect 检测 "Authorization: Payment ..." 头。

MppRouteConfigsplits 字段;分账请走低层 Mpp.charge(..., splits=[...])


HTTP 集成#

FastAPI(@mpp.pay() 装饰器)#

python
from fastapi import FastAPI, Request
from mpp import Credential, Receipt

app = FastAPI()

@app.get("/api/premium")
@mpp.pay(amount="0.00001", intent="charge", description="One premium API call")
async def premium(request: Request, credential: Credential, receipt: Receipt) -> dict:
    return {"data": "premium content", "receipt": {"reference": receipt.reference, "status": receipt.status}}

注意:Receipt 是 frozen dataclass(无 model_dump),需直接取字段或调用 receipt.to_payment_receipt()

行为:

  1. 无 Authorization → 返回 402 + WWW-Authenticate challenge
  2. 有 Authorization → 验证;成功则注入 credential / receipt 并执行 handler,设置 Payment-Receipt
  3. 失败 → 对应 HTTP 错误码

多协议(Payment Router)#

paymentrouter.PaymentGate + MppAdapter / X402Adapter,详见《双协议》示例与 Python x402 SDK 参考。


SA-API 错误码映射#

mpp_evm.errors.map_sa_error(code, msg) 把 SA code 映射为对应的 EvmPaymentError 子类,未命中时默认 InternalSAError:

SA code含义异常类型
70000参数无效BadRequestError
70001链不在支持列表InternalSAError
70002付款方黑名单MalformedCredentialError
70003credential 无效MalformedCredentialError
70004签名验证失败InvalidSignatureError
70005分账总额超过 amountInvalidSplitError
70006分账条数超限InvalidSplitError
70007交易未在链上确认InternalSAError
70008channel 已关闭ChannelClosedError
70009challenge 无效InvalidChallengeError
70010channelId 不存在ChannelNotFoundError
70011Escrow grace period 过短InternalSAError
70012金额超过存款AmountExceedsDepositError
70013voucher 递增量过小DeltaTooSmallError
70014channel 处于 CLOSINGChannelClosedError
8000API 内部错误InternalSAError

错误类型一览:EvmPaymentError(基类)、AmountExceedsDepositErrorBadRequestErrorChannelClosedErrorChannelNotFoundErrorDeltaTooSmallErrorInsufficientBalanceErrorInternalSAErrorInvalidChallengeErrorInvalidSignatureErrorInvalidSplitErrorSignerMismatchError