import { C as CreatePreapprovalParams, P as Preapproval, a as CreatePaymentParams, b as Payment, S as SearchPaymentsParams, c as PaymentsSearchResult, d as CreateRefundParams, R as Refund, e as CreatePreferenceParams, f as Preference, g as CreateCustomerParams, h as Customer, i as CustomerCard, j as PaymentMethod, I as InstallmentOffer, A as AccountInfo, k as CreateCardTokenParams, l as CardToken, m as CreateQrPaymentParams, Q as QrOrder, n as CreateSubscriptionPlanParams, o as SubscriptionPlan, p as SubscriptionPayment, q as CreateStoreParams, r as Store, s as CreatePosParams, t as Pos, D as Dispute, u as IdentificationType, v as Issuer, W as WebhookConfig, w as CreateWebhookParams, x as CreateOrderParams, O as Order, y as AccountBalance, z as AccountMovement, B as Settlement, M as MerchantOrder, E as BankAccount, F as PointDevice, G as CreatePointPaymentIntentParams, H as PointPaymentIntent, J as ParsedWebhookEvent, K as OAuthToken, T as ThreeDSInfo } from './types-BaOjfcOt.js'; export { L as AutoRecurring, N as CurrencyId, U as FrequencyType, V as MarketplaceParams, X as OrderItem, Y as OrderStatus, Z as PaymentStatus, _ as PointPaymentIntentState, $ as PreapprovalStatus, a0 as PreferenceItem, a1 as SiteId, a2 as ThreeDSStatus, a3 as WebhookBody, a4 as WebhookTopic } from './types-BaOjfcOt.js'; import { I as IdempotencyCache, S as SubscriptionStateAdapter, A as AuditLogger, a as AuditOperation } from './audit-B9Nhj3PH.js'; export { b as AuditEntry, c as AuditLogAdapter, d as InMemoryAuditLog, e as InMemoryIdempotencyCache, f as InMemoryOAuthTokenStore, g as InMemoryStateAdapter, O as OAuthTokenRecord, h as OAuthTokenStore, i as SubscriptionStateRecord } from './audit-B9Nhj3PH.js'; import { ToolSet, Tool } from 'ai'; import { ArAgentsError } from '@ar-agents/core'; import 'zod'; /** * Circuit breaker — protects your app from cascading failures when MP * (or any upstream) is degraded. * * # Why * * When MP's API has an outage, naive retry-with-backoff still pounds the * dead service N times per request × every concurrent request. That makes * MP's outage worse AND your app's error rate worse (each request burns * `requestTimeoutMs × maxRetries` ms of CPU/event-loop time before failing). * * A circuit breaker observes failures over a rolling window. After enough * failures it OPENS — subsequent calls fail fast (no network round-trip) * with a `CircuitOpenError`. After a cooldown it HALF-OPENs — lets one * trial through. If that succeeds, it CLOSES (back to normal). If it * fails, it RE-OPENs for another cooldown. * * # State machine * * CLOSED ──(failures ≥ threshold)──▶ OPEN * ▲ │ * │ │ (cooldown elapsed) * │ ▼ * │ HALF_OPEN * │ │ * └──(trial succeeds)────────────────┤ * │ (trial fails) * ▼ * OPEN * * # When to use * * - **Protects YOUR app** from being slow/dead when MP is slow/dead. * - **Protects MP** from your app pummeling it during incidents. * - **Surfaces a clear signal to ops**: `circuit_open` event tells you * "MP is broken, my app is intentionally short-circuiting" — different * from "MP timed out 30s × 3 retries × 1000 concurrent users". * * # When NOT to use * * - For idempotent reads where stale-cached data is acceptable, prefer * a cache-aside pattern instead. * - For fire-and-forget webhooks where the backpressure should propagate * to MP itself (return 5xx, MP retries with backoff). * * # Configuration * * Defaults are tuned for typical MP traffic patterns: * - `failureThreshold: 5` — open after 5 consecutive failures * - `successThreshold: 2` — close after 2 trial successes (half-open) * - `resetTimeoutMs: 30_000` — 30s cooldown before half-open trial * - `monitoringWindowMs: 60_000` — count failures within a 60s window * * # Per-host vs global * * The default `MercadoPagoClient` uses ONE breaker per client instance * (which means one per upstream host: `api.mercadopago.com` for prod, * `api.mercadopago.com` sandbox for TEST). For multi-host setups (e.g., * marketplace flows with per-seller clients), instantiate a SHARED breaker * and pass it to all clients — they all benefit from the same backpressure * signal. */ type CircuitState = "CLOSED" | "OPEN" | "HALF_OPEN"; interface CircuitBreakerOptions { /** Open the breaker after this many consecutive failures. Default 5. */ failureThreshold?: number; /** Close the breaker after this many successive successes in HALF_OPEN. Default 2. */ successThreshold?: number; /** Time to stay OPEN before allowing a HALF_OPEN trial. Default 30s. */ resetTimeoutMs?: number; /** Rolling window for counting failures. Failures older than this don't count. Default 60s. */ monitoringWindowMs?: number; /** * Called on EVERY state transition. Useful for emitting metrics/logs. * `cause` is the error that triggered the transition (when applicable). */ onStateChange?: (event: { from: CircuitState; to: CircuitState; cause?: unknown; consecutiveFailures: number; }) => void; /** * Predicate to decide whether an error should count as a circuit failure. * By default, all errors count. Override to ignore expected business * errors (e.g., 404s, validation errors) — they shouldn't open the breaker. */ isFailure?: (error: unknown) => boolean; /** Time provider (for tests). Defaults to `Date.now`. */ now?: () => number; } /** * Thrown when a circuit breaker is OPEN and rejects a call without trying. * Catch this separately from MercadoPagoError to differentiate "MP said no" * from "we didn't even ask MP". */ declare class CircuitOpenError extends Error { readonly retryAfterMs: number; readonly consecutiveFailures: number; constructor(retryAfterMs: number, consecutiveFailures: number); } /** * Thread-safe circuit breaker. Single-instance per upstream (typically per * `MercadoPagoClient`). Pass to multiple clients to share state. * * @example * ```ts * import { CircuitBreaker, MercadoPagoClient } from "@ar-agents/mercadopago"; * * const breaker = new CircuitBreaker({ * failureThreshold: 5, * resetTimeoutMs: 30_000, * onStateChange: (e) => metrics.increment(`circuit.${e.to}`), * }); * * const client = new MercadoPagoClient({ * accessToken: process.env.MP_ACCESS_TOKEN!, * circuitBreaker: breaker, * }); * ``` */ declare class CircuitBreaker { private state; private consecutiveFailures; private halfOpenSuccesses; private openedAt; /** Timestamps of failures within the monitoring window. */ private failureWindow; private readonly failureThreshold; private readonly successThreshold; private readonly resetTimeoutMs; private readonly monitoringWindowMs; private readonly onStateChange; private readonly isFailureFn; private readonly now; constructor(opts?: CircuitBreakerOptions); /** Read the current state. Useful for health checks + metrics. */ getState(): CircuitState; /** Read diagnostic state for health checks + dashboards. */ getStats(): { state: CircuitState; consecutiveFailures: number; failuresInWindow: number; msSinceOpened: number | null; msUntilHalfOpen: number | null; }; /** * Execute `fn` under the breaker's protection. * - If the breaker is OPEN, throws `CircuitOpenError` immediately. * - If `fn` succeeds, may transition HALF_OPEN → CLOSED. * - If `fn` fails (and the error counts as a failure), records the * failure; may transition CLOSED → OPEN or HALF_OPEN → OPEN. */ execute(fn: () => Promise): Promise; /** Manually force the breaker open. Useful for runbook / manual ops. */ trip(reason?: unknown): void; /** Manually reset the breaker to CLOSED. */ reset(): void; private recordSuccess; private recordFailure; private transitionTo; private pruneWindow; private failuresInCurrentWindow; } /** * Base class for any error originating from the Mercado Pago integration. All * specific error types extend this. Carries the MP HTTP status, the parsed * body when available, and the endpoint that failed for debugging. * * Extends `ArAgentsError` from `@ar-agents/core` so the family contract * (code / retryable / context) is uniform across every `@ar-agents/*` * integration. Existing public properties (`status`, `endpoint`, * `mpResponse`) are preserved on the instance AND mirrored into * `context` for callers using the new contract. * * `code` is derived from the MP error type (e.g. `"mp_auth_failed"`, * `"mp_rate_limited"`, `"mp_overloaded"`); the generic surface uses * `"mp_api_error"`. `retryable` defaults to true for 5xx, 429, and * timeouts; false otherwise. */ declare class MercadoPagoError extends ArAgentsError { readonly status: number; readonly endpoint: string; readonly mpResponse?: unknown; constructor(message: string, status: number, endpoint: string, mpResponse?: unknown, init?: { code?: string; retryable?: boolean; }); } /** * Thrown when the access token is missing, expired, or rejected by MP. */ declare class MercadoPagoAuthError extends MercadoPagoError { constructor(endpoint: string, body?: unknown); } /** * Thrown when MP returns the "back_url is not a valid URL" rejection. Common * when devs pass localhost or http:// — MP requires HTTPS only, even in sandbox. */ declare class MercadoPagoBackUrlInvalidError extends MercadoPagoError { constructor(endpoint: string, body?: unknown); } /** * Thrown when the buyer email matches the seller account's email. MP refuses * self-payment on subscriptions: the Confirmar button at the init_point UI * stays disabled with no surfaceable error message. */ declare class MercadoPagoSelfPaymentError extends MercadoPagoError { constructor(endpoint: string, body?: unknown); } /** * Thrown when MP returns "Cannot operate between different countries". Despite * the error text, this generally signals an account-type mismatch (real * account-in-test-mode vs. test user account), not a literal country mismatch. */ declare class MercadoPagoAccountTypeMismatchError extends MercadoPagoError { constructor(endpoint: string, body?: unknown); } /** * Thrown when MP's risk engine rejects the first payment of a subscription. * IMPORTANT: when this happens, MP automatically cancels the entire preapproval * — you cannot retry on the same subscription, you must create a fresh one. */ declare class MercadoPagoPaymentRejectedError extends MercadoPagoError { preapprovalId: string; statusDetail: string | null; constructor(preapprovalId: string, statusDetail: string | null, body?: unknown); } /** * Thrown when an attempt is made to authorize a preapproval via API. Only the * payer can authorize via the init_point UI; there is no admin override even * in sandbox. */ declare class MercadoPagoAuthorizeForbiddenError extends MercadoPagoError { constructor(preapprovalId: string, body?: unknown); } /** * Thrown when MP rate-limits the integration. Retry with exponential backoff. */ declare class MercadoPagoRateLimitError extends MercadoPagoError { retryAfterSeconds: number | null; constructor(endpoint: string, retryAfterSeconds: number | null, body?: unknown); } /** * Thrown when MP is overloaded and serves an HTML 503 page instead of a JSON * error. The library detects content-type !== application/json on 5xx and * raises this typed error so retry logic + agent UX can branch correctly. */ declare class MercadoPagoOverloadedError extends MercadoPagoError { constructor(endpoint: string, status: number); } /** * Thrown when a request exceeds the configured `requestTimeoutMs`. Retried * automatically up to `maxRetries`; this surfaces only when the budget runs * out. */ declare class MercadoPagoTimeoutError extends MercadoPagoError { readonly timeoutMs: number; constructor(endpoint: string, timeoutMs: number); } /** * Maps an MP error response body to the most specific known error class. Falls * back to the generic MercadoPagoError when no specific pattern matches. */ declare function classifyError(status: number, endpoint: string, body: unknown, context?: { preapprovalId?: string; payerEmail?: string; sellerEmail?: string; }): MercadoPagoError; interface MercadoPagoClientOptions { /** Access token. TEST- prefix for sandbox, APP_USR- for production. */ accessToken: string; /** * Escape hatch for browser-context tests (e.g., jsdom). MUST NOT be set * in production code — the constructor's browser-context check exists * specifically to prevent the access token from being bundled into a * client-side JavaScript bundle. The `__` prefix and explicit boolean * are deliberate friction so this never gets typed by accident. */ __allowBrowser?: boolean; /** * Override the API base URL. Mostly useful for tests against MSW or for * pointing at a regional MP host. Defaults to https://api.mercadopago.com. */ baseUrl?: string; /** * Custom fetch implementation. Defaults to globalThis.fetch. Override to * inject your own retry/instrumentation layer or to test with MSW. */ fetch?: typeof fetch; /** * Per-request timeout in ms. Aborts the request and throws if exceeded. * Default 30_000 (30s). MP can be slow under load; 30s is a safe upper bound. */ requestTimeoutMs?: number; /** * Number of retries on 5xx + network errors. Default 1 (single retry). * 4xx errors are NEVER retried (they're user/config errors). Each retry * uses exponential backoff: 250ms, 500ms, 1000ms, ... */ maxRetries?: number; /** * Observability hook fired AFTER every request (success or failure). * Useful for logging, metrics, tracing. Synchronous, fire-and-forget. * * The `traceContext` field follows the W3C Trace Context spec — pass * an OpenTelemetry-compatible context propagator and you get full * distributed tracing for free. See `traceContext` option below. */ onCall?: (event: { method: string; path: string; durationMs: number; httpStatus: number | null; retried: number; success: boolean; /** v0.9: MP's `x-request-id` echo. Useful for support tickets. */ requestId?: string | null; /** v0.9: MP's rate-limit headers when present. */ rateLimit?: { remaining: number | null; resetSeconds: number | null; }; /** v0.9: Circuit breaker state at the time of the call. */ circuitState?: "CLOSED" | "OPEN" | "HALF_OPEN"; /** v0.9: Trace context for OpenTelemetry-style propagation. */ traceContext?: { traceId?: string; spanId?: string; }; }) => void; /** * v0.9 — Opt-in circuit breaker. When MP is failing, fail fast instead of * piling up retries against a dead service. Pass a configured instance * (or share one across multiple clients to give them shared backpressure * signal). * * @example * ```ts * const breaker = new CircuitBreaker({ * failureThreshold: 5, * resetTimeoutMs: 30_000, * onStateChange: (e) => metrics.gauge("circuit.state", e.to), * }); * const client = new MercadoPagoClient({ accessToken: "...", circuitBreaker: breaker }); * ``` */ circuitBreaker?: CircuitBreaker; /** * v0.9 — Optional W3C Trace Context propagator. If provided, the client * extracts traceId/spanId on each request, injects `traceparent` / * `tracestate` headers (MP echoes them back via x-request-id), and surfaces * them in `onCall` events. Compatible with OpenTelemetry without adding * `@opentelemetry/api` as a peer dep. * * If you have OTEL set up, just pass `() => trace.getActiveSpan()?.spanContext()`. */ traceContext?: () => { traceId?: string; spanId?: string; traceFlags?: number; } | undefined; } interface RequestOptions { /** Idempotency key. Required for POST/PUT to dedupe retries safely. */ idempotencyKey?: string; /** Query string params. Object → URLSearchParams. */ query?: Record; /** Context for error classification. */ classifyContext?: { preapprovalId?: string; paymentId?: string; customerId?: string; payerEmail?: string; sellerEmail?: string; }; /** * v0.9 — Parent AbortSignal for deadline propagation. When the agent * has a fixed budget (e.g., 5s for the whole tool call), pass it here. * The client merges it with its own per-request timeout — whichever * fires first wins. */ signal?: AbortSignal; } /** * Thin, typed wrapper around Mercado Pago's REST API. Exposes the surface * the agent layer needs: Subscriptions (Preapprovals), Payments, Checkout Pro * (Preferences), Customers + saved Cards, Refunds, Payment Methods + * Installments, and Account info. Deliberately narrower than a full SDK * rebuild — we add endpoints when the agent layer needs them. */ declare class MercadoPagoClient { private readonly accessToken; private readonly baseUrl; private readonly fetchImpl; private readonly requestTimeoutMs; private readonly maxRetries; private readonly onCall; private readonly circuitBreaker; private readonly traceContext; constructor(options: MercadoPagoClientOptions); /** * v0.9 — Inspect the circuit breaker state (when configured). Returns * `null` when no circuit breaker is wired. Useful for health checks. */ getCircuitState(): ReturnType | null; private request; private requestUnprotected; /** * Create a recurring subscription (preapproval). The returned `init_point` * URL is where the buyer must complete the FIRST payment with their card + * CVV — there is no API path that bypasses this human step. */ createPreapproval(params: CreatePreapprovalParams): Promise; getPreapproval(id: string): Promise; cancelPreapproval(id: string): Promise; pausePreapproval(id: string): Promise; resumePreapproval(id: string): Promise; /** * Create a payment. Two main flows: * - **Card payment**: pass `token` (from MP frontend Cardform) + payment_method_id. * - **Account money / cash**: omit token, pass payment_method_id like "account_money", "rapipago", "pagofacil". * * For credit card payments where you don't have a card token (i.e., you only * have a payer email and want to send them a payment link), use * `createPreference` (Checkout Pro) instead. * * Idempotency: pass `idempotencyKey` to safely retry. Required for production * to dedupe network-failed requests. */ createPayment(params: CreatePaymentParams): Promise; /** Fetch a payment by ID. */ getPayment(id: string): Promise; /** * Search payments with filters. Common: by external_reference (your-system * id), by status, by date range. Pagination via offset + limit (max 100). */ searchPayments(params?: SearchPaymentsParams): Promise; /** * Capture a previously authorized payment. Only works for credit-card * payments created with `capture: false`. Optional partial capture amount. */ capturePayment(id: string, amount?: number): Promise; /** * Cancel a pending or in_process payment. Once approved, you must use * `createRefund` instead. */ cancelPayment(id: string): Promise; /** * Refund a payment fully (omit `amount`) or partially. Idempotency key * recommended — refunds can fail mid-flight and you don't want double-refunds * on retry. */ createRefund(params: CreateRefundParams): Promise; listRefunds(paymentId: string): Promise; getRefund(paymentId: string, refundId: string): Promise; /** * Create a payment preference for Checkout Pro. Returns `init_point` URL * where the buyer completes payment on MP-hosted form. This is the * recommended flow when you don't have a card token (most common path for * agents — you don't want to handle PCI data). * * Sandbox: use `sandbox_init_point` instead of `init_point`. */ createPreference(params: CreatePreferenceParams): Promise; getPreference(id: string): Promise; updatePreference(id: string, patch: Partial): Promise; createCustomer(params: CreateCustomerParams): Promise; getCustomer(id: string): Promise; /** * Search customers. Most common: by email (returns 0 or 1 result). * Note: MP's `/v1/customers/search` returns a paginated wrapper, not a flat array. */ searchCustomers(params?: { email?: string; limit?: number; offset?: number; }): Promise<{ paging: { total: number; limit: number; offset: number; }; results: Customer[]; }>; listCustomerCards(customerId: string): Promise; getCustomerCard(customerId: string, cardId: string): Promise; deleteCustomerCard(customerId: string, cardId: string): Promise; /** List all payment methods enabled for the account's site (MLA = Argentina). */ listPaymentMethods(): Promise; /** * Get installment options for an amount. THE killer AR feature — returns * `payer_costs` with `recommended_message` strings like "12 cuotas sin * interés de $X" that you should surface verbatim to the user. * * Pass `bin` (first 6 digits of card) for issuer-specific offers (e.g., * Naranja's interest-free promotions). Without bin, returns generic offers. */ getInstallments(params: { amount: number; paymentMethodId?: string; bin?: string; issuerId?: string; }): Promise; /** Get info about the account that owns this access token. */ getMe(): Promise; /** * Create a single-use card token from a saved card. This is the server-side * retokenization path (PCI-safe because the card data lives in MP's vault, * we only pass the saved card_id + customer_id + the user-supplied CVV). * * Tokens expire in 7 days but typically burn on first use. AR currently * REQUIRES CVV on every charge (MP doesn't store it); skipping CVV requires * a private MP product enablement, not a public API. */ createCardToken(params: CreateCardTokenParams): Promise; /** * High-level helper: charge a saved card in 3 steps. * 1. Mint a card token from {customer_id, card_id, security_code} * 2. Lookup card to fill payment_method_id (avoids agent guessing) * 3. Create the payment with the token + idempotency key * * Returns the resulting Payment. Uses deterministic idempotency from * (card_id, amount, externalReference) so retries dedupe on MP's side. */ chargeSavedCard(params: { customerId: string; cardId: string; securityCode: string; amount: number; description: string; installments?: number; externalReference?: string; statementDescriptor?: string; idempotencyKey?: string; }): Promise; /** * Create a dynamic in-store QR order. Returns `qr_data` (EMVCo TLV string) * + `in_store_order_id`. The buyer scans the QR with any AR wallet (Modo, * BNA+, Cuenta DNI, Naranja X, etc. — interop is mandated by Transferencias * 3.0). On payment, MP fires `point_integration_wh` then `payment` topics. * * Requires a pre-configured POS (`external_pos_id` from MP dashboard or * `POST /pos`). The seller's `user_id` is auto-fetched from `/users/me`. * * The lib does NOT render the QR image — pass `qr_data` to a QR renderer * (e.g., `qrcode` package) to get a data URL. The agent tool layer wraps * this and returns both raw + data URL. */ createQrPayment(userId: string, params: CreateQrPaymentParams): Promise; /** * Cancel a pending QR order on a POS. Necessary if the buyer never scans * — otherwise the next `createQrPayment` on the same POS returns 409. */ cancelQrPayment(userId: string, externalPosId: string): Promise; /** * Create a reusable subscription plan. Customers later subscribe to it via * `subscribeToPlan` (which creates a preapproval pointing at the plan). * * Use this when you have fixed tiers (Básico/Pro/Enterprise). For custom * per-customer amounts, skip plans and use `createPreapproval` directly. */ createSubscriptionPlan(params: CreateSubscriptionPlanParams): Promise; getSubscriptionPlan(id: string): Promise; listSubscriptionPlans(params?: { limit?: number; offset?: number; status?: string; }): Promise<{ paging: { total: number; limit: number; offset: number; }; results: SubscriptionPlan[]; }>; updateSubscriptionPlan(id: string, patch: { reason?: string; status?: "active" | "cancelled"; amount?: number; backUrl?: string; }): Promise; /** * Subscribe a customer to an existing plan. Returns a Preapproval with * `init_point` URL where the buyer completes the first payment. */ subscribeToPlan(params: { planId: string; payerEmail: string; cardTokenId?: string; externalReference?: string; }): Promise; /** * List the auto-charge attempts (authorized_payments) under a preapproval. * Useful for "show me the cobros of the last 6 months for this client". */ listSubscriptionPayments(preapprovalId: string, params?: { limit?: number; offset?: number; }): Promise<{ paging: { total: number; limit: number; offset: number; }; results: SubscriptionPayment[]; }>; /** Create a store for the seller. POSes (for QR) live under stores. */ createStore(userId: string, params: CreateStoreParams): Promise; listStores(userId: string, params?: { limit?: number; offset?: number; }): Promise<{ paging: { total: number; limit: number; offset: number; }; results: Store[]; }>; /** Create a POS under a store. The POS's `external_id` is what `createQrPayment` uses. */ createPos(params: CreatePosParams): Promise; listPos(params?: { storeId?: string | number; limit?: number; offset?: number; }): Promise<{ paging: { total: number; limit: number; offset: number; }; results: Pos[]; }>; listPaymentDisputes(paymentId: string): Promise; getDispute(paymentId: string, disputeId: string): Promise; /** List valid identification types for the seller's site. AR returns DNI/CI/LE/LC/Otro/Pasaporte/CUIT/CUIL. */ listIdentificationTypes(): Promise; /** List card issuers for a payment method. Useful with `bin` for installments. */ listIssuers(params: { paymentMethodId: string; bin?: string; }): Promise; /** List configured webhook subscriptions. */ listWebhooks(): Promise; /** Create a webhook subscription for a topic. */ createWebhook(params: CreateWebhookParams): Promise; updateWebhook(id: string, patch: { url?: string; topic?: string; }): Promise; deleteWebhook(id: string): Promise; /** * Create a new Order. Use `capture_mode: "manual"` for auth-only flows * where you want to capture funds later (ride-share, hotels, marketplaces). * * For marketplace splits, set `marketplace`, `marketplace_fee`, * `collector_id` — see `MarketplaceParams`. */ createOrder(params: CreateOrderParams, options?: RequestOptions): Promise; getOrder(id: string): Promise; updateOrder(id: string, patch: Partial): Promise; /** * Capture a previously-authorized Order (only for orders created with * `capture_mode: "manual"`). Captures up to the originally-authorized * amount; pass `amount` for partial capture. */ captureOrder(id: string, amount?: number): Promise; /** * Cancel an Order. Releases any auth-holds; marks the Order as canceled. * For orders that have already been captured, use `createRefund` instead. */ cancelOrder(id: string): Promise; /** * Get the seller's current MP wallet balance (available + unavailable). * - `available_balance`: spendable / withdrawable right now. * - `unavailable_balance`: in retention (e.g., 14-21 days for new sellers). * - `total_amount` = sum of both. */ getAccountBalance(): Promise; /** * List wallet movements (incoming payments, transfers, refunds, holdings). * Defaults to most-recent-first, paginated. Filter by date range with * `from`/`to` (ISO 8601). */ listAccountMovements(params?: { from?: string; to?: string; limit?: number; offset?: number; }): Promise<{ movements: AccountMovement[]; paging: { limit: number; offset: number; total: number; }; }>; /** * List settlements (transfers from MP wallet to your bank account). * Useful for monthly conciliation reports. */ listSettlements(params?: { from?: string; to?: string; status?: string; limit?: number; offset?: number; }): Promise<{ settlements: Settlement[]; paging: { limit: number; offset: number; total: number; }; }>; /** * Get a single settlement by id. Returns the full Settlement object * including bank_account info (CBU, bank name). */ getSettlement(id: string): Promise; /** * Update a customer's profile (name, last name, address, etc.). MP merges * the patch — fields you don't send remain unchanged. */ updateCustomer(id: string, patch: Partial<{ first_name: string; last_name: string; phone: { area_code?: string; number?: string; }; identification: { type: string; number: string; }; address: { street_name?: string; street_number?: number; zip_code?: string; }; description: string; default_card?: string; }>): Promise; /** * Add a saved card to a customer using a card token (one-time, get from * MP's frontend Cardform). The card is then chargeable with charge_saved_card. */ createCustomerCard(customerId: string, cardToken: string): Promise; /** * Update an existing subscription. Common patches: * - `transaction_amount` to change the recurring amount * - `card_token_id` to switch payment method (e.g., expired card) * - `status: "cancelled" | "paused"` (alternative to dedicated cancel/pause endpoints) * - `reason` to update the description shown to the buyer */ updatePreapproval(id: string, patch: Partial<{ transaction_amount: number; card_token_id: string; status: "authorized" | "paused" | "cancelled"; reason: string; external_reference: string; }>): Promise; /** * Search subscriptions across the seller's account. Common filters: * `status` (pending/authorized/paused/cancelled), `payer_email`, * `external_reference`. Paginated. */ searchPreapprovals(params?: { status?: string; payerEmail?: string; externalReference?: string; preapproval_plan_id?: string; limit?: number; offset?: number; }): Promise<{ results: Preapproval[]; paging: { limit: number; offset: number; total: number; }; }>; /** * Get a merchant_order with all its associated payments + shipments. * Useful for reconciling "which payments belong to which preference" * — typical webhook handler use case. */ getMerchantOrder(id: string): Promise; /** * Search merchant_orders by external_reference, preference_id, or status. */ searchMerchantOrders(params?: { preferenceId?: string; externalReference?: string; status?: string; limit?: number; offset?: number; }): Promise<{ elements: MerchantOrder[]; paging: { limit: number; offset: number; total: number; }; }>; /** * Update a merchant_order — typically to add items or update shipping. */ updateMerchantOrder(id: string, patch: Record): Promise; getStore(userId: string, storeId: string): Promise; updateStore(userId: string, storeId: string, patch: Partial): Promise; deleteStore(userId: string, storeId: string): Promise; getPos(posId: string): Promise; updatePos(posId: string, patch: Partial): Promise; deletePos(posId: string): Promise; /** * List bank accounts registered by the seller. The default is the one * that receives `release_money` settlements. */ listBankAccounts(): Promise; /** * Register a new bank account (CBU) for the seller. Note: MP usually * requires this through the dashboard for compliance — this endpoint may * not work for all sellers. */ registerBankAccount(params: { cbu: string; alias?: string; }): Promise; /** * List the Point devices linked to the seller's MP account. Each device * has an id (the device serial), an operating_mode (PDV vs STANDALONE), * and an optional pos_id (when bound to a logical POS). */ listPointDevices(params?: { posId?: string | number; limit?: number; offset?: number; }): Promise<{ devices: PointDevice[]; paging: { total: number; limit: number; offset: number; }; }>; /** * Switch a Point device's operating mode: * - "PDV": device is bound to a logical Pos and only takes payments * triggered through that Pos (typical for cash-register integrations). * - "STANDALONE": device works independently, accepts any payment. */ updatePointDeviceOperatingMode(deviceId: string, operatingMode: "PDV" | "STANDALONE"): Promise; /** * Create a payment intent on a Point device — the device prompts the buyer * to tap/insert/swipe. Returns immediately with intent id; query state via * `getPointPaymentIntent()` or wait for `point_integration_wh` webhook. * * NOTE: amount is in CENTAVOS (Point API differs from Payments API which * uses pesos). 100 = $1 ARS, 1000 = $10, 10000 = $100, etc. */ createPointPaymentIntent(deviceId: string, params: CreatePointPaymentIntentParams): Promise; /** Get the current state of a Point payment intent. */ getPointPaymentIntent(intentId: string): Promise; /** * Cancel an OPEN payment intent before the buyer interacts with the device. * Only works while state is "OPEN" — once the buyer taps, you can't cancel. */ cancelPointPaymentIntent(deviceId: string, intentId: string): Promise<{ id: string; canceled: true; }>; /** * Liveness probe against MP. Returns latency + circuit-breaker state. * Use as a /health endpoint for k8s, Vercel cron, or status-page checks. * * Returns `{ ok: false, ... }` instead of throwing — designed for * monitoring loops that want to keep running. * * @param signal Optional AbortSignal to cap wait time (e.g., 2s for * status-page polling). */ healthCheck(signal?: AbortSignal): Promise<{ ok: boolean; latencyMs: number; /** MP user_id when reachable. */ userId: string | null; /** Last error message when not OK. */ error: string | null; /** Circuit breaker state when configured. */ circuit: ReturnType | null; }>; } /** * Webhook idempotency / dedup — short-circuits duplicate webhook deliveries * from MP to prevent double-processing. * * # The problem * * MP retries webhook deliveries on 5xx responses. The retry policy is * exponential backoff: 5min, 15min, 30min, 1h, 6h, 24h, 48h, 96h, 192h * (~12 attempts over 8 days). If your handler temporarily 5xx'd (DB * down, deploy in progress, etc.) and then recovered, you'll receive * the SAME webhook 5+ times. Without dedup: * * - You double-charge (if the webhook triggers a charge) * - You double-send notifications (5 emails to the buyer instead of 1) * - You double-create downstream resources * * # The fix * * Cache the unique "delivery key" of every webhook you've successfully * processed. On retry, recognize the key, return 200 immediately, skip * processing. * * # The "delivery key" * * MP doesn't ship a single canonical id per delivery, but the tuple * `${topic}:${dataId}:${requestId}` is stable for retries (same delivery * attempt → same x-request-id) and unique enough to dedupe. * * # Storage * * Reuse `IdempotencyCache` from `state.ts`. Default TTL: 7 days (matches * MP's webhook retry window). Override per-deployment. */ interface WebhookDedupOptions { /** * Storage for processed webhook ids. Plug in `VercelKVIdempotencyCache` * for production or `InMemoryIdempotencyCache` for tests. */ cache: IdempotencyCache; /** * Time-to-live for dedup entries (seconds). Default 7 days — covers MP's * full retry window (~8 days) with a safety margin. */ ttlSeconds?: number; /** * Optional callback fired when a duplicate is detected. Useful for * metrics ("webhooks deduped" counter). */ onDuplicate?: (deliveryKey: string) => void; } interface DedupResult { /** * `true` if this is the first time we've seen this delivery — caller * should process it. * `false` if it's a retry of a previously-seen delivery — caller should * acknowledge with 200 and skip processing. */ shouldProcess: boolean; /** The deduplication key derived from the webhook. */ deliveryKey: string; } /** * Dedup helper. Use this BEFORE processing a webhook to short-circuit retries. * * @example * ```ts * import { WebhookDedup, VercelKVIdempotencyCache } from "@ar-agents/mercadopago"; * * const dedup = new WebhookDedup({ * cache: new VercelKVIdempotencyCache(), * onDuplicate: (key) => metrics.increment("mp.webhook.duplicate"), * }); * * export async function POST(req: Request) { * const event = parseWebhookEvent(...); * if (!event) return new Response("bad request", { status: 400 }); * * const requestId = req.headers.get("x-request-id"); * const { shouldProcess } = await dedup.check({ * topic: event.topic, * dataId: event.dataId, * requestId, * }); * if (!shouldProcess) return new Response("ok (duplicate)", { status: 200 }); * * // ... process the webhook ... * * return new Response("ok", { status: 200 }); * } * ``` */ declare class WebhookDedup { private readonly cache; private readonly ttlSeconds; private readonly onDuplicate; constructor(opts: WebhookDedupOptions); /** * Check whether a webhook delivery has been seen before. If new, mark it * as seen (so subsequent retries return shouldProcess=false). If seen, * return shouldProcess=false WITHOUT marking again. * * **Important**: this method is not atomic across concurrent calls — two * simultaneous deliveries with the same key may both pass shouldProcess=true. * For strict at-most-once processing, follow with a transaction or use a * cache that supports `setNX`-style semantics (Redis, Cloudflare KV with * conditional writes). * * For most webhook handlers this race is acceptable: even if two get * through, the downstream business logic (e.g., "charge if not already * charged") will be idempotent on its own. */ check(args: { topic: string; dataId: string; requestId: string | null; }): Promise; /** * Manually mark a delivery as processed. Call this AFTER your business * logic succeeds — useful when you want to control when the dedup * marker is written (e.g., only on success). * * Combined with calling `check()` BEFORE the work, this gives "at-least-once" * semantics: failed processing → no marker → retry will be processed again. */ markProcessed(args: { topic: string; dataId: string; requestId: string | null; }): Promise; /** * Variant of `check` that doesn't mark on first sight — caller must * explicitly `markProcessed` when their business logic succeeds. * Use this for at-least-once semantics (each delivery processed at * least once, possibly more if processing fails before mark). */ peekIsDuplicate(args: { topic: string; dataId: string; requestId: string | null; }): Promise; private deriveKey; } interface MercadoPagoToolsOptions { /** State adapter for persisting subscription records. */ state: SubscriptionStateAdapter; /** * Default back_url used when callers don't supply one. MUST be HTTPS, MP * rejects http:// and localhost back URLs even in sandbox. */ backUrl: string; /** * Optionally override the agent-facing tool descriptions. Pass an object * with keys matching tool names; values replace the default description. * Useful for localizing the agent's tool reasoning. */ descriptions?: Partial>; /** * Default notification webhook URL used when callers don't supply one. * Optional, MP falls back to dashboard config if not set. */ notificationUrl?: string; /** * Webhook secret for the `handle_webhook` tool. Required to verify * incoming webhook HMAC-SHA256 signatures. Get it from MP dev panel → * "Notificaciones" → "Webhooks" → "Configurar notificaciones". * If omitted, `handle_webhook` returns `{ verified: false, error: ... }` * and the agent should reject the webhook. */ webhookSecret?: string; /** * OAuth credentials for the marketplace flow. Required for * `oauth_exchange_code` and `oauth_refresh_token` (the secret cannot be * passed by the agent, it's a server-side secret). If omitted, those * tools return `{ available: false }` with setup instructions. */ oauth?: { clientId: string; clientSecret: string; }; /** * v0.10, Audit logger. When passed, every state-mutating tool call * automatically emits an audit entry with operation/actor/inputHash/ * resourceId/outcome/duration. Read-only tools (get/search/list) skip * audit logging. */ audit?: AuditLogger; /** * v0.10, Logical actor for audit entries (e.g., "agent:billing-bot", * "user:42"). Defaults to the AuditLogger's defaultActor. */ auditActor?: string; /** * v0.10, Webhook deduplication for handle_webhook tool. Caches * processed (topic, dataId, requestId) tuples to short-circuit MP's * retries (which fire on 5xx and can deliver the same event 5+ times). */ webhookDedup?: WebhookDedup; /** * v0.15, Programmatic Human-In-The-Loop gate for irreversible / * money-moving operations. When set, every call to one of the gated * tools (cancel_payment, capture_payment, refund_payment, * delete_customer_card, cancel_qr_payment, cancel_order, * cancel_point_payment_intent, delete_webhook) invokes this callback * BEFORE executing. Return `true` to proceed, `false` to reject the * call (the tool returns `{ ok: false, reason: "Confirmation declined" }`). * * The description-based HITL warnings still apply (they nudge the LLM * to confirm in-conversation), but those depend on the LLM's heuristic * and can be bypassed via prompt injection. This callback is the actual * out-of-band enforcement: wire it to your UI / Slack / email / SMS * confirmation flow so a human approves money-movement explicitly. * * @example * ```ts * mercadoPagoTools(client, { * state, backUrl, * requireConfirmation: async (op, args) => { * // Send a Slack DM to the operator with the operation summary * // and wait for their button click. Throw or return false to reject. * return await slack.confirm({ * channel: "#mp-approvals", * text: `Refund $${args.amount ?? "FULL"} on payment ${args.payment_id}?`, * timeoutMs: 60_000, * }); * }, * }); * ``` * * If omitted (default), the description-based HITL is the only line of * defense, fine for trusted/internal agents, NOT recommended for * untrusted-input agents (anything reading from a public webhook). */ requireConfirmation?: (operation: GatedOperation, args: Record) => Promise; } /** * Tool names that go through `requireConfirmation` when configured. * Adding a new irreversible operation? Add it here AND in the * `applyConfirmationGate` wrapper at the bottom of this file. */ type GatedOperation = "cancel_payment" | "capture_payment" | "refund_payment" | "delete_customer_card" | "cancel_qr_payment" | "cancel_order" | "cancel_point_payment_intent" | "delete_webhook"; type ToolName = "create_subscription" | "get_subscription_status" | "cancel_subscription" | "pause_subscription" | "resume_subscription" | "create_payment" | "get_payment" | "search_payments" | "cancel_payment" | "capture_payment" | "refund_payment" | "list_refunds" | "create_payment_preference" | "get_payment_preference" | "create_customer" | "find_customer_by_email" | "list_customer_cards" | "delete_customer_card" | "list_payment_methods" | "calculate_installments" | "get_account_info" | "charge_saved_card" | "create_qr_payment" | "cancel_qr_payment" | "create_subscription_plan" | "list_subscription_plans" | "update_subscription_plan" | "subscribe_to_plan" | "list_subscription_payments" | "create_store" | "list_stores" | "create_pos" | "list_pos" | "list_payment_disputes" | "get_dispute" | "list_identification_types" | "list_issuers" | "list_webhooks" | "create_webhook" | "update_webhook" | "delete_webhook" | "handle_webhook" | "oauth_authorize_url" | "oauth_exchange_code" | "oauth_refresh_token" | "create_order" | "get_order" | "update_order" | "capture_order" | "cancel_order" | "get_account_balance" | "list_account_movements" | "list_settlements" | "get_settlement" | "analyze_payment_3ds" | "get_test_cards" | "get_customer" | "update_customer" | "create_customer_card" | "get_customer_card" | "get_subscription_plan" | "update_subscription" | "search_subscriptions" | "get_refund" | "update_payment_preference" | "get_merchant_order" | "search_merchant_orders" | "update_merchant_order" | "get_store" | "update_store" | "delete_store" | "get_pos" | "update_pos" | "delete_pos" | "list_bank_accounts" | "register_bank_account" | "list_point_devices" | "update_point_device_mode" | "create_point_payment_intent" | "get_point_payment_intent" | "cancel_point_payment_intent" | "compute_marketplace_fee" | "explain_payment_status" | "mp_health_check" | "find_applicable_promos" | "confirm_3ds_challenge" | "search_payments_all" | "list_settlements_all" | "validate_tax_id"; /** * Build a tool set for the Vercel AI SDK that exposes Mercado Pago to an * agent. Pass directly to `Experimental_Agent`'s `tools` option, or merge with * other tool sets. * * @example * ```ts * import { Experimental_Agent as Agent, stepCountIs } from 'ai'; * import { MercadoPagoClient, mercadoPagoTools, InMemoryStateAdapter } from '@ar-agents/mercadopago'; * * const mp = new MercadoPagoClient({ accessToken: process.env.MP_ACCESS_TOKEN! }); * const agent = new Agent({ * model: 'anthropic/claude-sonnet-4-6', * tools: mercadoPagoTools(mp, { * state: new InMemoryStateAdapter(), * backUrl: 'https://mysite.com/done', * }), * stopWhen: stepCountIs(8), * }); * ``` */ declare function mercadoPagoTools(client: MercadoPagoClient, options: MercadoPagoToolsOptions): ToolSet; /** * Webhook helpers — parse incoming MP notifications and verify the * HMAC-SHA256 signature MP sends in the `x-signature` header. * * # Edge Runtime * * Both `verifyWebhookSignature` and `parseWebhookEvent` work in Vercel * Edge Runtime, Cloudflare Workers, Deno, browsers, and Node 18+. The * HMAC verification uses Web Crypto under the hood (see `./crypto.ts`) * and is **async** — make sure to `await` the call. */ /** * Parse a Mercado Pago webhook from the raw request body and URL search params. * MP sends the topic and resource id in EITHER the URL query string OR the * body, depending on integration version — this normalizes both shapes into a * single structure. * * **Pure function — synchronous, no I/O.** * * @example * ```ts * export async function POST(req: Request) { * const body = await req.json().catch(() => ({})); * const event = parseWebhookEvent(body, new URL(req.url).searchParams); * if (event && event.topic === 'preapproval') { * // refresh status from MP, update your store * } * return Response.json({ received: true }); * } * ``` */ declare function parseWebhookEvent(body: unknown, searchParams?: URLSearchParams): ParsedWebhookEvent | null; /** * Verify the HMAC-SHA256 signature MP sends in the `x-signature` header for * webhook authenticity. Returns true if the signature matches the expected * value derived from the integration's secret key AND the timestamp is * within the replay-tolerance window. * * **Async** — runs on Web Crypto under the hood, works in Edge Runtime. * * @param requestId The value of the `x-request-id` request header. * @param dataId The id of the resource the webhook is about (from query or body). * @param signatureHeader The full `x-signature` header value MP sent. * @param secret Your integration's webhook secret (configured in MP dev panel). * @param replayToleranceSeconds Optional override. Default 300s (5 min). * * @remarks * MP's `x-signature` header has the form: `ts=NNNNNNNN,v1=HEXSIGNATURE`. We * extract the timestamp and the v1 signature, then compute * `HMAC-SHA256(secret, "id:${dataId};request-id:${requestId};ts:${ts};")` * and compare with constant-time equality. * * **Replay protection**: rejects signatures whose `ts` is older than * `replayToleranceSeconds` (default 5min) — prevents an attacker who * captured a valid webhook from replaying it later. */ declare function verifyWebhookSignature(params: { requestId: string | null; dataId: string; signatureHeader: string | null; secret: string; replayToleranceSeconds?: number; }): Promise; /** * Mercado Pago OAuth flow — for marketplace integrations where YOUR app * cobra a través de cuentas MP de terceros (sellers in your platform). * * # The flow (3 legs) * * 1. **Authorize URL** — Redirect the seller to `buildAuthorizeUrl()`. They * log in to MP and approve your app. MP redirects them back to your * `redirect_uri` with `?code=AUTH_CODE&state=YOUR_STATE`. * 2. **Code exchange** — Your server POSTs to `/oauth/token` via * `exchangeCodeForToken()` with the code. Returns `{ access_token, * refresh_token, user_id, expires_in (~6h), ... }`. **Persist all of it.** * 3. **Token refresh** — Before `expires_in` runs out (or on 401), call * `refreshAccessToken()` with the saved `refresh_token` to get a fresh * access_token. The refresh_token does NOT expire and is the only way * to keep the integration alive long-term. * * # Per-seller MercadoPagoClient * * Once you have an OAuth `access_token` for a seller, instantiate a * `MercadoPagoClient({ accessToken })` AS THAT SELLER. All API calls then * happen on the seller's behalf — payments, refunds, subscriptions, * everything. * * # Marketplace fee * * To take a fee while collecting on the seller's behalf, pass * `marketplace`, `marketplaceFee`, `collectorId` to `createPreference()` * or `createOrder()`. See `MarketplaceParams` for details. * * # Setup * * 1. Register your application in MP's dev panel * (https://www.mercadopago.com.ar/developers/panel/applications) to get * `clientId` (= application id) and `clientSecret`. * 2. Configure the `redirect_uri` whitelist in the same panel — MP rejects * redirects to URIs not whitelisted. * 3. Pick a `marketplace` identifier (used in fee routing). */ /** * Build the URL the seller visits to authorize your app. Redirect them here. * On approval, MP redirects them to `redirect_uri?code=...&state=...`. * * @param state Optional opaque value echoed back in the redirect — use this * to bind the OAuth round-trip to a specific user/session and * prevent CSRF. Always set it in production. */ declare function buildAuthorizeUrl(params: { /** Your app's client ID (= application id from MP dev panel). */ clientId: string; /** Where MP redirects after approval. Must be whitelisted in MP panel. */ redirectUri: string; /** CSRF / session-binding token, echoed back. Strongly recommended. */ state?: string; /** * Override the authorize endpoint base. Default points to AR; for other * sites use `https://auth.mercadopago.com.{br,mx,co,cl,uy}/authorization`. */ authorizeUrl?: string; }): string; /** * Exchange the authorization code (from the OAuth redirect) for an * `OAuthToken`. POSTs to `/oauth/token` with `grant_type=authorization_code`. * * **Persist the entire response** — the `refresh_token` is the only way to * keep the integration alive long-term, and `user_id` identifies the seller. */ declare function exchangeCodeForToken(params: { clientId: string; clientSecret: string; /** The `code` query param from the OAuth redirect. */ code: string; /** Must match the `redirect_uri` used in `buildAuthorizeUrl`. */ redirectUri: string; /** Override the token endpoint (testing). */ tokenUrl?: string; /** Custom fetch (testing). */ fetchImpl?: typeof fetch; }): Promise; /** * Refresh an access_token using the saved refresh_token. Call this * proactively before `expires_in` runs out, or reactively on a 401 from a * per-seller MercadoPagoClient. * * The new response includes a fresh `refresh_token` — **always persist it, * replacing the old one**, even though MP often returns the same value. */ declare function refreshAccessToken(params: { clientId: string; clientSecret: string; refreshToken: string; tokenUrl?: string; fetchImpl?: typeof fetch; }): Promise; /** * Compute when an access_token will expire, given the timestamp it was * issued and the `expires_in` value (in seconds). * * @returns A unix-ms timestamp. */ declare function expirationTimeMs(issuedAtMs: number, expiresInSeconds: number | undefined): number; /** * Check whether an access_token is close to expiring. Use this to decide * whether to proactively refresh BEFORE making an API call. * * @param skewSeconds Buffer to refresh early (default 5 min). MP tokens * typically last 6h; refreshing in the last 5 min avoids * races with API calls that take a few seconds. */ declare function isExpiringSoon(expirationMs: number, skewSeconds?: number): boolean; /** * MP sandbox test cards for AR (MLA) — the official numbers MP publishes for * its TEST environment. Use these in unit tests + integration tests to * exercise the create_payment / charge_saved_card flows without touching a * real card. * * # When this matters * * Most non-trivial dev flows hit the issue of "I want to test approved / * rejected / pending paths" but MP's docs scatter the test card numbers * across multiple pages. This module collects them so you can `import { TEST_CARDS_AR }` * and pick the scenario you need. * * # Source * * AR (MLA) test cards published at * https://www.mercadopago.com.ar/developers/es/docs/checkout-api/additional-content/test-cards * * Last sync: 2026-05. */ /** * The full data needed to test a payment: * - `number` — 16 digits * - `cvv` — 3 digits * - `exp` — MM/YY (use any future date in TEST mode) * - `paymentMethodId` — what to pass as `payment_method_id` to create_payment * - `holderName` — special string that triggers the desired status * (e.g. "APRO" → approved, "OTHE" → rejected with bad CVV) */ interface TestCard { brand: string; number: string; cvv: string; exp: string; paymentMethodId: string; /** * Holder-name "magic strings" — MP routes the payment to a specific * status_detail based on this: * - `APRO` → status: approved * - `OTHE` → rejected (status_detail: cc_rejected_other_reason) * - `CONT` → pending (status_detail: pending_contingency) * - `CALL` → rejected (status_detail: cc_rejected_call_for_authorize) * - `FUND` → rejected (status_detail: cc_rejected_insufficient_amount) * - `SECU` → rejected (status_detail: cc_rejected_bad_filled_security_code) * - `EXPI` → rejected (status_detail: cc_rejected_bad_filled_date) * - `FORM` → rejected (status_detail: cc_rejected_bad_filled_other) */ holderNameToTest: Record; } /** * The MP-published test cards for AR. Pass `holderName: "APRO"` for an * approved payment, `"OTHE"` for a rejected one, etc. */ declare const TEST_CARDS_AR: Record; /** * Pre-built payer objects that MP recognizes as test buyers. Pair with * an APRO test card → status: approved. * * **Use a NEW email per call** if you don't want MP's idempotency-on-email * to dedupe — append a timestamp. */ declare const TEST_PAYERS_AR: { readonly approvedBuyer: () => { email: string; identification: { type: string; number: string; }; }; }; /** * Resolve a `(card, scenario)` pair to a ready-to-use `CreatePaymentParams`-like * object. Reduces boilerplate in test files. * * @example * ```ts * const card = buildTestCardScenario("VISA_CREDIT", "APRO", 1500); * await client.createPayment({ ...card, externalReference: "test-1" }); * ``` */ declare function buildTestCardScenario(cardKey: keyof typeof TEST_CARDS_AR, scenario: string, amountArs: number): { transactionAmount: number; paymentMethodId: string; payerEmail: string; description: string; installments: number; /** * Magic holder name — pass to MP frontend's CardForm `cardholderName` * field. (For server-side create_payment, pass via additional_info.) */ holderName: string; }; /** * 3DS (Strong Customer Authentication) analyzer for Mercado Pago Payments. * * # Background * * 3DS (3-D Secure / "verified by Visa", "Mastercard SecureCode") is the * issuer-side 2FA layer for card payments. MP triggers it automatically when: * - The card's issuer requires it (driven by MCC + amount + risk). * - The buyer's country mandates it (MX, BR, several EU countries). * * In Argentina (MLA), 3DS is OPTIONAL but strongly recommended for * high-value transactions and is required for some FCE MiPyMEs flows. * * # What this module does * * Given a `Payment` returned by `getPayment()` or `createPayment()`, derive * a normalized `ThreeDSInfo` telling you: * - Whether 3DS was triggered at all * - Whether it was frictionless (no buyer interaction) or required a challenge * - The challenge URL (if any) you must redirect the buyer to * - A human-readable description suitable for surfacing to the user * * # When to use * * Call `analyze3DS(payment)` after EVERY `createPayment()` for credit cards. * If `info.challengeUrl !== null`, you MUST redirect the buyer there before * the payment can complete — otherwise it stays in `pending` forever. */ /** * Analyze a Payment's 3DS state. Pure function, no I/O. */ declare function analyze3DS(payment: Payment): ThreeDSInfo; /** * Submit the 3DS challenge result back to MP after the buyer completes the * issuer challenge. Used as the FINAL step in the 3DS challenge flow: * * 1. `createPayment` returns `pending` + `pending_challenge` status_detail * 2. `analyze3DS(payment)` extracts the `challengeUrl` * 3. Buyer is redirected to `challengeUrl` and completes the challenge * 4. The issuer redirects to your `back_url` with a `challenge_complete=true` * (or similar query — depends on issuer / browser flow) * 5. **You call this method** to confirm the challenge and finalize the payment * * # Why this is separate * * Step 5 isn't documented as a SINGLE endpoint in MP's public docs — different * 3DS providers (Mastercard, Visa, Cabal) handle the challenge resolution * differently. This method tries the documented path: re-fetching the payment * via `getPayment` after the challenge — MP updates the status server-side * once the issuer reports the challenge result via their backchannel. * * # When to call * * - **Before** showing the user a final "approved/rejected" screen * - **After** the buyer is redirected back from the challenge URL * - **With backoff**: MP sometimes lags by a few seconds — recommended to * poll `getPayment` 3-5 times with 1s spacing if the first call still * returns `pending_challenge`. */ declare function confirmChallengeAndPoll(client: MercadoPagoClient, paymentId: string, options?: { /** Maximum number of polls. Default 5. */ maxAttempts?: number; /** Sleep between polls in ms. Default 1000ms. */ pollIntervalMs?: number; /** Optional AbortSignal to cap the total wait. */ signal?: AbortSignal; }): Promise<{ payment: Payment; threeDs: ThreeDSInfo; resolved: boolean; attempts: number; }>; /** * Pagination helpers — automatic pagination over MP's paginated endpoints * via AsyncIterable. Replaces the manual offset/limit loop. * * # Why * * MP's paginated endpoints (search_payments, search_subscriptions, * list_account_movements, list_settlements, search_merchant_orders, etc.) * cap responses at 100 items per page. Iterating "all matching X" without * helpers means writing the offset+limit loop in every caller — annoying * + error-prone (off-by-one bugs are common). * * # Usage * * ```ts * import { paginate } from "@ar-agents/mercadopago"; * * for await (const payment of paginate( * (offset) => client.searchPayments({ offset, limit: 100, status: "approved" }), * { extractItems: (page) => page.results ?? [], extractTotal: (page) => page.paging?.total ?? 0 }, * )) { * console.log(payment.id); * } * ``` * * Or use the convenience wrappers below: * * ```ts * for await (const payment of paginatePayments(client, { status: "approved" })) { * console.log(payment.id); * } * * // Materialize all (only when sure it fits in memory): * const allPayments = await collect(paginatePayments(client, { status: "approved" })); * ``` * * # Performance * * - **Streaming**: items are yielded as each page arrives — your downstream * work can start before all pages are fetched. * - **Bounded concurrency**: by default fetches one page at a time. Pass * `concurrency: 4` to prefetch up to 4 pages ahead (faster, more bandwidth). * - **Total cap**: pass `maxItems: 1000` to bail out early. * * # Edge cases handled * * - Empty pages (returns no items, terminates). * - Pages where total < expected (terminates correctly). * - Mid-iteration cancellation (caller breaks the for-await — no further fetches). * - Paging.total === 0 (terminates immediately). */ interface PaginateOptions { /** Extract the items array from a page. */ extractItems: (page: TPage) => TItem[]; /** * Extract the total count from a page. Used to know when to stop. * If not available, the iterator terminates when an empty page arrives. */ extractTotal?: (page: TPage) => number | undefined; /** Page size. Default 100 (MP's max for most endpoints). */ pageSize?: number; /** * Stop after yielding `maxItems` total. Useful for "first N matching" * queries that would otherwise iterate the full result set. */ maxItems?: number; /** * Number of pages to prefetch concurrently. Default 1 (no prefetch). * Higher = lower wall-clock time but more concurrent MP requests. */ concurrency?: number; } /** * Generic paginator. Most callers use the typed convenience wrappers below. * * @param fetchPage Function that fetches page N given the offset. */ declare function paginate(fetchPage: (offset: number, limit: number) => Promise, opts: PaginateOptions): AsyncGenerator; /** Materialize an AsyncIterable into an array. Caller's responsibility to ensure it fits. */ declare function collect(iter: AsyncIterable): Promise; declare function paginatePayments(client: MercadoPagoClient, filter?: Parameters[0], opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; declare function paginateSubscriptions(client: MercadoPagoClient, filter?: Parameters[0], opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; declare function paginateAccountMovements(client: MercadoPagoClient, filter?: { from?: string; to?: string; }, opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; declare function paginateSettlements(client: MercadoPagoClient, filter?: { from?: string; to?: string; status?: string; }, opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; declare function paginateMerchantOrders(client: MercadoPagoClient, filter?: Parameters[0], opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; declare function paginateSubscriptionPlans(client: MercadoPagoClient, filter?: { status?: string; }, opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; declare function paginateSubscriptionPayments(client: MercadoPagoClient, preapprovalId: string, opts?: { pageSize?: number; maxItems?: number; concurrency?: number; }): AsyncGenerator; /** * Token bucket rate limiter — proactive client-side rate limiting. * * # Why proactive * * The current client honors `Retry-After` after a 429 (reactive). That's * good but suboptimal: every 429 still costs you a network round-trip, an * error log, and adds latency to the retry. Proactive rate limiting reads * MP's `x-rate-limit-remaining` header and slows down BEFORE the next 429. * * # The token bucket model * * - The bucket holds N tokens (the burst capacity). * - Tokens refill at R tokens/second (the steady-state rate). * - Each request consumes 1 token. * - When the bucket is empty, requests wait until a token is available. * * Example: capacity=20, refill=10/s means "burst 20 requests, then 10/s * sustained". Matches MP's typical limits (precise numbers vary by endpoint * and aren't publicly documented). * * # Per-host vs global * * Default: one bucket per `MercadoPagoClient`. Pass a SHARED bucket to * multiple clients in marketplace setups so they share the rate limit * (otherwise each per-seller client would think it has its own quota). * * # Adaptive learning * * The bucket auto-tunes from response headers: if MP says * `x-rate-limit-remaining: 5` and the bucket has 50 tokens, the bucket * is over-spending. The `learnFromHeaders` method updates the available * count to the lower of (current, MP's stated remaining). */ interface RateLimiterOptions { /** Bucket capacity (max burst). Default 50. */ capacity?: number; /** Refill rate in tokens per second. Default 25. */ refillPerSecond?: number; /** * If true, the limiter calls `learnFromHeaders` after every successful * request to keep its bucket in sync with MP's actual quota. Default true. */ adaptive?: boolean; /** * Hard cap on how long `acquire()` will wait. If the bucket can't refill * in this time, `acquire()` rejects with `RateLimitTimeoutError`. * Default 30s — anything longer is probably better handled as an error. */ acquireTimeoutMs?: number; /** Time provider (testing). Defaults to Date.now. */ now?: () => number; } declare class RateLimitTimeoutError extends Error { readonly waitedMs: number; constructor(waitedMs: number); } declare class TokenBucketRateLimiter { private tokens; private lastRefill; private readonly capacity; private readonly refillPerSecond; private readonly adaptive; private readonly acquireTimeoutMs; private readonly now; constructor(opts?: RateLimiterOptions); /** * Acquire a token. Resolves immediately if tokens are available; * otherwise waits until one is. Rejects with `RateLimitTimeoutError` * if the wait exceeds `acquireTimeoutMs`. */ acquire(): Promise; /** * Best-effort acquire: returns true if a token was available, false * otherwise. Doesn't wait. Useful for "non-blocking" code paths that * want to fall back to a cached response or queue the request elsewhere. */ tryAcquire(): boolean; /** * Adaptive learning hook — call after every API response with MP's * rate-limit headers to keep the bucket in sync with reality. */ learnFromHeaders(headers: { remaining: number | null; resetSeconds: number | null; }): void; /** Inspect the current bucket state. */ getStats(): { tokens: number; capacity: number; refillPerSecond: number; }; private refill; } /** * Argentine issuer cuotas promotional catalog — embedded knowledge of which * banks/cards have "cuotas sin interés" (interest-free installment) deals * with which sellers, on which days. * * # Why embed this * * MP's `calculate_installments` API returns the CURRENT cuotas options for * a given (payment_method, amount, bin) tuple — but it doesn't tell the * agent which deals are GENERALLY available (e.g., "Naranja con Galicia, 6 * cuotas sin interés todos los martes"). Devs surface that information to * buyers BEFORE checkout to drive conversion. * * This catalog is the AR-specific knowledge that turns the toolkit from * "MP API wrapper" into "MP integration with retail context". * * # Sources * * - Each issuer's published "Cuotas Simples" / "Ahora 12" page * - BCRA Comunicación A 7825 (financiamiento al consumo) * - Manually verified against Naranja, Galicia, Santander, Macro, BBVA, ICBC * Patagonia, Banco Nación, Banco Provincia, Banco Ciudad, Comafi, HSBC * public landing pages * * # Maintenance * * The promos schedule changes seasonally. Update this file quarterly + when * BCRA publishes a new "Ahora N" program. PRs welcomed. * * Last sync: 2026-Q2. */ interface CuotasPromo { /** Issuer name (matches `list_issuers` response). */ issuer: string; /** Card brand (visa, master, amex, naranja, cabal, etc.). */ paymentMethodId: string; /** Number of interest-free installments. */ installments: number; /** Days of the week the promo applies. Empty = always. */ daysOfWeek?: Array<"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun">; /** ISO date when the promo starts (inclusive). */ startDate?: string; /** ISO date when the promo expires (inclusive). */ endDate?: string; /** Minimum purchase amount in ARS for the promo to apply. */ minAmountArs?: number; /** Maximum monthly cap on the promo per cardholder. Optional. */ maxAmountArs?: number; /** Free-form description shown to the buyer. ALWAYS surface verbatim. */ description: string; /** Categories where the promo applies (per BCRA codes). Empty = any. */ categories?: Array<"electronics" | "appliances" | "clothing" | "supermarket" | "travel" | "education" | "health" | "general">; } /** * The "Ahora 12 / 18 / 24 / 30" national program — recurring federal scheme * that subsidizes interest-free installments on essential categories. * * As of 2026-Q2: 3, 6, 12, 18, 24, 30 installment options on appliances, * electronics, clothing, books, school supplies, tires, eyewear, motorcycles, * national-tourism services. Not all categories qualify for all tiers. */ declare const AHORA_PROGRAM_PROMOS: CuotasPromo[]; /** * Issuer-specific promos (running in addition to the Ahora program). * * Note: these change frequently. Check `lastVerified` before relying. */ declare const AR_ISSUER_PROMOS: CuotasPromo[]; /** * Find applicable promos for a given context. * * Pure function — no I/O. Use to surface "cuotas sin interés" hints to the * buyer BEFORE they call `calculate_installments` (the API only returns * what's offered for the EXACT card, which the buyer hasn't entered yet). * * @example * ```ts * import { findApplicablePromos } from "@ar-agents/mercadopago"; * * const promos = findApplicablePromos({ * issuer: "Banco Galicia", * paymentMethodId: "visa", * amountArs: 50_000, * category: "supermarket", * date: new Date(), // optional, defaults to now * }); * // → [{ installments: 12, description: "Galicia ... 12 cuotas sin interés ...", ... }] * ``` */ declare function findApplicablePromos(args: { issuer?: string; paymentMethodId?: string; amountArs?: number; category?: NonNullable[number]; date?: Date; /** Include the Ahora program in addition to issuer-specific. Default true. */ includeAhoraProgram?: boolean; }): CuotasPromo[]; /** * Tool middleware — composable wrappers around any Vercel AI SDK tool. * * # The pattern * * Vercel AI SDK tools have a uniform shape: `{ description, inputSchema, execute }`. * Middleware wraps a tool's `execute()` with cross-cutting concerns (logging, * rate limiting, retries, metrics) WITHOUT modifying the tool itself. * * Compose middleware to layer behaviors: * * ```ts * import { withAuditLog, withRateLimit, withMetrics, compose } from "@ar-agents/mercadopago"; * * const baseTools = mercadoPagoTools(client, { state, backUrl }); * * const tools = Object.fromEntries( * Object.entries(baseTools).map(([name, tool]) => [ * name, * compose( * withMetrics(name, { onMetric: (m) => statsd.increment(...) }), * withRateLimit(rateLimiter), * withAuditLog(auditLogger, name), * )(tool), * ]) * ); * ``` * * # Why this matters * * Without middleware, every cross-cutting concern (audit, rate limit, retry) * has to be wired INTO the tool implementation OR repeated at every call * site. Middleware lets you add/remove/swap concerns from a single config * point — clean separation of concerns + testable in isolation. */ /** * A tool middleware — takes a tool, returns a wrapped tool with the same * shape but enhanced behavior in `execute()`. */ type ToolMiddleware = >(tool: T) => T; /** * Compose multiple middleware functions. The LAST middleware in the list * runs INNERMOST (closest to the original tool's execute): * * ``` * compose(a, b, c)(tool) == a(b(c(tool))) * ``` * * Reasoning: the most "core" concerns (e.g. audit log) typically wrap the * actual call closely (innermost), while observability layers (e.g. metrics, * tracing) sit outside. * * @example * ```ts * const enhance = compose( * withMetrics("create_payment"), // outer (records duration of everything below) * withRateLimit(limiter), // middle (rate-limits before the call) * withAuditLog(audit, "create_payment"), // inner (records the call result) * ); * const enhanced = enhance(originalTool); * ``` */ declare function compose(...middlewares: ToolMiddleware[]): ToolMiddleware; /** * Wrap a tool's `execute()` with audit logging. Every call records an entry * with operation, actor, inputHash, outcome, and duration. * * @param logger The configured AuditLogger. * @param operation The operation name (matches AuditOperation union). * @param actor Optional actor override (defaults to logger's defaultActor). */ declare function withAuditLog(logger: AuditLogger, operation: AuditOperation, actor?: string): ToolMiddleware; /** * Wrap a tool's `execute()` with rate limiting. Acquires a token from the * bucket BEFORE the call; if the bucket is empty, awaits up to the bucket's * `acquireTimeoutMs`. Throws `RateLimitTimeoutError` if the wait exceeds it. */ declare function withRateLimit(limiter: TokenBucketRateLimiter): ToolMiddleware; interface MetricsHook { /** * Called after every tool invocation. Synchronous, fire-and-forget. * Compatible with Datadog, StatsD, Prometheus client, OTEL meter, etc. */ onMetric: (event: { toolName: string; durationMs: number; success: boolean; errorCode?: string; }) => void; } /** * Wrap a tool's `execute()` with metrics emission. Records duration + a * success/error counter for every call. */ declare function withMetrics(toolName: string, hook: MetricsHook): ToolMiddleware; interface RetryOptions { /** Max attempts including initial. Default 3. */ maxAttempts?: number; /** Base backoff in ms (multiplied by 2^attempt). Default 250. */ baseBackoffMs?: number; /** * Predicate: should this error trigger a retry? Default: retries on * any thrown Error EXCEPT MercadoPagoError 4xx (those are user errors). */ shouldRetry?: (err: unknown, attempt: number) => boolean; /** Optional hook fired on every retry attempt. */ onRetry?: (event: { attempt: number; error: unknown; delayMs: number; }) => void; } /** * Wrap a tool's `execute()` with retry-with-backoff. Useful for tools that * call external APIs not protected by the underlying client's retry budget * (e.g., agent-side aggregation tools). * * The MercadoPagoClient already retries internally on 5xx/429, so layering * this on top of MP-backed tools usually means total retries = client × tool. * Use sparingly. */ declare function withRetry(opts?: RetryOptions): ToolMiddleware; /** * Apply a middleware to every tool in a ToolSet. Useful for blanket policies: * "all tools rate-limited", "all tools metrics-emitted". * * @example * ```ts * const baseTools = mercadoPagoTools(client, { state, backUrl }); * const limited = applyToAllTools(baseTools, withRateLimit(limiter)); * ``` */ declare function applyToAllTools>>(tools: T, middleware: ToolMiddleware): T; /** * TaxID validation across LATAM — pure-algorithm validators for the major * jurisdictions where MP operates. NO network calls. * * # Why * * Marketplace-style apps that span multiple LATAM countries need to * validate buyer/seller tax IDs in their respective formats: AR (DNI/CUIT/CUIL), * BR (CPF/CNPJ), MX (RFC), CL (RUT), CO (NIT), UY (RUT), PE (RUC). * * Each country has its own checksum algorithm. Wiring this once per app * is annoying + error-prone. Embedding it here means the agent can * validate ANY LATAM tax ID with a single tool call. * * # Sources * * - AR DNI/CUIT/CUIL: AFIP RG 100/1998 (modulo-11 checksum) * - BR CPF: Receita Federal (two-step modulo-11) * - BR CNPJ: Receita Federal (two-step weighted modulo) * - MX RFC: SAT regex + 13-char structure * - CL RUT: SII modulo-11 + check digit "0-9, K" * - CO NIT: DIAN modulo-11 * - UY RUT: 12-digit numeric + checksum * - PE RUC: SUNAT 11-digit + checksum */ type TaxIdCountry = "AR" | "BR" | "MX" | "CL" | "CO" | "UY" | "PE"; type TaxIdType = "DNI" | "CUIT" | "CUIL" | "CPF" | "CNPJ" | "RFC" | "RUT_CL" | "NIT" | "RUT_UY" | "RUC"; interface TaxIdValidationResult { valid: boolean; /** Bare digits/chars after normalization (no separators). */ normalized: string; /** Pretty-formatted version with country-specific separators. */ formatted: string | null; type: TaxIdType; country: TaxIdCountry; /** Spanish error message when invalid. Surface verbatim to users. */ error: string | null; } /** * Validate a tax ID against the appropriate country algorithm. * * @example * validateTaxId("20-12345678-6", "CUIT") * // → { valid: true, normalized: "20123456786", formatted: "20-12345678-6", ... } * * @example * validateTaxId("123.456.789-09", "CPF") * // → { valid: true, normalized: "12345678909", ... } */ declare function validateTaxId(input: string, type: TaxIdType): TaxIdValidationResult; /** * Convenience: try to detect the type from the input shape + country, then * validate. Useful when the agent doesn't know if it's a CPF or a CNPJ. * * @returns null when the input doesn't match any known type for the country */ declare function detectAndValidate(input: string, country: TaxIdCountry): TaxIdValidationResult | null; /** * Pure helpers — no I/O, deterministic, fast. Importable directly from the * package root or used via the agent tools (`compute_marketplace_fee`, * `explain_payment_status`). */ interface MarketplaceFeeRule { /** Fixed fee in ARS (added on top of percentage). */ flatArs?: number; /** Percentage of the transaction amount (0-100). */ percent?: number; /** Minimum fee floor in ARS. */ minArs?: number; /** Maximum fee cap in ARS. */ maxArs?: number; /** Round to nearest peso (default true). */ round?: boolean; } /** * Compute the exact `marketplace_fee` (in ARS) to pass to `create_order` / * `create_payment_preference` for a given transaction amount and fee rule. * * @example * // 5% fee with $50 floor and $5000 cap * computeMarketplaceFee(10000, { percent: 5, minArs: 50, maxArs: 5000 }) * // → 500 * * computeMarketplaceFee(500, { percent: 5, minArs: 50 }) * // → 50 (would be 25 by percent, floor lifts to 50) * * @example * // Flat $200 + 2% * computeMarketplaceFee(10000, { flatArs: 200, percent: 2 }) * // → 400 */ declare function computeMarketplaceFee(amountArs: number, rule: MarketplaceFeeRule): number; interface PaymentStatusExplanation { /** Spanish summary of the current state — surface to user. */ summary: string; /** What the agent should do next. Actionable guidance. */ recommendedAction: string; /** * Whether this state is FINAL (no further changes) or transient * (can still flip with another webhook). */ final: boolean; /** Whether the buyer paid successfully (approved). */ paid: boolean; /** * Whether this is a recoverable rejection (user can retry with another * card / different installments) vs a hard rejection (stop trying). */ retryable: boolean; } /** * Human-readable explanation of a Payment's current state — derives summary, * recommended action, finality, and whether the rejection is retryable from * `payment.status` + `payment.status_detail`. * * Pure function. Use the output to drive agent decisions ("¿reintento? * ¿le digo al cliente que cambie de tarjeta?") without having to memorize * every MP status_detail code. */ declare function explainPaymentStatus(payment: Payment): PaymentStatusExplanation; export { AHORA_PROGRAM_PROMOS, AR_ISSUER_PROMOS, AccountBalance, AccountInfo, AccountMovement, AuditLogger, AuditOperation, BankAccount, CardToken, CircuitBreaker, type CircuitBreakerOptions, CircuitOpenError, type CircuitState, CreateCardTokenParams, CreateCustomerParams, CreateOrderParams, CreatePaymentParams, CreatePointPaymentIntentParams, CreatePosParams, CreatePreapprovalParams, CreatePreferenceParams, CreateQrPaymentParams, CreateRefundParams, CreateStoreParams, CreateSubscriptionPlanParams, CreateWebhookParams, type CuotasPromo, Customer, CustomerCard, type DedupResult, Dispute, IdempotencyCache, IdentificationType, InstallmentOffer, Issuer, type MarketplaceFeeRule, MercadoPagoAccountTypeMismatchError, MercadoPagoAuthError, MercadoPagoAuthorizeForbiddenError, MercadoPagoBackUrlInvalidError, MercadoPagoClient, type MercadoPagoClientOptions, MercadoPagoError, MercadoPagoOverloadedError, MercadoPagoPaymentRejectedError, MercadoPagoRateLimitError, MercadoPagoSelfPaymentError, MercadoPagoTimeoutError, type MercadoPagoToolsOptions, MerchantOrder, type MetricsHook, OAuthToken, Order, type PaginateOptions, ParsedWebhookEvent, Payment, PaymentMethod, type PaymentStatusExplanation, PaymentsSearchResult, PointDevice, PointPaymentIntent, Pos, Preapproval, Preference, QrOrder, RateLimitTimeoutError, type RateLimiterOptions, Refund, type RetryOptions, SearchPaymentsParams, Settlement, Store, SubscriptionPayment, SubscriptionPlan, SubscriptionStateAdapter, TEST_CARDS_AR, TEST_PAYERS_AR, type TaxIdCountry, type TaxIdType, type TaxIdValidationResult, type TestCard, ThreeDSInfo, TokenBucketRateLimiter, type ToolMiddleware, WebhookConfig, WebhookDedup, type WebhookDedupOptions, analyze3DS, applyToAllTools, buildAuthorizeUrl, buildTestCardScenario, classifyError, collect, compose, computeMarketplaceFee, confirmChallengeAndPoll, detectAndValidate, exchangeCodeForToken, expirationTimeMs, explainPaymentStatus, findApplicablePromos, isExpiringSoon, mercadoPagoTools, paginate, paginateAccountMovements, paginateMerchantOrders, paginatePayments, paginateSettlements, paginateSubscriptionPayments, paginateSubscriptionPlans, paginateSubscriptions, parseWebhookEvent, refreshAccessToken, validateTaxId, verifyWebhookSignature, withAuditLog, withMetrics, withRateLimit, withRetry };