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