@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.
@@ -0,0 +1,403 @@
1
+ import { z } from 'zod';
2
+ import { ToolSet } from 'ai';
3
+
4
+ /**
5
+ * Base class for any error originating from the Mercado Pago integration. All
6
+ * specific error types extend this. Carries the MP HTTP status, the parsed
7
+ * body when available, and the endpoint that failed for debugging.
8
+ */
9
+ declare class MercadoPagoError extends Error {
10
+ status: number;
11
+ endpoint: string;
12
+ mpResponse?: unknown | undefined;
13
+ constructor(message: string, status: number, endpoint: string, mpResponse?: unknown | undefined);
14
+ }
15
+ /**
16
+ * Thrown when the access token is missing, expired, or rejected by MP.
17
+ */
18
+ declare class MercadoPagoAuthError extends MercadoPagoError {
19
+ constructor(endpoint: string, body?: unknown);
20
+ }
21
+ /**
22
+ * Thrown when MP returns the "back_url is not a valid URL" rejection. Common
23
+ * when devs pass localhost or http:// — MP requires HTTPS only, even in sandbox.
24
+ */
25
+ declare class MercadoPagoBackUrlInvalidError extends MercadoPagoError {
26
+ constructor(endpoint: string, body?: unknown);
27
+ }
28
+ /**
29
+ * Thrown when the buyer email matches the seller account's email. MP refuses
30
+ * self-payment on subscriptions: the Confirmar button at the init_point UI
31
+ * stays disabled with no surfaceable error message.
32
+ */
33
+ declare class MercadoPagoSelfPaymentError extends MercadoPagoError {
34
+ constructor(endpoint: string, body?: unknown);
35
+ }
36
+ /**
37
+ * Thrown when MP returns "Cannot operate between different countries". Despite
38
+ * the error text, this generally signals an account-type mismatch (real
39
+ * account-in-test-mode vs. test user account), not a literal country mismatch.
40
+ */
41
+ declare class MercadoPagoAccountTypeMismatchError extends MercadoPagoError {
42
+ constructor(endpoint: string, body?: unknown);
43
+ }
44
+ /**
45
+ * Thrown when MP's risk engine rejects the first payment of a subscription.
46
+ * IMPORTANT: when this happens, MP automatically cancels the entire preapproval
47
+ * — you cannot retry on the same subscription, you must create a fresh one.
48
+ */
49
+ declare class MercadoPagoPaymentRejectedError extends MercadoPagoError {
50
+ preapprovalId: string;
51
+ statusDetail: string | null;
52
+ constructor(preapprovalId: string, statusDetail: string | null, body?: unknown);
53
+ }
54
+ /**
55
+ * Thrown when an attempt is made to authorize a preapproval via API. Only the
56
+ * payer can authorize via the init_point UI; there is no admin override even
57
+ * in sandbox.
58
+ */
59
+ declare class MercadoPagoAuthorizeForbiddenError extends MercadoPagoError {
60
+ constructor(preapprovalId: string, body?: unknown);
61
+ }
62
+ /**
63
+ * Thrown when MP rate-limits the integration. Retry with exponential backoff.
64
+ */
65
+ declare class MercadoPagoRateLimitError extends MercadoPagoError {
66
+ retryAfterSeconds: number | null;
67
+ constructor(endpoint: string, retryAfterSeconds: number | null, body?: unknown);
68
+ }
69
+ /**
70
+ * Maps an MP error response body to the most specific known error class. Falls
71
+ * back to the generic MercadoPagoError when no specific pattern matches.
72
+ */
73
+ declare function classifyError(status: number, endpoint: string, body: unknown, context?: {
74
+ preapprovalId?: string;
75
+ payerEmail?: string;
76
+ sellerEmail?: string;
77
+ }): MercadoPagoError;
78
+
79
+ /**
80
+ * Site IDs supported by Mercado Pago. The lib targets MLA (Argentina) primarily;
81
+ * other LATAM sites may work for the read paths but the full Subscriptions flow
82
+ * is only verified against MLA.
83
+ */
84
+ declare const SiteIdSchema: z.ZodEnum<{
85
+ MLA: "MLA";
86
+ MLB: "MLB";
87
+ MLM: "MLM";
88
+ MCO: "MCO";
89
+ MLC: "MLC";
90
+ MLU: "MLU";
91
+ }>;
92
+ type SiteId = z.infer<typeof SiteIdSchema>;
93
+ /**
94
+ * Currency identifiers MP exposes. ARS is the supported case for v0.1.
95
+ */
96
+ declare const CurrencyIdSchema: z.ZodEnum<{
97
+ ARS: "ARS";
98
+ USD: "USD";
99
+ BRL: "BRL";
100
+ MXN: "MXN";
101
+ }>;
102
+ type CurrencyId = z.infer<typeof CurrencyIdSchema>;
103
+ /**
104
+ * Recurrence frequency unit for a subscription's auto_recurring config.
105
+ */
106
+ declare const FrequencyTypeSchema: z.ZodEnum<{
107
+ months: "months";
108
+ days: "days";
109
+ }>;
110
+ type FrequencyType = z.infer<typeof FrequencyTypeSchema>;
111
+ /**
112
+ * Lifecycle states a Mercado Pago preapproval can be in. The string is the
113
+ * canonical MP value; we widen to `string` for forward compatibility because
114
+ * MP has historically introduced new states without notice.
115
+ */
116
+ declare const PreapprovalStatusSchema: z.ZodUnion<readonly [z.ZodLiteral<"pending">, z.ZodLiteral<"authorized">, z.ZodLiteral<"paused">, z.ZodLiteral<"cancelled">, z.ZodString]>;
117
+ type PreapprovalStatus = z.infer<typeof PreapprovalStatusSchema>;
118
+ declare const AutoRecurringSchema: z.ZodObject<{
119
+ frequency: z.ZodNumber;
120
+ frequency_type: z.ZodEnum<{
121
+ months: "months";
122
+ days: "days";
123
+ }>;
124
+ transaction_amount: z.ZodNumber;
125
+ currency_id: z.ZodEnum<{
126
+ ARS: "ARS";
127
+ USD: "USD";
128
+ BRL: "BRL";
129
+ MXN: "MXN";
130
+ }>;
131
+ start_date: z.ZodOptional<z.ZodString>;
132
+ end_date: z.ZodOptional<z.ZodString>;
133
+ }, z.core.$strip>;
134
+ type AutoRecurring = z.infer<typeof AutoRecurringSchema>;
135
+ declare const PreapprovalSchema: z.ZodObject<{
136
+ id: z.ZodString;
137
+ status: z.ZodUnion<readonly [z.ZodLiteral<"pending">, z.ZodLiteral<"authorized">, z.ZodLiteral<"paused">, z.ZodLiteral<"cancelled">, z.ZodString]>;
138
+ payer_email: z.ZodString;
139
+ init_point: z.ZodString;
140
+ external_reference: z.ZodOptional<z.ZodString>;
141
+ date_created: z.ZodString;
142
+ last_modified: z.ZodString;
143
+ next_payment_date: z.ZodOptional<z.ZodString>;
144
+ payer_id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
145
+ auto_recurring: z.ZodObject<{
146
+ frequency: z.ZodNumber;
147
+ frequency_type: z.ZodEnum<{
148
+ months: "months";
149
+ days: "days";
150
+ }>;
151
+ transaction_amount: z.ZodNumber;
152
+ currency_id: z.ZodEnum<{
153
+ ARS: "ARS";
154
+ USD: "USD";
155
+ BRL: "BRL";
156
+ MXN: "MXN";
157
+ }>;
158
+ start_date: z.ZodOptional<z.ZodString>;
159
+ end_date: z.ZodOptional<z.ZodString>;
160
+ }, z.core.$strip>;
161
+ }, z.core.$strip>;
162
+ type Preapproval = z.infer<typeof PreapprovalSchema>;
163
+ /**
164
+ * Input for creating a preapproval (subscription). Internal field names match
165
+ * MP API semantics; the public client method maps from camelCase Naza-friendly
166
+ * params to the snake_case payload MP expects.
167
+ */
168
+ interface CreatePreapprovalParams {
169
+ /** Short customer-facing description shown at checkout. */
170
+ reason: string;
171
+ /** Email of the buyer. Cannot equal the seller account's email (MP rejects). */
172
+ payerEmail: string;
173
+ /** Recurring amount per cycle. */
174
+ amount: number;
175
+ /** ARS for Argentina. Other currencies depend on the seller account's site. */
176
+ currency: CurrencyId;
177
+ /** Recurrence frequency (e.g., 1 + months = monthly). */
178
+ frequency: number;
179
+ frequencyType: FrequencyType;
180
+ /** HTTPS URL where MP redirects the buyer after first payment. localhost rejected. */
181
+ backUrl: string;
182
+ /** Optional client-side identifier for the subscription. */
183
+ externalReference?: string;
184
+ }
185
+ /**
186
+ * The shape of an MP webhook notification body for `topic=preapproval`. MP's
187
+ * webhook payload varies by event type; this is the union of fields seen in
188
+ * production.
189
+ */
190
+ declare const WebhookBodySchema: z.ZodObject<{
191
+ type: z.ZodOptional<z.ZodString>;
192
+ topic: z.ZodOptional<z.ZodString>;
193
+ action: z.ZodOptional<z.ZodString>;
194
+ data: z.ZodOptional<z.ZodObject<{
195
+ id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
196
+ }, z.core.$strip>>;
197
+ resource: z.ZodOptional<z.ZodString>;
198
+ user_id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
199
+ api_version: z.ZodOptional<z.ZodString>;
200
+ date_created: z.ZodOptional<z.ZodString>;
201
+ id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
202
+ live_mode: z.ZodOptional<z.ZodBoolean>;
203
+ }, z.core.$loose>;
204
+ type WebhookBody = z.infer<typeof WebhookBodySchema>;
205
+ /**
206
+ * Normalized webhook event after parsing. The library extracts topic + dataId
207
+ * from either query params or body, since MP sends them in either location
208
+ * depending on integration version.
209
+ */
210
+ interface ParsedWebhookEvent {
211
+ /** Topic of the event, e.g., "preapproval", "payment", "subscription_authorized_payment". */
212
+ topic: string;
213
+ /** ID of the affected resource. */
214
+ dataId: string;
215
+ /** Action descriptor when present (e.g., "updated", "created"). */
216
+ action: string | null;
217
+ /** Raw body MP sent, for caller inspection / debugging. */
218
+ raw: WebhookBody;
219
+ }
220
+
221
+ interface MercadoPagoClientOptions {
222
+ /** Access token. TEST- prefix for sandbox, APP_USR- for production. */
223
+ accessToken: string;
224
+ /**
225
+ * Override the API base URL. Mostly useful for tests against MSW or for
226
+ * pointing at a regional MP host. Defaults to https://api.mercadopago.com.
227
+ */
228
+ baseUrl?: string;
229
+ /**
230
+ * Custom fetch implementation. Defaults to globalThis.fetch. Override to
231
+ * inject your own retry/instrumentation layer or to test with MSW.
232
+ */
233
+ fetch?: typeof fetch;
234
+ }
235
+ /**
236
+ * Thin, typed wrapper around Mercado Pago's REST API. Only the endpoints the
237
+ * agent layer needs are exposed; this is deliberately narrow rather than a
238
+ * full SDK rebuild.
239
+ */
240
+ declare class MercadoPagoClient {
241
+ private readonly accessToken;
242
+ private readonly baseUrl;
243
+ private readonly fetchImpl;
244
+ constructor(options: MercadoPagoClientOptions);
245
+ private request;
246
+ /**
247
+ * Create a recurring subscription (preapproval). The returned `init_point`
248
+ * URL is where the buyer must complete the FIRST payment with their card +
249
+ * CVV — there is no API path that bypasses this human step.
250
+ */
251
+ createPreapproval(params: CreatePreapprovalParams): Promise<Preapproval>;
252
+ /**
253
+ * Fetch the current state of a preapproval. Useful to confirm whether the
254
+ * buyer has completed the first payment (`status: 'authorized'`) or whether
255
+ * the subscription was cancelled.
256
+ */
257
+ getPreapproval(id: string): Promise<Preapproval>;
258
+ /**
259
+ * Cancel an active preapproval. Irreversible: MP will not charge the buyer
260
+ * again and the subscription cannot be reactivated.
261
+ */
262
+ cancelPreapproval(id: string): Promise<Preapproval>;
263
+ /**
264
+ * Pause an authorized preapproval. The subscription stops auto-charging but
265
+ * can be re-activated. Note: MP only allows pausing subs that are currently
266
+ * `authorized` — pending/cancelled subs reject this.
267
+ */
268
+ pausePreapproval(id: string): Promise<Preapproval>;
269
+ /**
270
+ * Re-activate a paused preapproval. Charges resume on the next scheduled
271
+ * date.
272
+ */
273
+ resumePreapproval(id: string): Promise<Preapproval>;
274
+ }
275
+
276
+ /**
277
+ * In-memory record of a subscription. The lib persists the MP-side fields
278
+ * needed to reason about a subscription without hitting the API every time
279
+ * (status, last webhook info, customer email, etc.) plus a free-form metadata
280
+ * bag for callers to attach business context (tenant id, plan name, etc.).
281
+ */
282
+ interface SubscriptionStateRecord {
283
+ status?: string;
284
+ payerEmail?: string;
285
+ amount?: number;
286
+ currency?: string;
287
+ frequency?: number;
288
+ frequencyType?: string;
289
+ initPoint?: string;
290
+ externalReference?: string;
291
+ createdAt?: string;
292
+ cancelledAt?: string;
293
+ lastWebhookStatus?: string;
294
+ lastWebhookAt?: string;
295
+ metadata?: Record<string, unknown>;
296
+ }
297
+ /**
298
+ * Persistence surface for subscription state. Implementations may back this
299
+ * with Upstash Redis, Vercel KV, Postgres, in-memory, or anything that
300
+ * supports the three operations. The default `InMemoryStateAdapter` is
301
+ * provided for tests and trivial single-process deployments; production
302
+ * setups should plug in a durable store.
303
+ */
304
+ interface SubscriptionStateAdapter {
305
+ set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
306
+ get(id: string): Promise<SubscriptionStateRecord | null>;
307
+ list?(): Promise<string[]>;
308
+ }
309
+ /**
310
+ * Volatile, single-process state adapter. Useful for tests and demos. Do not
311
+ * use in production: state is lost on restart and is not safe across tenants.
312
+ */
313
+ declare class InMemoryStateAdapter implements SubscriptionStateAdapter {
314
+ private readonly store;
315
+ set(id: string, state: Partial<SubscriptionStateRecord>): Promise<void>;
316
+ get(id: string): Promise<SubscriptionStateRecord | null>;
317
+ list(): Promise<string[]>;
318
+ /** Test helper: drop everything. Not part of the adapter interface. */
319
+ reset(): void;
320
+ }
321
+
322
+ interface MercadoPagoToolsOptions {
323
+ /** State adapter for persisting subscription records. */
324
+ state: SubscriptionStateAdapter;
325
+ /**
326
+ * Default back_url used when callers don't supply one. MUST be HTTPS — MP
327
+ * rejects http:// and localhost back URLs even in sandbox.
328
+ */
329
+ backUrl: string;
330
+ /**
331
+ * Optionally override the agent-facing tool descriptions. Pass an object
332
+ * with keys matching tool names; values replace the default description.
333
+ * Useful for localizing the agent's tool reasoning.
334
+ */
335
+ descriptions?: Partial<Record<ToolName, string>>;
336
+ }
337
+ type ToolName = "create_subscription" | "get_subscription_status" | "cancel_subscription" | "pause_subscription" | "resume_subscription";
338
+ /**
339
+ * Build a tool set for the Vercel AI SDK that exposes Mercado Pago Subscriptions
340
+ * to an agent. Pass directly to `Experimental_Agent`'s `tools` option, or merge
341
+ * with other tool sets.
342
+ *
343
+ * @example
344
+ * ```ts
345
+ * import { Experimental_Agent as Agent, stepCountIs } from 'ai';
346
+ * import { MercadoPagoClient, mercadoPagoTools, InMemoryStateAdapter } from '@ar-agents/mercadopago';
347
+ *
348
+ * const mp = new MercadoPagoClient({ accessToken: process.env.MP_ACCESS_TOKEN! });
349
+ * const agent = new Agent({
350
+ * model: 'anthropic/claude-sonnet-4-6',
351
+ * tools: mercadoPagoTools(mp, {
352
+ * state: new InMemoryStateAdapter(),
353
+ * backUrl: 'https://mysite.com/done',
354
+ * }),
355
+ * stopWhen: stepCountIs(8),
356
+ * });
357
+ * ```
358
+ */
359
+ declare function mercadoPagoTools(client: MercadoPagoClient, options: MercadoPagoToolsOptions): ToolSet;
360
+
361
+ /**
362
+ * Parse a Mercado Pago webhook from the raw request body and URL search params.
363
+ * MP sends the topic and resource id in EITHER the URL query string OR the
364
+ * body, depending on integration version — this normalizes both shapes into a
365
+ * single structure.
366
+ *
367
+ * @example
368
+ * ```ts
369
+ * export async function POST(req: Request) {
370
+ * const body = await req.json().catch(() => ({}));
371
+ * const event = parseWebhookEvent(body, new URL(req.url).searchParams);
372
+ * if (event && event.topic === 'preapproval') {
373
+ * // refresh status from MP, update your store
374
+ * }
375
+ * return Response.json({ received: true });
376
+ * }
377
+ * ```
378
+ */
379
+ declare function parseWebhookEvent(body: unknown, searchParams?: URLSearchParams): ParsedWebhookEvent | null;
380
+ /**
381
+ * Verify the HMAC-SHA256 signature MP sends in the `x-signature` header for
382
+ * webhook authenticity. Returns true if the signature matches the expected
383
+ * value derived from the integration's secret key.
384
+ *
385
+ * @param requestId The value of the `x-request-id` request header.
386
+ * @param dataId The id of the resource the webhook is about (from query or body).
387
+ * @param signatureHeader The full `x-signature` header value MP sent.
388
+ * @param secret Your integration's webhook secret (configured in MP dev panel).
389
+ *
390
+ * @remarks
391
+ * MP's `x-signature` header has the form: `ts=NNNNNNNN,v1=HEXSIGNATURE`. We
392
+ * extract the timestamp and the v1 signature, then compute
393
+ * `HMAC-SHA256(secret, "id:${dataId};request-id:${requestId};ts:${ts};")`
394
+ * and compare with constant-time equality.
395
+ */
396
+ declare function verifyWebhookSignature(params: {
397
+ requestId: string | null;
398
+ dataId: string;
399
+ signatureHeader: string | null;
400
+ secret: string;
401
+ }): boolean;
402
+
403
+ export { type AutoRecurring, type CreatePreapprovalParams, type CurrencyId, type FrequencyType, InMemoryStateAdapter, MercadoPagoAccountTypeMismatchError, MercadoPagoAuthError, MercadoPagoAuthorizeForbiddenError, MercadoPagoBackUrlInvalidError, MercadoPagoClient, type MercadoPagoClientOptions, MercadoPagoError, MercadoPagoPaymentRejectedError, MercadoPagoRateLimitError, MercadoPagoSelfPaymentError, type MercadoPagoToolsOptions, type ParsedWebhookEvent, type Preapproval, type PreapprovalStatus, type SiteId, type SubscriptionStateAdapter, type SubscriptionStateRecord, type WebhookBody, classifyError, mercadoPagoTools, parseWebhookEvent, verifyWebhookSignature };