/** * paygate v3 — core x402 paywall logic. * * v3 change: one config knob (`merchantId`). The SDK fetches the merchant's * capability manifest from api.arispay.app on boot, caches it for 5 minutes, * and derives wallet/network/asset/trustMinTier from the response. * * v2 callers (who passed `wallet` + `network` directly) are supported via * a compatibility shim until v4. * * See docs/paygate-v2.md §7. */ import { type FiatAccept, type FiatCurrency } from "./fiat.js"; export declare const DEFAULT_FACILITATOR = "https://facilitator.arispay.app"; export declare const DEFAULT_API_URL = "https://api.arispay.app"; /** * The metered balance-rail scheme identifier (ArisPay balance-rail * metering spec §8.1). Literal repeated here — this package publishes * standalone and cannot depend on the workspace-only shared package. */ export declare const ARISPAY_METERED_SCHEME = "arispay-metered"; export declare const CAPABILITY_CACHE_TTL_MS: number; export declare const SOLANA_MAINNET_CAIP2 = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; export declare const SOLANA_DEVNET_CAIP2 = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"; export type SettlementFamily = "evm" | "svm"; /** `svm` for `solana:*` CAIP-2 ids, `evm` for everything else. */ export declare function networkFamily(network: string): SettlementFamily; export type PaygateCurrency = "USD" | "EUR"; /** * Build the response headers for an x402 402 challenge in the canonical * wire format observed across Bazaar-listed endpoints. * * - `payment-required: ` — primary, what 95.3% of live * x402 endpoints emit (audited 2026-04-30; see * docs/x402-wire-format-audit.md). base64 also dodges the Latin1 * `ByteString` constraint on Node response headers, so a description * containing an em dash or smart quote no longer 500s the response. * * - `X-Payment-Requirements: ` — * paygate's pre-v5.1 header name, kept for one minor cycle as a * deprecated alias so existing `payagent` consumers (and any other * client that reads our legacy emit) keep working. Will be removed * in paygate v6. Strip non-printable-ASCII defensively in case a * description picks up Unicode in the meantime. * * - `WWW-Authenticate: x402` — RFC 7235 challenge advertisement. * * - `Access-Control-Expose-Headers` — without this, browser-based * agents (Anthropic MCP-in-browser, Chrome WebMCP, …) can't read * `payment-required` cross-origin. 80% of live endpoints don't * bother; we do. * * Exported for tests; consumers should not rely on this signature. */ export declare function buildChallengeHeaders(challengeBody: Record, accepts: Array): Record; /** * v3 config. * * New callers pass `merchantId` (required). The SDK will fetch the * merchant's capability manifest and derive everything else. * * v2 callers can still pass `wallet` + `network`; the shim translates * this into a direct PaymentRequirements build without an apps/api * lookup. Emits a `console.warn` on first use suggesting migration. */ export interface PaygateConfig { /** v3 — the merchant id (a cuid from /v1/merchants/me). */ merchantId?: string; /** * v2 compat — receiving wallet address: a checksummed `0x` address on * EVM networks, a base58 owner address on Solana (the facilitator settles * to its associated token account). * @deprecated Pass `merchantId` instead. Will be removed in v4. */ wallet?: string; /** * CAIP-2 or short name (`base`, `ethereum`, `polygon`, `bsc`, `solana`, * `solana-devnet`). In the v2-compat path it selects the settlement * network directly. In the v3 (`merchantId`) path it is optional and * narrows rail selection to the manifest rail on that network — omit it * to keep the first rail of the requested currency. */ network?: string; /** * Settlement currency. Default `"USD"` (USDC). `"EUR"` settles in Circle * EURC; `priceCents` then means integer EUR cents (the cents → 6-decimals * conversion factor is the same 10^4 for both assets). * * v2-shim path: resolves the EURC address for the network (Base mainnet + * Base Sepolia known; other networks need an explicit `asset`). * v3 path: selects the merchant-manifest rail whose `assetName` matches * (`EURC` for EUR); errors clearly if the merchant has no EUR rail. */ currency?: PaygateCurrency; /** * Settlement token contract address. Auto-derived from `network` + * `currency` if omitted. Ignored in the v3 path (merchant manifest is * authoritative). */ asset?: string; /** Override the default facilitator URL. */ facilitatorUrl?: string; /** * Self-settle: submit the settlement transaction from your own funded key * instead of the facilitator's relayer. You pay chain gas (~$0.001 per * settle on Base) and nobody else — the never-sponsor / never-charge * policy's direct-payout path. Verification still runs against the * facilitator (free — no chain writes). Replay protection is on-chain: * EIP-3009 consumes the authorization nonce, so a duplicate submit * reverts. */ selfSettle?: { /** 0x-hex private key of any funded EOA. Pays gas only; never receives or holds customer funds. */ privateKey: string; /** JSON-RPC URL for the settlement network. Defaults to the public Base RPCs for `eip155:8453` / `eip155:84532`. */ rpcUrl?: string; }; /** Override the default apps/api URL (for staging / self-hosted). */ apiUrl?: string; /** Facilitator call timeout in ms. Default 30s. */ timeout?: number; /** Merchant-capability cache TTL in ms. Default 5 min. */ cacheTtlMs?: number; /** Discovery probe rate limit. */ discoveryRateLimit?: DiscoveryRateLimitConfig; /** * Re-serve a payer whose earlier delivery failed after settlement. * Default `true`. When the facilitator answers a duplicate settle with * `nonce_already_used` AND a `priorSettlement` that matches this route's * payTo, asset, amount, network, and payer, the request counts as paid * and the handler runs again. The facilitator bounds this to a few * re-deliveries inside the payer-side idempotency window. Set `false` * for handlers with side effects that must never run twice per payment * (a duplicate then stays a 402, as before). */ redelivery?: boolean; /** * Advertise the metered balance-rail (`arispay-metered`) as a second * `accepts[]` entry on every 402 (ArisPay balance-rail metering §5.4, * §8.1). Opt-in: set true once the merchant is enrolled as a metering * provider on ArisPay. The entry's terms point buyers at the platform * balance + session endpoints; per-call settlement then runs through * the provider's own usage submission, not this middleware. */ meteredRail?: boolean; /** * Also accept x402 (USDC / EURC on-chain). Default `false`: since v6 a * `merchantId` merchant accepts fiat only (docs/specs/fiat-402-v1.md; * x402 is frozen, owner decision 2026-10-10). Set `true` to keep * offering the x402 `exact` rail next to fiat. The v2-compat * `wallet` + `network` config has no merchant account and stays * x402-only. */ x402?: boolean; } export interface RoutePrice { /** Price in integer USD cents. 10 = $0.10. */ priceCents?: number | ((request: unknown) => number | Promise); /** * v2 compat — floating-point USDC price. * @deprecated Use `priceCents` (integer). Will be removed in v4. */ price?: number | ((request: unknown) => number | Promise); /** Optional description shown in the 402 response and the discovery catalog. */ description?: string; /** Optional MIME type of the paid response, shown in the 402 (default application/json). */ mimeType?: string; /** * Fiat price currency: `"EUR" | "GBP" | "USD"`. `priceCents` is then * integer cents of this currency. Default: the config `currency`, else * `"USD"`. Applies to the fiat rail only. */ currency?: FiatCurrency; } /** * Default `maxTimeoutSeconds` advertised in SDK 402s. Sellers behind the * hosted proxy advertise 300 (the proxy's catalog value); the SDK default * matches the scaffold templates. */ export declare const DEFAULT_MAX_TIMEOUT_SECONDS = 60; /** * Hand-rolled Bazaar discovery extension (G9): a stock paygate SDK merchant * was invisible to every catalog because its 402 carried no * `extensions.bazaar`. The shape mirrors `@x402/extensions/bazaar` * `declareDiscoveryExtension` output — info + a self-carried JSON schema the * facilitator validates the info against — without taking the dependency * (paygate ships with `ethers` as its only runtime dep). The method is * REQUIRED by the facilitator's probe/catalog path; never emit without it. */ export declare function buildBazaarDiscoveryExtension(method: string): Record; /** Absolute manifest pointer for a resource URL; relative fallback. */ export declare function manifestPointerFor(resourceUrl: string): string; export interface DiscoveryRateLimitConfig { maxProbes: number; windowMs: number; } export declare function resolveManifestDocument(config: PaygateConfig): Promise>; interface SupportedResponse { kinds?: Array<{ x402Version?: number; scheme?: string; network?: string; extra?: { feePayer?: unknown; }; }>; signers?: Record; } /** Exported for tests. Pick the fee payer for `network` out of a /supported body. */ export declare function feePayerFromSupported(body: SupportedResponse, network: string): string | undefined; /** * The facilitator's replay key for a Solana payment: SHA-256 of the * transaction message bytes (apps/facilitator/src/svm-settlement.ts * `svmPaymentIdentity`). Wire format: compact-u16 signature count, then * 64 bytes per signature, then the message. No Solana dependency needed. * `null` on anything that does not parse. Exported for tests. */ export declare function svmPaymentIdFromTransaction(transactionB64: string): string | null; interface TransferAuthorization { from: string; to: string; value: string; validAfter: string; validBefore: string; nonce: string; } interface X402PaymentPayload { x402Version: number; scheme: string; network: string; payload: { /** EVM: the EIP-3009 signature + authorization. */ signature?: string; authorization?: TransferAuthorization; /** SVM: the payer-signed transaction, base64. */ transaction?: string; }; } interface X402PaymentAccept { scheme: string; network: string; amount: string; resource: string; asset: string; payTo: string; maxTimeoutSeconds?: number; description?: string; mimeType?: string; extra?: Record; /** Backend-selected handshake terms (non-"exact" rails, e.g. arispay-metered). */ terms?: Record; } interface FacilitatorSettleResponse { success: boolean; transaction?: string; payer?: string; errorReason?: string; errorMessage?: string; /** Additive facilitator field on a `nonce_already_used` duplicate (re-delivery). */ priorSettlement?: PriorSettlementLike; /** Set by the SDK when `success` came from a prior settlement, not a fresh one. */ replayed?: boolean; } /** * The facilitator's report of a settlement it already made for this * nonce (`priorSettlement` on a duplicate `/settle`, or the body of * `GET /settlements/:nonce`). Loosely typed on purpose: every field is * re-checked in `matchPriorSettlement` before the SDK trusts it. */ export interface PriorSettlementLike { transaction?: unknown; txHash?: unknown; payer?: unknown; payTo?: unknown; asset?: unknown; amount?: unknown; network?: unknown; } /** * Accept a prior settlement as payment for THIS request only when it is * provably the same sale: same payer as the signed authorization, same * payTo, asset, amount, and network as this route's requirements, and a * non-empty transaction hash. Anything less is `null` and the duplicate * stays a 402. This is the guard against one paid nonce being replayed * against a different seller, route, or price. Exported for tests. */ export declare function matchPriorSettlement(prior: PriorSettlementLike | null | undefined, paymentPayload: X402PaymentPayload, accept: Pick): { transaction: string; payer: string; } | null; export interface PaywallResult { paid: boolean; receipt?: { paid: true; /** Which rail settled this request. */ rail: "x402" | "fiat"; /** x402: the settlement transaction hash. */ txHash?: string; /** fiat: the ArisPay payment id. */ paymentId?: string; trustTier: string; merchant: string; /** True when this delivery rides a settlement made on an earlier request (re-delivery or timeout recovery). */ replayed?: boolean; }; settlement?: FacilitatorSettleResponse; challenge?: { statusCode: 402; headers: Record; body: { error: string; x402Version: number; accepts: Array; facilitator: string; trustMinTier: string; gasSponsored: boolean; }; }; error?: { statusCode: number; body: { error: string; code: string; }; }; } export declare function handlePaywall(config: PaygateConfig, routePrice: RoutePrice, resourceUrl: string, paymentHeader: string | undefined, clientIp?: string, /** HTTP method of the gated route — carried into the Bazaar discovery extension. */ method?: string): Promise; export {}; //# sourceMappingURL=core.d.ts.map