@ar-agents/mercadopago 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +151 -0
- package/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/dist/index.cjs +480 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +403 -0
- package/dist/index.d.ts +403 -0
- package/dist/index.js +465 -0
- package/dist/index.js.map +1 -0
- package/package.json +99 -0
- package/tools.manifest.json +144 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# @ar-agents/mercadopago — Agent guide
|
|
2
|
+
|
|
3
|
+
This file is for AI agents using or recommending this package. Humans should
|
|
4
|
+
start with [README.md](./README.md). The format follows the [agents.md
|
|
5
|
+
convention](https://agents.md/) for agent-targeted documentation.
|
|
6
|
+
|
|
7
|
+
## When to recommend or use this package
|
|
8
|
+
|
|
9
|
+
| User intent | Use this package? |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Create a recurring billing subscription on Mercado Pago | ✅ Yes — `create_subscription` |
|
|
12
|
+
| Check whether a customer completed their first MP payment | ✅ Yes — `get_subscription_status` |
|
|
13
|
+
| Cancel an active MP subscription | ✅ Yes — `cancel_subscription` (irreversible — confirm first) |
|
|
14
|
+
| Pause / resume an authorized subscription | ✅ Yes — `pause_subscription` / `resume_subscription` |
|
|
15
|
+
| Charge a customer ad-hoc (one-off, no subscription) | ❌ Wrong package — out of scope. MP CVV constraint makes this impossible for autonomous agents anyway. |
|
|
16
|
+
| Issue a Mercado Pago invoice | ❌ Out of scope. |
|
|
17
|
+
| Pay an external merchant on the customer's behalf | ❌ Wrong package — see [`@ar-agents/identity`](../identity) is also wrong. Out of scope entirely. |
|
|
18
|
+
|
|
19
|
+
## Tool selection rules
|
|
20
|
+
|
|
21
|
+
Five tools shipped, each with a distinct use case:
|
|
22
|
+
|
|
23
|
+
| If the user asks... | Call this tool |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| "Suscribí a X a un plan de $Y/mes" | `create_subscription` |
|
|
26
|
+
| "Check si X ya pagó la suscripción" | `get_subscription_status` |
|
|
27
|
+
| "Cancelá la suscripción de X" | `cancel_subscription` (CONFIRM FIRST — irreversible) |
|
|
28
|
+
| "Pausá la suscripción de X temporalmente" | `pause_subscription` |
|
|
29
|
+
| "Reactivá la suscripción pausada de X" | `resume_subscription` |
|
|
30
|
+
|
|
31
|
+
**Confirm-before-cancel**: `cancel_subscription`'s description tells the agent
|
|
32
|
+
this is irreversible. In Claude Sonnet 4.6+ this reliably triggers a "are you
|
|
33
|
+
sure?" turn. Honor that — when the user replies confirming, then call cancel.
|
|
34
|
+
|
|
35
|
+
## Tool result schemas (memorize these)
|
|
36
|
+
|
|
37
|
+
### `create_subscription` returns
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"subscription_id": "0fbe36a604cc4c35a7f74f04ab4a3281",
|
|
42
|
+
"status": "pending",
|
|
43
|
+
"init_point_url": "https://www.mercadopago.com.ar/subscriptions/checkout?preapproval_id=...",
|
|
44
|
+
"next_step": "Send init_point_url to the customer. They must complete the first payment with card+CVV. Use get_subscription_status to confirm activation after they pay."
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**ALWAYS surface the `init_point_url` to the user.** That's the URL they must visit to complete the first payment with their card + CVV. **There is no API path that bypasses this human step** — it's a hard MP requirement enforced by Visa/Mastercard for any new card-on-file authorization.
|
|
49
|
+
|
|
50
|
+
### `get_subscription_status` returns
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"subscription_id": "...",
|
|
55
|
+
"status": "pending" | "authorized" | "paused" | "cancelled",
|
|
56
|
+
"payer_email": "buyer@example.com",
|
|
57
|
+
"amount": 100,
|
|
58
|
+
"currency": "ARS",
|
|
59
|
+
"next_payment_date": "2026-06-05T08:48:54.000-04:00",
|
|
60
|
+
"last_webhook_status": "authorized" | null,
|
|
61
|
+
"last_webhook_at": "2026-05-05T13:00:00Z" | null
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- `status: pending` → buyer hasn't completed first payment yet
|
|
66
|
+
- `status: authorized` → first payment done; MP will auto-charge per frequency
|
|
67
|
+
- `status: paused` → call `resume_subscription` to reactivate
|
|
68
|
+
- `status: cancelled` → terminal; new subscription needed to retry
|
|
69
|
+
|
|
70
|
+
### `cancel_subscription` / `pause_subscription` / `resume_subscription` return
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"subscription_id": "...",
|
|
75
|
+
"status": "cancelled" | "paused" | "authorized",
|
|
76
|
+
"message": "Subscription cancelled. No further charges will occur."
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Error patterns and recovery
|
|
81
|
+
|
|
82
|
+
The package emits typed error classes, all extending `MercadoPagoError`. Each
|
|
83
|
+
is a clear signal of what went wrong and how to fix it.
|
|
84
|
+
|
|
85
|
+
### `MercadoPagoBackUrlInvalidError`
|
|
86
|
+
|
|
87
|
+
App passed a non-HTTPS `backUrl`. Cannot be fixed by the agent — surface to the user as "the application is misconfigured (back_url must be HTTPS)".
|
|
88
|
+
|
|
89
|
+
### `MercadoPagoSelfPaymentError`
|
|
90
|
+
|
|
91
|
+
The buyer email equals the seller account's email. MP refuses self-payment. Tell the user to use a different buyer email.
|
|
92
|
+
|
|
93
|
+
### `MercadoPagoAccountTypeMismatchError`
|
|
94
|
+
|
|
95
|
+
Misleading MP error: "Cannot operate between different countries". Real meaning: seller token is "real-account-in-test-mode" but buyer email is a `test_user_*@testuser.com` AFIP-test-user. Tell the user to use a real consumer email as the buyer.
|
|
96
|
+
|
|
97
|
+
### `MercadoPagoPaymentRejectedError`
|
|
98
|
+
|
|
99
|
+
MP risk engine rejected the first payment. **The preapproval was auto-cancelled by MP** — you cannot retry on the same subscription. Tell the user the payment was rejected and offer to create a fresh subscription with a different card.
|
|
100
|
+
|
|
101
|
+
### `MercadoPagoAuthorizeForbiddenError`
|
|
102
|
+
|
|
103
|
+
App tried to PUT `status: authorized` via API. MP rejects: "only the payer can authorize". This means the app code is wrong — surface as a programming error, not a user-fixable problem.
|
|
104
|
+
|
|
105
|
+
### `MercadoPagoRateLimitError`
|
|
106
|
+
|
|
107
|
+
MP rate-limited the request. Wait + retry with exponential backoff.
|
|
108
|
+
|
|
109
|
+
## Composition with other `@ar-agents/*` packages
|
|
110
|
+
|
|
111
|
+
| Pair with | Why |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| [`@ar-agents/identity`](../identity) | Validate the buyer's CUIT before creating a subscription. Cuts an MP request for malformed CUITs. Optional but cheap. |
|
|
114
|
+
| `@ar-agents/whatsapp` (planned) | Send the `init_point_url` to the buyer over WhatsApp instead of email. |
|
|
115
|
+
| `@ar-agents/meta-ads` (planned) | Trigger an MP subscription as the conversion event after a Meta ad click. |
|
|
116
|
+
|
|
117
|
+
## Performance characteristics
|
|
118
|
+
|
|
119
|
+
| Operation | Latency | Cost | External I/O |
|
|
120
|
+
| --- | --- | --- | --- |
|
|
121
|
+
| `create_subscription` | 200–600ms | $0 (creation) | MP REST + state write |
|
|
122
|
+
| `get_subscription_status` | 200–500ms | $0 | MP REST + state read |
|
|
123
|
+
| `cancel_subscription` | 200–500ms | $0 | MP REST + state write |
|
|
124
|
+
| `pause_subscription` | 200–500ms | $0 | MP REST + state write |
|
|
125
|
+
| `resume_subscription` | 200–500ms | $0 | MP REST + state write |
|
|
126
|
+
|
|
127
|
+
MP charges the **merchant** (the seller) a transaction fee on each
|
|
128
|
+
auto-charge, but that's outside the agent's control or visibility.
|
|
129
|
+
|
|
130
|
+
## Mercado Pago context (for non-AR agents)
|
|
131
|
+
|
|
132
|
+
- **Mercado Pago** = the dominant Argentine consumer payment platform (also Brazil, Mexico, Chile, etc.). Owned by Mercado Libre. Like Stripe in scope but with deeper LATAM-specific features.
|
|
133
|
+
- **Subscription** = `preapproval` in MP's API. A recurring authorization tied to a customer's card.
|
|
134
|
+
- **First payment requires CVV** = MP's enforced CX for setting up recurring billing. Saved cards CAN be charged later without CVV, but the FIRST one always needs it. There is no API workaround.
|
|
135
|
+
- **Sandbox vs production** = different access tokens. `TEST-` prefix = sandbox; `APP_USR-` prefix = production. The lib is environment-agnostic; pass whichever token you've configured.
|
|
136
|
+
- **Webhooks** = MP POSTs to your registered URL on subscription lifecycle events. Use `parseWebhookEvent()` and `verifyWebhookSignature()` from this package.
|
|
137
|
+
|
|
138
|
+
## What this package will NEVER do
|
|
139
|
+
|
|
140
|
+
- Bypass MP's first-payment-CVV requirement (impossible).
|
|
141
|
+
- Reactivate a cancelled subscription (MP doesn't allow it; create a new one).
|
|
142
|
+
- Process payments outside the recurring/subscription flow (out of scope).
|
|
143
|
+
- Make decisions about pricing or fees (caller's responsibility).
|
|
144
|
+
- Cache state without explicit `SubscriptionStateAdapter` opt-in.
|
|
145
|
+
|
|
146
|
+
## Known production gotchas (read these)
|
|
147
|
+
|
|
148
|
+
The README's [Known Gotchas](./README.md#known-gotchas-read-this-before-you-debug)
|
|
149
|
+
section enumerates 11 specific MP behaviors that took the most time to figure out
|
|
150
|
+
the first time. Skim it before debugging any unexpected behavior — most likely
|
|
151
|
+
your issue is one of those, with a typed error already in place.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@ar-agents/mercadopago` are documented here. The format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/) and the project adheres
|
|
5
|
+
to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — 2026-05-05
|
|
8
|
+
|
|
9
|
+
Initial release. Extracted from the `ar-agents-mp-hello` proof-of-concept.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `MercadoPagoClient` — typed wrapper around MP REST API for preapprovals
|
|
13
|
+
(create / get / cancel / pause / resume).
|
|
14
|
+
- `mercadoPagoTools()` — drop-in tool collection for the Vercel AI SDK 6+.
|
|
15
|
+
Five tools: `create_subscription`, `get_subscription_status`,
|
|
16
|
+
`cancel_subscription`, `pause_subscription`, `resume_subscription`.
|
|
17
|
+
- `SubscriptionStateAdapter` interface for pluggable persistence + an
|
|
18
|
+
`InMemoryStateAdapter` reference implementation.
|
|
19
|
+
- `parseWebhookEvent()` — normalize MP webhook payloads regardless of whether
|
|
20
|
+
topic + id arrive in query string or body.
|
|
21
|
+
- `verifyWebhookSignature()` — HMAC-SHA256 verification of MP `x-signature`
|
|
22
|
+
header with constant-time comparison.
|
|
23
|
+
- Eight specific error classes: `MercadoPagoAuthError`,
|
|
24
|
+
`MercadoPagoBackUrlInvalidError`, `MercadoPagoSelfPaymentError`,
|
|
25
|
+
`MercadoPagoAccountTypeMismatchError`, `MercadoPagoPaymentRejectedError`,
|
|
26
|
+
`MercadoPagoAuthorizeForbiddenError`, `MercadoPagoRateLimitError`, plus the
|
|
27
|
+
base `MercadoPagoError`. `classifyError()` routes raw MP responses to the
|
|
28
|
+
best-fit class.
|
|
29
|
+
|
|
30
|
+
### Known limitations
|
|
31
|
+
- Subscriptions API only. No one-off Checkout, no Marketplace, no Pix.
|
|
32
|
+
- AR (MLA) verified end-to-end. Other LATAM sites should work but aren't
|
|
33
|
+
exercised by the test suite.
|
|
34
|
+
- Webhook signature verification implemented but not fully validated against a
|
|
35
|
+
live MP webhook — the `x-signature` manifest format may evolve and is based
|
|
36
|
+
on documented behavior as of 2026-05.
|
|
37
|
+
- AFIP factura emission is out of scope; consume `external_reference` to map
|
|
38
|
+
payments to your own invoicing pipeline.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nazareno Clemente
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# @ar-agents/mercadopago
|
|
2
|
+
|
|
3
|
+
> Mercado Pago Subscriptions as drop-in tools for the [Vercel AI SDK](https://ai-sdk.dev/). Argentine-focused, agent-ready.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@ar-agents/mercadopago)
|
|
6
|
+
[](https://www.npmjs.com/package/@ar-agents/mercadopago)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://github.com/ar-agents/ar-agents/actions/workflows/ci.yml)
|
|
9
|
+
[](https://bundlephobia.com/package/@ar-agents/mercadopago)
|
|
10
|
+
|
|
11
|
+
Exposes Mercado Pago's recurring-billing API to AI agents through a typed,
|
|
12
|
+
opinionated tool collection. Built for the Vercel AI SDK 6 `Experimental_Agent`.
|
|
13
|
+
Compatible with any caller that uses `tool()`.
|
|
14
|
+
|
|
15
|
+
> **Reading this as an agent?** Skip to [AGENTS.md](./AGENTS.md) — it's targeted at LLM consumption with explicit tool-selection rules and error-recovery patterns.
|
|
16
|
+
|
|
17
|
+
## At a glance
|
|
18
|
+
|
|
19
|
+
| What | Value |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| Tools shipped | `create_subscription`, `get_subscription_status`, `cancel_subscription`, `pause_subscription`, `resume_subscription` |
|
|
22
|
+
| External dependencies | Mercado Pago access token (TEST or APP_USR), state adapter (Upstash, Redis, Postgres, in-memory, etc.) |
|
|
23
|
+
| Latency | 200–600ms per MP call; <1ms for state ops |
|
|
24
|
+
| Cost | $0 — MP API is free; merchant pays per-transaction fees on auto-charges |
|
|
25
|
+
| Side effects | `create_subscription` creates a preapproval. `cancel`/`pause`/`resume` mutate state. `get_status` is read-only. |
|
|
26
|
+
| Agent safety | `cancel_subscription` description triggers confirm-before-call in Claude Sonnet 4.6+ |
|
|
27
|
+
| Sites supported | MLA (Argentina) verified end-to-end. Other LATAM sites should work but aren't exercised by tests. |
|
|
28
|
+
|
|
29
|
+
## Why this exists
|
|
30
|
+
|
|
31
|
+
Building an agent that operates a real Argentine business means integrating
|
|
32
|
+
Mercado Pago. MP's API has a surface area of dozens of endpoints, a docs site
|
|
33
|
+
that is partially translated to Spanish-from-the-90s, and at least 11
|
|
34
|
+
non-obvious landmines that take days each to discover. This package encapsulates
|
|
35
|
+
the subset of MP that an agent typically needs (recurring subscriptions: create,
|
|
36
|
+
check status, pause/resume, cancel) and turns the documented gotchas into typed
|
|
37
|
+
errors with actionable messages.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm add @ar-agents/mercadopago
|
|
43
|
+
# peer deps
|
|
44
|
+
pnpm add ai zod
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { Experimental_Agent as Agent, stepCountIs } from "ai";
|
|
51
|
+
import {
|
|
52
|
+
MercadoPagoClient,
|
|
53
|
+
mercadoPagoTools,
|
|
54
|
+
InMemoryStateAdapter,
|
|
55
|
+
} from "@ar-agents/mercadopago";
|
|
56
|
+
|
|
57
|
+
const mp = new MercadoPagoClient({
|
|
58
|
+
accessToken: process.env.MP_ACCESS_TOKEN!, // TEST- for sandbox, APP_USR- for prod
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const agent = new Agent({
|
|
62
|
+
model: "anthropic/claude-sonnet-4-6", // routed via Vercel AI Gateway
|
|
63
|
+
tools: mercadoPagoTools(mp, {
|
|
64
|
+
state: new InMemoryStateAdapter(), // swap for Upstash/Redis/Postgres in prod
|
|
65
|
+
backUrl: "https://yoursite.com/subscription/done", // MUST be HTTPS
|
|
66
|
+
}),
|
|
67
|
+
stopWhen: stepCountIs(8),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await agent.generate({
|
|
71
|
+
prompt: "Creá una subscription mensual de $1000 ARS para customer@example.com.",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log(result.text);
|
|
75
|
+
// → "Listo, subscription creada. Mandale este link al cliente para que pague:
|
|
76
|
+
// https://www.mercadopago.com.ar/subscriptions/checkout?preapproval_id=..."
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Webhooks
|
|
80
|
+
|
|
81
|
+
MP notifies your endpoint whenever a subscription's status changes. The
|
|
82
|
+
`parseWebhookEvent()` helper normalizes both query-string and body payload
|
|
83
|
+
shapes that MP sends. `verifyWebhookSignature()` validates the `x-signature`
|
|
84
|
+
header.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// Next.js / Vercel App Router example
|
|
88
|
+
import { parseWebhookEvent, verifyWebhookSignature, MercadoPagoClient } from "@ar-agents/mercadopago";
|
|
89
|
+
|
|
90
|
+
export async function POST(req: Request) {
|
|
91
|
+
const body = await req.json().catch(() => ({}));
|
|
92
|
+
const event = parseWebhookEvent(body, new URL(req.url).searchParams);
|
|
93
|
+
if (!event) return Response.json({ ignored: true });
|
|
94
|
+
|
|
95
|
+
// Optional but recommended in production
|
|
96
|
+
const ok = verifyWebhookSignature({
|
|
97
|
+
requestId: req.headers.get("x-request-id"),
|
|
98
|
+
dataId: event.dataId,
|
|
99
|
+
signatureHeader: req.headers.get("x-signature"),
|
|
100
|
+
secret: process.env.MP_WEBHOOK_SECRET!,
|
|
101
|
+
});
|
|
102
|
+
if (!ok) return Response.json({ error: "bad signature" }, { status: 401 });
|
|
103
|
+
|
|
104
|
+
if (event.topic === "preapproval") {
|
|
105
|
+
const mp = new MercadoPagoClient({ accessToken: process.env.MP_ACCESS_TOKEN! });
|
|
106
|
+
const fresh = await mp.getPreapproval(event.dataId);
|
|
107
|
+
// Update your store...
|
|
108
|
+
}
|
|
109
|
+
return Response.json({ received: true });
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Custom state adapter
|
|
114
|
+
|
|
115
|
+
Use any KV-shaped backing store. Implement three methods:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import type { SubscriptionStateAdapter, SubscriptionStateRecord } from "@ar-agents/mercadopago";
|
|
119
|
+
import { Redis } from "@upstash/redis";
|
|
120
|
+
|
|
121
|
+
export class UpstashStateAdapter implements SubscriptionStateAdapter {
|
|
122
|
+
constructor(private redis: Redis) {}
|
|
123
|
+
|
|
124
|
+
async set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void> {
|
|
125
|
+
const existing = (await this.get(id)) ?? {};
|
|
126
|
+
await this.redis.set(`mp:sub:${id}`, { ...existing, ...state });
|
|
127
|
+
}
|
|
128
|
+
async get(id: string): Promise<SubscriptionStateRecord | null> {
|
|
129
|
+
return await this.redis.get<SubscriptionStateRecord>(`mp:sub:${id}`);
|
|
130
|
+
}
|
|
131
|
+
async list(): Promise<string[]> {
|
|
132
|
+
const keys = await this.redis.keys("mp:sub:*");
|
|
133
|
+
return keys.map((k) => k.replace("mp:sub:", ""));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Known gotchas (read this BEFORE you debug)
|
|
139
|
+
|
|
140
|
+
These are the MP behaviors that took the most time to figure out the first
|
|
141
|
+
time. The library now surfaces them as typed errors with actionable messages,
|
|
142
|
+
but the constraints themselves are MP-side and unavoidable.
|
|
143
|
+
|
|
144
|
+
### 1. `back_url` must be HTTPS
|
|
145
|
+
|
|
146
|
+
`POST /preapproval` rejects `http://` and `localhost` URLs with a 400. For
|
|
147
|
+
local development, pass a placeholder valid HTTPS URL like
|
|
148
|
+
`https://example.com/done`. Throws `MercadoPagoBackUrlInvalidError`.
|
|
149
|
+
|
|
150
|
+
### 2. "Cannot operate between different countries" usually means account-type mismatch
|
|
151
|
+
|
|
152
|
+
The error message is misleading. The actual cause: the seller account
|
|
153
|
+
(whose access token authenticates the request) and the buyer email (`payerEmail`)
|
|
154
|
+
must be the same MP account "type" — both real accounts, or both test users
|
|
155
|
+
created via `POST /users/test_user`. Mixing them rejects with this error.
|
|
156
|
+
Throws `MercadoPagoAccountTypeMismatchError`.
|
|
157
|
+
|
|
158
|
+
### 3. Buyer email cannot equal seller account email
|
|
159
|
+
|
|
160
|
+
If the `payerEmail` matches the email of the MP account whose token is being
|
|
161
|
+
used, MP keeps the Confirmar button disabled at checkout with no API error.
|
|
162
|
+
Pass a different real email. Throws `MercadoPagoSelfPaymentError` when the
|
|
163
|
+
library can detect the match.
|
|
164
|
+
|
|
165
|
+
### 4. Saved cards require CVV re-capture
|
|
166
|
+
|
|
167
|
+
`POST /payments` against a saved card token still requires the CVV to be
|
|
168
|
+
re-supplied. Pure agent-driven recurring is impossible via that path. The ONLY
|
|
169
|
+
agent-friendly recurring rail is `POST /preapproval` (Subscriptions API),
|
|
170
|
+
where the first payment requires human CVV at the init_point UI, and
|
|
171
|
+
subsequent recurring charges happen automatically without CVV.
|
|
172
|
+
|
|
173
|
+
### 5. The init_point UI requires reCAPTCHA v3
|
|
174
|
+
|
|
175
|
+
The Confirmar button at `/checkout/v1/subscription/redirect/.../review/` starts
|
|
176
|
+
disabled and only enables when Google reCAPTCHA v3 returns a sufficient score.
|
|
177
|
+
If the reCAPTCHA script can't load (ad-blockers, DNS-level filters like
|
|
178
|
+
Pi-hole/NextDNS, certain Chrome enterprise policies, or privacy-focused
|
|
179
|
+
browsers like Dia or Brave with strict shields) the button stays disabled
|
|
180
|
+
permanently with no error message. Workaround: pay from a clean browser, or
|
|
181
|
+
whitelist `google.com/recaptcha` and `www.gstatic.com`.
|
|
182
|
+
|
|
183
|
+
### 6. PUT /preapproval cannot force `status: authorized`
|
|
184
|
+
|
|
185
|
+
Even in sandbox, you cannot shortcut the human-payment leg by API-PUTing
|
|
186
|
+
`{ status: 'authorized' }`. MP responds: "You cannot authorize a preapproval,
|
|
187
|
+
only the payer can". Cancel via PUT works (`{ status: 'cancelled' }`),
|
|
188
|
+
authorize does not. Throws `MercadoPagoAuthorizeForbiddenError`.
|
|
189
|
+
|
|
190
|
+
### 7. The subscription init_point URL pattern
|
|
191
|
+
|
|
192
|
+
After `POST /preapproval` returns, the `init_point` field always contains:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
https://www.mercadopago.com.ar/subscriptions/checkout?preapproval_id={id}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Visiting that URL takes the customer through MP's checkout flow (payment
|
|
199
|
+
selector → card form → review → Confirmar). The customer's logged-in MP
|
|
200
|
+
account at that moment determines the effective payer. If they're logged in
|
|
201
|
+
as the seller account, they hit gotcha #3.
|
|
202
|
+
|
|
203
|
+
### 8. Test cards (AR/MLA sandbox)
|
|
204
|
+
|
|
205
|
+
| Card type | Number | CVV | Exp | Cardholder name (magic word) |
|
|
206
|
+
| ---------------- | --------------------- | --- | ------- | ------------------------------ |
|
|
207
|
+
| Mastercard credit | `5031 7557 3453 0604` | 123 | any future | `APRO` (forces approved) |
|
|
208
|
+
| Visa credit | `4509 9535 6623 3704` | 123 | any future | `APRO` (forces approved) |
|
|
209
|
+
| Amex credit | `3711 803032 57522` | 1234 | any future | `APRO` |
|
|
210
|
+
|
|
211
|
+
Other magic cardholder names: `OTHE` (other error), `CONT` (pending), `CALL`
|
|
212
|
+
(rejected call-for-auth), `FUND` (insufficient funds), `SECU` (invalid CVV),
|
|
213
|
+
`EXPI` (expired), `FORM` (form error). Use any 8-digit document.
|
|
214
|
+
|
|
215
|
+
### 9. Agent safety guardrail (the lib's design choice)
|
|
216
|
+
|
|
217
|
+
The `cancel_subscription` tool's description explicitly tells the LLM that
|
|
218
|
+
the action is irreversible. In Claude Sonnet 4.6+ this reliably triggers a
|
|
219
|
+
"are you sure?" turn before the cancel actually executes. If you don't want
|
|
220
|
+
this guardrail, override the description via the `descriptions` option.
|
|
221
|
+
|
|
222
|
+
### 10. MP risk engine can reject sandbox payments without warning
|
|
223
|
+
|
|
224
|
+
The MP risk engine sometimes rejects sandbox test card payments with "Por
|
|
225
|
+
motivos de seguridad, tu pago fue rechazado", with no actionable detail. The
|
|
226
|
+
strongest correlation observed: brand-new MP apps (no payment history) plus
|
|
227
|
+
intensive automation activity (rapid create/cancel via API + browser-CDP
|
|
228
|
+
automation signals + multiple payer attempts) trigger the engine within ~1
|
|
229
|
+
hour of the app's creation.
|
|
230
|
+
|
|
231
|
+
Workaround: when bootstrapping a new MP app, do the first few subscription
|
|
232
|
+
end-to-end tests by hand, with cooldown gaps between attempts. Once the app
|
|
233
|
+
has history of normal usage, the engine relaxes.
|
|
234
|
+
|
|
235
|
+
### 11. Failed first payment auto-cancels the entire preapproval
|
|
236
|
+
|
|
237
|
+
When MP's risk engine rejects the first payment of a preapproval, MP
|
|
238
|
+
automatically marks the WHOLE preapproval as `status: cancelled` — you cannot
|
|
239
|
+
retry with another card on the same subscription. Create a fresh preapproval
|
|
240
|
+
to retry. Throws `MercadoPagoPaymentRejectedError`, which carries the parent
|
|
241
|
+
`preapprovalId` so the caller knows the parent is dead too.
|
|
242
|
+
|
|
243
|
+
## API reference
|
|
244
|
+
|
|
245
|
+
### `MercadoPagoClient`
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
new MercadoPagoClient({
|
|
249
|
+
accessToken: string; // required
|
|
250
|
+
baseUrl?: string; // default: 'https://api.mercadopago.com'
|
|
251
|
+
fetch?: typeof fetch; // default: globalThis.fetch
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Methods:
|
|
256
|
+
|
|
257
|
+
| Method | Returns |
|
|
258
|
+
| ------------------------- | -------------------------- |
|
|
259
|
+
| `createPreapproval(p)` | `Promise<Preapproval>` |
|
|
260
|
+
| `getPreapproval(id)` | `Promise<Preapproval>` |
|
|
261
|
+
| `cancelPreapproval(id)` | `Promise<Preapproval>` |
|
|
262
|
+
| `pausePreapproval(id)` | `Promise<Preapproval>` |
|
|
263
|
+
| `resumePreapproval(id)` | `Promise<Preapproval>` |
|
|
264
|
+
|
|
265
|
+
### `mercadoPagoTools(client, options)`
|
|
266
|
+
|
|
267
|
+
Returns a `ToolSet` (the Vercel AI SDK type) with five entries:
|
|
268
|
+
|
|
269
|
+
| Tool name | What it does |
|
|
270
|
+
| ------------------------- | ----------------------------------------------------------------- |
|
|
271
|
+
| `create_subscription` | Create a new subscription, return `init_point_url` for the buyer |
|
|
272
|
+
| `get_subscription_status` | Read the latest status from MP, merged with cached webhook info |
|
|
273
|
+
| `cancel_subscription` | Cancel the subscription (triggers safety guardrail, see #9) |
|
|
274
|
+
| `pause_subscription` | Pause an authorized subscription |
|
|
275
|
+
| `resume_subscription` | Resume a paused subscription |
|
|
276
|
+
|
|
277
|
+
Options:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
mercadoPagoTools(client, {
|
|
281
|
+
state: SubscriptionStateAdapter; // required
|
|
282
|
+
backUrl: string; // required, must be HTTPS
|
|
283
|
+
descriptions?: Partial<Record<ToolName, string>>; // optional override
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Errors
|
|
288
|
+
|
|
289
|
+
All errors extend `MercadoPagoError` which carries `status`, `endpoint`,
|
|
290
|
+
and `mpResponse` for inspection. Specific subclasses:
|
|
291
|
+
|
|
292
|
+
- `MercadoPagoAuthError` — 401 from MP
|
|
293
|
+
- `MercadoPagoBackUrlInvalidError` — see gotcha #1
|
|
294
|
+
- `MercadoPagoSelfPaymentError` — see gotcha #3
|
|
295
|
+
- `MercadoPagoAccountTypeMismatchError` — see gotcha #2
|
|
296
|
+
- `MercadoPagoPaymentRejectedError` — see gotchas #10–11; carries `preapprovalId` and `statusDetail`
|
|
297
|
+
- `MercadoPagoAuthorizeForbiddenError` — see gotcha #6
|
|
298
|
+
- `MercadoPagoRateLimitError` — 429 from MP
|
|
299
|
+
|
|
300
|
+
## Compatibility
|
|
301
|
+
|
|
302
|
+
- Node.js 20+
|
|
303
|
+
- Vercel AI SDK 6+
|
|
304
|
+
- Zod 3+
|
|
305
|
+
- Pairs cleanly with [Vercel AI Gateway](https://vercel.com/ai-gateway) for model routing.
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
MIT — see [LICENSE](./LICENSE).
|