@pagosrd/mcp 0.1.0 → 0.1.1
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/CHANGELOG.md +10 -0
- package/dist/index.js +4 -212
- package/dist/index.js.map +1 -1
- package/package.json +30 -10
package/CHANGELOG.md
ADDED
package/dist/index.js
CHANGED
|
@@ -2,219 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
|
|
6
|
-
// ../node/dist/index.js
|
|
7
|
-
import { randomUUID } from "crypto";
|
|
8
|
-
var DEFAULT_BASE_URL = "https://api.pagosrd.com";
|
|
9
|
-
var PagosRDError = class extends Error {
|
|
10
|
-
constructor(message, status, code, field, requestId, raw) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.status = status;
|
|
13
|
-
this.code = code;
|
|
14
|
-
this.field = field;
|
|
15
|
-
this.requestId = requestId;
|
|
16
|
-
this.raw = raw;
|
|
17
|
-
}
|
|
18
|
-
status;
|
|
19
|
-
code;
|
|
20
|
-
field;
|
|
21
|
-
requestId;
|
|
22
|
-
raw;
|
|
23
|
-
name = "PagosRDError";
|
|
24
|
-
};
|
|
25
|
-
var PagosRDConnectionError = class extends Error {
|
|
26
|
-
name = "PagosRDConnectionError";
|
|
27
|
-
};
|
|
28
|
-
var PagosRD = class {
|
|
29
|
-
constructor(apiKey2, options = {}) {
|
|
30
|
-
this.apiKey = apiKey2;
|
|
31
|
-
if (!apiKey2 || !/^sk_(test|live)_/.test(apiKey2)) {
|
|
32
|
-
throw new Error("Llave de API inv\xE1lida: debe empezar con sk_test_ o sk_live_.");
|
|
33
|
-
}
|
|
34
|
-
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
35
|
-
this.maxRetries = options.maxRetries ?? 2;
|
|
36
|
-
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
37
|
-
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
38
|
-
}
|
|
39
|
-
apiKey;
|
|
40
|
-
baseUrl;
|
|
41
|
-
maxRetries;
|
|
42
|
-
timeoutMs;
|
|
43
|
-
fetchImpl;
|
|
44
|
-
/** true si la llave es de modo de prueba. */
|
|
45
|
-
get testMode() {
|
|
46
|
-
return this.apiKey.startsWith("sk_test_");
|
|
47
|
-
}
|
|
48
|
-
// ---------------------------------------------------------------- recursos
|
|
49
|
-
paymentLinks = {
|
|
50
|
-
create: (params, opts) => this.post("/v1/payment_links", params, opts),
|
|
51
|
-
retrieve: (id) => this.get(`/v1/payment_links/${enc(id)}`),
|
|
52
|
-
list: (params = {}) => this.get("/v1/payment_links", params),
|
|
53
|
-
cancel: (id) => this.post(`/v1/payment_links/${enc(id)}/cancel`, void 0)
|
|
54
|
-
};
|
|
55
|
-
payments = {
|
|
56
|
-
/**
|
|
57
|
-
* Cobra con una tarjeta guardada, sin que el cliente vea ninguna página. La llave de
|
|
58
|
-
* idempotencia es obligatoria: usa algo propio de la operación (el id del pedido) para
|
|
59
|
-
* que reintentar nunca cobre dos veces. El pago vuelve succeeded, failed o processing.
|
|
60
|
-
*/
|
|
61
|
-
create: (params, opts) => this.post("/v1/payments", params, opts),
|
|
62
|
-
retrieve: (id) => this.get(`/v1/payments/${enc(id)}`),
|
|
63
|
-
list: (params = {}) => this.get("/v1/payments", params)
|
|
64
|
-
};
|
|
65
|
-
paymentMethods = {
|
|
66
|
-
retrieve: (id) => this.get(`/v1/payment_methods/${enc(id)}`),
|
|
67
|
-
list: (params = {}) => this.get("/v1/payment_methods", params),
|
|
68
|
-
/** Deja de poder usarse de inmediato y se borra el token en el procesador. */
|
|
69
|
-
del: (id) => this.request("DELETE", `/v1/payment_methods/${enc(id)}`, void 0, randomUUID())
|
|
70
|
-
};
|
|
71
|
-
subscriptions = {
|
|
72
|
-
/**
|
|
73
|
-
* PagosRD cobra solo en cada fecha con la tarjeta guardada; reintenta los rechazos a 1, 3 y 5 días.
|
|
74
|
-
* La llave de idempotencia es obligatoria: usa el id del contrato para que reintentar no cree otra.
|
|
75
|
-
*/
|
|
76
|
-
create: (params, opts) => this.post("/v1/subscriptions", params, opts),
|
|
77
|
-
retrieve: (id) => this.get(`/v1/subscriptions/${enc(id)}`),
|
|
78
|
-
list: (params = {}) => this.get("/v1/subscriptions", params),
|
|
79
|
-
update: (id, params) => this.request("PATCH", `/v1/subscriptions/${enc(id)}`, params),
|
|
80
|
-
/** Reintenta ya el cobro de una suscripción past_due o unpaid con la misma tarjeta. */
|
|
81
|
-
retry: (id) => this.post(`/v1/subscriptions/${enc(id)}/retry`, void 0),
|
|
82
|
-
cancel: (id) => this.post(`/v1/subscriptions/${enc(id)}/cancel`, void 0)
|
|
83
|
-
};
|
|
84
|
-
refunds = {
|
|
85
|
-
create: (paymentId, params = {}, opts) => this.post(`/v1/payments/${enc(paymentId)}/refunds`, params, opts),
|
|
86
|
-
list: (params = {}) => this.get("/v1/refunds", params)
|
|
87
|
-
};
|
|
88
|
-
merchants = {
|
|
89
|
-
create: (params) => this.post("/v1/merchants", params),
|
|
90
|
-
retrieve: (id) => this.get(`/v1/merchants/${enc(id)}`),
|
|
91
|
-
list: (params = {}) => this.get("/v1/merchants", params),
|
|
92
|
-
update: (id, params) => this.request("PATCH", `/v1/merchants/${enc(id)}`, params),
|
|
93
|
-
setProcessorConfig: (id, config) => this.request("PUT", `/v1/merchants/${enc(id)}/processor`, config),
|
|
94
|
-
balance: (id) => this.get(`/v1/merchants/${enc(id)}/balance`)
|
|
95
|
-
};
|
|
96
|
-
webhookEndpoints = {
|
|
97
|
-
create: (params) => this.post("/v1/webhook_endpoints", params),
|
|
98
|
-
list: () => this.get("/v1/webhook_endpoints"),
|
|
99
|
-
retrieve: (id) => this.get(`/v1/webhook_endpoints/${enc(id)}`),
|
|
100
|
-
update: (id, params) => this.request("PATCH", `/v1/webhook_endpoints/${enc(id)}`, params),
|
|
101
|
-
del: (id) => this.request("DELETE", `/v1/webhook_endpoints/${enc(id)}`),
|
|
102
|
-
rollSecret: (id) => this.post(`/v1/webhook_endpoints/${enc(id)}/roll_secret`, void 0),
|
|
103
|
-
deliveries: (params = {}) => this.get("/v1/webhook_deliveries", params)
|
|
104
|
-
};
|
|
105
|
-
events = {
|
|
106
|
-
retrieve: (id) => this.get(`/v1/events/${enc(id)}`),
|
|
107
|
-
list: (params = {}) => this.get("/v1/events", params),
|
|
108
|
-
resend: (id, endpointId) => this.post(`/v1/events/${enc(id)}/resend`, endpointId ? { endpoint_id: endpointId } : {})
|
|
109
|
-
};
|
|
110
|
-
summary(params = {}) {
|
|
111
|
-
return this.get("/v1/summary", params);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Recorre todas las páginas de una lista.
|
|
115
|
-
*
|
|
116
|
-
* for await (const pago of client.autoPaginate((p) => client.payments.list({ ...p, status: "succeeded" }))) { … }
|
|
117
|
-
*/
|
|
118
|
-
async *autoPaginate(fetchPage, pageSize = 100) {
|
|
119
|
-
let startingAfter;
|
|
120
|
-
while (true) {
|
|
121
|
-
const page = await fetchPage({ limit: pageSize, starting_after: startingAfter });
|
|
122
|
-
for (const item of page.data) yield item;
|
|
123
|
-
if (!page.has_more || page.data.length === 0) return;
|
|
124
|
-
startingAfter = page.data[page.data.length - 1].id;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// ---------------------------------------------------------------- HTTP
|
|
128
|
-
get(path, query) {
|
|
129
|
-
return this.request("GET", path, void 0, void 0, query);
|
|
130
|
-
}
|
|
131
|
-
post(path, body, opts) {
|
|
132
|
-
return this.request("POST", path, body, opts?.idempotencyKey ?? randomUUID());
|
|
133
|
-
}
|
|
134
|
-
async request(method, path, body, idempotencyKey, query) {
|
|
135
|
-
const url = new URL(this.baseUrl + path);
|
|
136
|
-
for (const [k, v] of Object.entries(query ?? {})) if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
137
|
-
const headers = {
|
|
138
|
-
authorization: `Bearer ${this.apiKey}`,
|
|
139
|
-
"user-agent": "pagosrd-node/0.1.0",
|
|
140
|
-
accept: "application/json"
|
|
141
|
-
};
|
|
142
|
-
if (body !== void 0) headers["content-type"] = "application/json";
|
|
143
|
-
if (idempotencyKey) headers["idempotency-key"] = idempotencyKey;
|
|
144
|
-
const retryable = method === "GET" || Boolean(idempotencyKey);
|
|
145
|
-
for (let attempt = 0; ; attempt++) {
|
|
146
|
-
let res;
|
|
147
|
-
try {
|
|
148
|
-
res = await this.fetchImpl(url, {
|
|
149
|
-
method,
|
|
150
|
-
headers,
|
|
151
|
-
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
152
|
-
signal: AbortSignal.timeout(this.timeoutMs)
|
|
153
|
-
});
|
|
154
|
-
} catch (err) {
|
|
155
|
-
if (retryable && attempt < this.maxRetries) {
|
|
156
|
-
await sleep(backoff(attempt));
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
throw new PagosRDConnectionError(`No se pudo conectar con PagosRD: ${err.message}`);
|
|
160
|
-
}
|
|
161
|
-
if ((res.status === 429 || res.status >= 500) && retryable && attempt < this.maxRetries) {
|
|
162
|
-
const retryAfter = Number(res.headers.get("retry-after"));
|
|
163
|
-
await sleep(Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1e3 : backoff(attempt));
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
const text = await res.text();
|
|
167
|
-
const data = text ? safeJson(text) : null;
|
|
168
|
-
if (!res.ok) {
|
|
169
|
-
const p = data ?? {};
|
|
170
|
-
throw new PagosRDError(p.detail ?? `Error HTTP ${res.status}`, res.status, p.code ?? "error_http", p.field, res.headers.get("x-request-id") ?? void 0, data);
|
|
171
|
-
}
|
|
172
|
-
return data;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
var enc = encodeURIComponent;
|
|
177
|
-
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
178
|
-
var backoff = (attempt) => Math.min(8e3, 500 * 2 ** attempt) * (0.5 + Math.random() / 2);
|
|
179
|
-
function safeJson(text) {
|
|
180
|
-
try {
|
|
181
|
-
return JSON.parse(text);
|
|
182
|
-
} catch {
|
|
183
|
-
return { detail: text };
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
var MoneyError = class extends Error {
|
|
187
|
-
name = "MoneyError";
|
|
188
|
-
};
|
|
189
|
-
function cents(value, field = "amount") {
|
|
190
|
-
if (!Number.isSafeInteger(value)) throw new MoneyError(`${field} debe ser un entero de centavos.`);
|
|
191
|
-
if (value < 0) throw new MoneyError(`${field} no puede ser negativo.`);
|
|
192
|
-
return value;
|
|
193
|
-
}
|
|
194
|
-
function toCents(amount) {
|
|
195
|
-
const text = typeof amount === "number" ? amount.toString() : amount;
|
|
196
|
-
const noSymbol = text.trim().replace(/^(RD\$|US\$|\$)\s*/i, "");
|
|
197
|
-
if (noSymbol.includes(",") && !/^\d{1,3}(,\d{3})+(\.\d{1,2})?$/.test(noSymbol)) {
|
|
198
|
-
throw new MoneyError(`Monto ambiguo: "${text}". Usa punto para los decimales y coma solo para los miles, por ejemplo "1,500.50".`);
|
|
199
|
-
}
|
|
200
|
-
const cleaned = noSymbol.replace(/,/g, "");
|
|
201
|
-
const match = /^(\d+)(?:\.(\d{1,2}))?$/.exec(cleaned);
|
|
202
|
-
if (!match) throw new MoneyError(`Monto inv\xE1lido: "${text}". Usa como m\xE1ximo 2 decimales.`);
|
|
203
|
-
const value = Number(match[1]) * 100 + Number((match[2] ?? "").padEnd(2, "0"));
|
|
204
|
-
return cents(value);
|
|
205
|
-
}
|
|
206
|
-
var formatters = /* @__PURE__ */ new Map();
|
|
207
|
-
function formatMoney(value, currency = "DOP") {
|
|
208
|
-
let f = formatters.get(currency);
|
|
209
|
-
if (!f) {
|
|
210
|
-
f = new Intl.NumberFormat("es-DO", { style: "currency", currency, minimumFractionDigits: 2 });
|
|
211
|
-
formatters.set(currency, f);
|
|
212
|
-
}
|
|
213
|
-
return f.format(value / 100);
|
|
214
|
-
}
|
|
5
|
+
import { PagosRD as PagosRD2 } from "@pagosrd/node";
|
|
215
6
|
|
|
216
7
|
// src/server.ts
|
|
217
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { formatMoney, PagosRDError, toCents } from "@pagosrd/node";
|
|
218
10
|
import { z } from "zod";
|
|
219
11
|
var pesos = z.string().describe('Monto en pesos dominicanos, por ejemplo "4,500.00" o "1500".');
|
|
220
12
|
function linkText(l) {
|
|
@@ -248,7 +40,7 @@ async function run(fn) {
|
|
|
248
40
|
}
|
|
249
41
|
}
|
|
250
42
|
function createServer(client2) {
|
|
251
|
-
const server2 = new McpServer({ name: "pagosrd", version: "0.1.
|
|
43
|
+
const server2 = new McpServer({ name: "pagosrd", version: "0.1.1" });
|
|
252
44
|
server2.registerTool(
|
|
253
45
|
"crear_link_de_cobro",
|
|
254
46
|
{
|
|
@@ -394,7 +186,7 @@ if (!apiKey) {
|
|
|
394
186
|
console.error("Falta PAGOSRD_API_KEY (tu llave sk_test_\u2026 o sk_live_\u2026).");
|
|
395
187
|
process.exit(1);
|
|
396
188
|
}
|
|
397
|
-
var client = new
|
|
189
|
+
var client = new PagosRD2(apiKey, { baseUrl: process.env.PAGOSRD_API_URL });
|
|
398
190
|
var server = createServer(client);
|
|
399
191
|
await server.connect(new StdioServerTransport());
|
|
400
192
|
console.error(`PagosRD MCP listo (${client.testMode ? "modo de prueba" : "modo live"}).`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../node/src/client.ts","../../core/src/money.ts","../../core/src/crypto.ts","../../core/src/webhooks.ts","../../node/src/webhooks.ts","../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { PagosRD } from \"@pagosrd/node\";\nimport { createServer } from \"./server.js\";\n\nconst apiKey = process.env.PAGOSRD_API_KEY;\nif (!apiKey) {\n console.error(\"Falta PAGOSRD_API_KEY (tu llave sk_test_… o sk_live_…).\");\n process.exit(1);\n}\nconst client = new PagosRD(apiKey, { baseUrl: process.env.PAGOSRD_API_URL });\nconst server = createServer(client);\nawait server.connect(new StdioServerTransport());\n// stdout es del protocolo MCP: los mensajes para humanos van a stderr.\nconsole.error(`PagosRD MCP listo (${client.testMode ? \"modo de prueba\" : \"modo live\"}).`);\n","import { randomUUID } from \"node:crypto\";\nimport type {\n AzulConfig,\n CardnetConfig,\n CreateMerchantParams,\n CustomerInfoParams,\n CreatePaymentLinkParams,\n CreatePaymentParams,\n CreateRefundParams,\n CreateSubscriptionParams,\n Event,\n List,\n Merchant,\n PageParams,\n Payment,\n PaymentLink,\n PaymentMethod,\n PaymentStatus,\n Refund,\n RequestOptions,\n Subscription,\n SubscriptionStatus,\n Summary,\n UpdateSubscriptionParams,\n WebhookDelivery,\n WebhookEndpoint,\n} from \"./types.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.pagosrd.com\";\n\n/** Error devuelto por la API (Problem Details). */\nexport class PagosRDError extends Error {\n override readonly name = \"PagosRDError\";\n constructor(\n message: string,\n readonly status: number,\n readonly code: string,\n readonly field: string | undefined,\n readonly requestId: string | undefined,\n readonly raw: unknown,\n ) {\n super(message);\n }\n}\n\n/** No se pudo hablar con la API (red caída, timeout). */\nexport class PagosRDConnectionError extends Error {\n override readonly name = \"PagosRDConnectionError\";\n}\n\nexport interface PagosRDOptions {\n /** Por defecto https://api.pagosrd.com */\n baseUrl?: string;\n /** Reintentos ante fallas de red, 429 y 5xx. Por defecto 2. Los POST llevan llave de idempotencia, así que reintentar es seguro. */\n maxRetries?: number;\n /** Tiempo máximo por solicitud en ms. Por defecto 30000. */\n timeoutMs?: number;\n fetch?: typeof fetch;\n}\n\ntype Query = object;\n\nexport class PagosRD {\n private readonly baseUrl: string;\n private readonly maxRetries: number;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(\n private readonly apiKey: string,\n options: PagosRDOptions = {},\n ) {\n if (!apiKey || !/^sk_(test|live)_/.test(apiKey)) {\n throw new Error(\"Llave de API inválida: debe empezar con sk_test_ o sk_live_.\");\n }\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.maxRetries = options.maxRetries ?? 2;\n this.timeoutMs = options.timeoutMs ?? 30_000;\n this.fetchImpl = options.fetch ?? globalThis.fetch;\n }\n\n /** true si la llave es de modo de prueba. */\n get testMode(): boolean {\n return this.apiKey.startsWith(\"sk_test_\");\n }\n\n // ---------------------------------------------------------------- recursos\n\n readonly paymentLinks = {\n create: (params: CreatePaymentLinkParams, opts?: RequestOptions) => this.post<PaymentLink>(\"/v1/payment_links\", params, opts),\n retrieve: (id: string) => this.get<PaymentLink>(`/v1/payment_links/${enc(id)}`),\n list: (params: PageParams & { status?: PaymentLink[\"status\"]; merchant_id?: string; reference?: string } = {}) =>\n this.get<List<PaymentLink>>(\"/v1/payment_links\", params),\n cancel: (id: string) => this.post<PaymentLink>(`/v1/payment_links/${enc(id)}/cancel`, undefined),\n };\n\n readonly payments = {\n /**\n * Cobra con una tarjeta guardada, sin que el cliente vea ninguna página. La llave de\n * idempotencia es obligatoria: usa algo propio de la operación (el id del pedido) para\n * que reintentar nunca cobre dos veces. El pago vuelve succeeded, failed o processing.\n */\n create: (params: CreatePaymentParams, opts: RequestOptions & { idempotencyKey: string }) => this.post<Payment>(\"/v1/payments\", params, opts),\n retrieve: (id: string) => this.get<Payment>(`/v1/payments/${enc(id)}`),\n list: (\n params: PageParams & {\n status?: PaymentStatus;\n merchant_id?: string;\n payment_link_id?: string;\n payment_method_id?: string;\n subscription_id?: string;\n created_gte?: string;\n created_lte?: string;\n } = {},\n ) => this.get<List<Payment>>(\"/v1/payments\", params),\n };\n\n readonly paymentMethods = {\n retrieve: (id: string) => this.get<PaymentMethod>(`/v1/payment_methods/${enc(id)}`),\n list: (params: PageParams & { merchant_id?: string; customer_reference?: string; status?: PaymentMethod[\"status\"] } = {}) =>\n this.get<List<PaymentMethod>>(\"/v1/payment_methods\", params),\n /** Deja de poder usarse de inmediato y se borra el token en el procesador. */\n del: (id: string) => this.request<PaymentMethod>(\"DELETE\", `/v1/payment_methods/${enc(id)}`, undefined, randomUUID()),\n };\n\n readonly subscriptions = {\n /**\n * PagosRD cobra solo en cada fecha con la tarjeta guardada; reintenta los rechazos a 1, 3 y 5 días.\n * La llave de idempotencia es obligatoria: usa el id del contrato para que reintentar no cree otra.\n */\n create: (params: CreateSubscriptionParams, opts: RequestOptions & { idempotencyKey: string }) => this.post<Subscription>(\"/v1/subscriptions\", params, opts),\n retrieve: (id: string) => this.get<Subscription>(`/v1/subscriptions/${enc(id)}`),\n list: (params: PageParams & { status?: SubscriptionStatus; merchant_id?: string; customer_reference?: string; payment_method_id?: string } = {}) =>\n this.get<List<Subscription>>(\"/v1/subscriptions\", params),\n update: (id: string, params: UpdateSubscriptionParams) => this.request<Subscription>(\"PATCH\", `/v1/subscriptions/${enc(id)}`, params),\n /** Reintenta ya el cobro de una suscripción past_due o unpaid con la misma tarjeta. */\n retry: (id: string) => this.post<Subscription>(`/v1/subscriptions/${enc(id)}/retry`, undefined),\n cancel: (id: string) => this.post<Subscription>(`/v1/subscriptions/${enc(id)}/cancel`, undefined),\n };\n\n readonly refunds = {\n create: (paymentId: string, params: CreateRefundParams = {}, opts?: RequestOptions) => this.post<Refund>(`/v1/payments/${enc(paymentId)}/refunds`, params, opts),\n list: (params: PageParams & { payment_id?: string } = {}) => this.get<List<Refund>>(\"/v1/refunds\", params),\n };\n\n readonly merchants = {\n create: (params: CreateMerchantParams) => this.post<Merchant>(\"/v1/merchants\", params),\n retrieve: (id: string) => this.get<Merchant>(`/v1/merchants/${enc(id)}`),\n list: (params: PageParams = {}) => this.get<List<Merchant>>(\"/v1/merchants\", params),\n update: (id: string, params: { name?: string; rnc?: string | null; status?: \"active\" | \"disabled\"; metadata?: Record<string, string>; customer_info?: CustomerInfoParams }) =>\n this.request<Merchant>(\"PATCH\", `/v1/merchants/${enc(id)}`, params),\n setProcessorConfig: (id: string, config: AzulConfig | CardnetConfig) => this.request<Merchant>(\"PUT\", `/v1/merchants/${enc(id)}/processor`, config),\n balance: (id: string) => this.get<{ object: \"balance\"; merchant_id: string; processor_receivable: number }>(`/v1/merchants/${enc(id)}/balance`),\n };\n\n readonly webhookEndpoints = {\n create: (params: { url: string; events?: string[]; description?: string | null }) => this.post<WebhookEndpoint & { secret: string }>(\"/v1/webhook_endpoints\", params),\n list: () => this.get<List<WebhookEndpoint>>(\"/v1/webhook_endpoints\"),\n retrieve: (id: string) => this.get<WebhookEndpoint>(`/v1/webhook_endpoints/${enc(id)}`),\n update: (id: string, params: { url?: string; events?: string[]; description?: string | null; enabled?: boolean }) =>\n this.request<WebhookEndpoint>(\"PATCH\", `/v1/webhook_endpoints/${enc(id)}`, params),\n del: (id: string) => this.request<{ id: string; deleted: true }>(\"DELETE\", `/v1/webhook_endpoints/${enc(id)}`),\n rollSecret: (id: string) => this.post<{ secret: string }>(`/v1/webhook_endpoints/${enc(id)}/roll_secret`, undefined),\n deliveries: (params: PageParams & { endpoint_id?: string; event_id?: string; status?: WebhookDelivery[\"status\"] } = {}) =>\n this.get<List<WebhookDelivery>>(\"/v1/webhook_deliveries\", params),\n };\n\n readonly events = {\n retrieve: (id: string) => this.get<Event>(`/v1/events/${enc(id)}`),\n list: (params: PageParams & { type?: string } = {}) => this.get<List<Event>>(\"/v1/events\", params),\n resend: (id: string, endpointId?: string) => this.post<List<WebhookDelivery>>(`/v1/events/${enc(id)}/resend`, endpointId ? { endpoint_id: endpointId } : {}),\n };\n\n summary(params: { from?: string; to?: string } = {}) {\n return this.get<Summary>(\"/v1/summary\", params);\n }\n\n /**\n * Recorre todas las páginas de una lista.\n *\n * for await (const pago of client.autoPaginate((p) => client.payments.list({ ...p, status: \"succeeded\" }))) { … }\n */\n async *autoPaginate<T extends { id: string }>(fetchPage: (page: PageParams) => Promise<List<T>>, pageSize = 100): AsyncGenerator<T> {\n let startingAfter: string | undefined;\n while (true) {\n const page = await fetchPage({ limit: pageSize, starting_after: startingAfter });\n for (const item of page.data) yield item;\n if (!page.has_more || page.data.length === 0) return;\n startingAfter = page.data[page.data.length - 1]!.id;\n }\n }\n\n // ---------------------------------------------------------------- HTTP\n\n private get<T>(path: string, query?: Query) {\n return this.request<T>(\"GET\", path, undefined, undefined, query);\n }\n\n private post<T>(path: string, body: unknown, opts?: RequestOptions) {\n return this.request<T>(\"POST\", path, body, opts?.idempotencyKey ?? randomUUID());\n }\n\n private async request<T>(method: string, path: string, body?: unknown, idempotencyKey?: string, query?: Query): Promise<T> {\n const url = new URL(this.baseUrl + path);\n for (const [k, v] of Object.entries(query ?? {}) as [string, unknown][]) if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n const headers: Record<string, string> = {\n authorization: `Bearer ${this.apiKey}`,\n \"user-agent\": \"pagosrd-node/0.1.0\",\n accept: \"application/json\",\n };\n if (body !== undefined) headers[\"content-type\"] = \"application/json\";\n if (idempotencyKey) headers[\"idempotency-key\"] = idempotencyKey;\n // Solo se reintenta lo que es seguro: GET, o POST con llave de idempotencia.\n const retryable = method === \"GET\" || Boolean(idempotencyKey);\n\n for (let attempt = 0; ; attempt++) {\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (err) {\n if (retryable && attempt < this.maxRetries) {\n await sleep(backoff(attempt));\n continue;\n }\n throw new PagosRDConnectionError(`No se pudo conectar con PagosRD: ${(err as Error).message}`);\n }\n if ((res.status === 429 || res.status >= 500) && retryable && attempt < this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\"));\n await sleep(Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1000 : backoff(attempt));\n continue;\n }\n const text = await res.text();\n const data: unknown = text ? safeJson(text) : null;\n if (!res.ok) {\n const p = (data ?? {}) as { detail?: string; code?: string; field?: string };\n throw new PagosRDError(p.detail ?? `Error HTTP ${res.status}`, res.status, p.code ?? \"error_http\", p.field, res.headers.get(\"x-request-id\") ?? undefined, data);\n }\n return data as T;\n }\n }\n}\n\nconst enc = encodeURIComponent;\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\nconst backoff = (attempt: number) => Math.min(8_000, 500 * 2 ** attempt) * (0.5 + Math.random() / 2);\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return { detail: text };\n }\n}\n","/** Monto en centavos enteros. Solo se crea con `cents()`, que valida. */\nexport type Cents = number & { readonly __brand: \"Cents\" };\n\nexport const CURRENCIES = [\"DOP\", \"USD\"] as const;\nexport type Currency = (typeof CURRENCIES)[number];\n\nexport class MoneyError extends Error {\n override readonly name = \"MoneyError\";\n}\n\n/** Valida y marca un entero de centavos. */\nexport function cents(value: number, field = \"amount\"): Cents {\n if (!Number.isSafeInteger(value)) throw new MoneyError(`${field} debe ser un entero de centavos.`);\n if (value < 0) throw new MoneyError(`${field} no puede ser negativo.`);\n return value as Cents;\n}\n\n/** Convierte \"1,500.50\" o \"RD$1,500.50\" a 150050 sin pasar por punto flotante. */\nexport function toCents(amount: string | number): Cents {\n const text = typeof amount === \"number\" ? amount.toString() : amount;\n const noSymbol = text.trim().replace(/^(RD\\$|US\\$|\\$)\\s*/i, \"\");\n // La coma SOLO se acepta como separador de miles bien formado (\"1,500.50\").\n // \"1500,50\" o \"4,5\" se rechazan: podrían ser decimales y multiplicar el monto por 100.\n if (noSymbol.includes(\",\") && !/^\\d{1,3}(,\\d{3})+(\\.\\d{1,2})?$/.test(noSymbol)) {\n throw new MoneyError(`Monto ambiguo: \"${text}\". Usa punto para los decimales y coma solo para los miles, por ejemplo \"1,500.50\".`);\n }\n const cleaned = noSymbol.replace(/,/g, \"\");\n const match = /^(\\d+)(?:\\.(\\d{1,2}))?$/.exec(cleaned);\n if (!match) throw new MoneyError(`Monto inválido: \"${text}\". Usa como máximo 2 decimales.`);\n const value = Number(match[1]) * 100 + Number((match[2] ?? \"\").padEnd(2, \"0\"));\n return cents(value);\n}\n\nconst formatters = new Map<Currency, Intl.NumberFormat>();\n\n/** 150050 -> \"RD$1,500.50\" */\nexport function formatMoney(value: number, currency: Currency = \"DOP\"): string {\n let f = formatters.get(currency);\n if (!f) {\n f = new Intl.NumberFormat(\"es-DO\", { style: \"currency\", currency, minimumFractionDigits: 2 });\n formatters.set(currency, f);\n }\n return f.format(value / 100);\n}\n","import { createCipheriv, createDecipheriv, createHash, createHmac, hkdfSync, randomBytes, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Cifrado de secretos en reposo (credenciales de AZUL, secretos de webhooks).\n *\n * AES-256-GCM con una llave derivada (HKDF-SHA256) de la llave maestra y de un\n * \"contexto\" (por ejemplo el id del comercio). Así cada comercio tiene su propia\n * llave efectiva y un texto cifrado no se puede mover a otro registro.\n *\n * Formato: \"v1:<iv b64>:<tag b64>:<cifrado b64>\". La versión permite rotar la\n * llave maestra en el futuro.\n */\n\nexport class CryptoError extends Error {\n override readonly name = \"CryptoError\";\n}\n\nexport function parseMasterKey(base64: string): Buffer {\n const key = Buffer.from(base64, \"base64\");\n if (key.length !== 32) {\n throw new CryptoError(\"La llave maestra debe ser de 32 bytes en base64. Genera una con: openssl rand -base64 32\");\n }\n return key;\n}\n\nfunction deriveKey(masterKey: Buffer, context: string): Buffer {\n return Buffer.from(hkdfSync(\"sha256\", masterKey, Buffer.alloc(0), `pagosrd:${context}`, 32));\n}\n\nexport function encryptSecret(masterKey: Buffer, context: string, plaintext: string): string {\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", deriveKey(masterKey, context), iv);\n cipher.setAAD(Buffer.from(context));\n const encrypted = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n return `v1:${iv.toString(\"base64\")}:${cipher.getAuthTag().toString(\"base64\")}:${encrypted.toString(\"base64\")}`;\n}\n\nexport function decryptSecret(masterKey: Buffer, context: string, payload: string): string {\n const [version, iv, tag, data] = payload.split(\":\");\n if (version !== \"v1\" || !iv || !tag || !data) throw new CryptoError(\"Formato de secreto cifrado desconocido.\");\n try {\n const decipher = createDecipheriv(\"aes-256-gcm\", deriveKey(masterKey, context), Buffer.from(iv, \"base64\"));\n decipher.setAAD(Buffer.from(context));\n decipher.setAuthTag(Buffer.from(tag, \"base64\"));\n return Buffer.concat([decipher.update(Buffer.from(data, \"base64\")), decipher.final()]).toString(\"utf8\");\n } catch (err) {\n throw new CryptoError(\"No se pudo descifrar el secreto: llave maestra o contexto incorrectos.\", { cause: err });\n }\n}\n\nexport function encryptJson(masterKey: Buffer, context: string, value: unknown): string {\n return encryptSecret(masterKey, context, JSON.stringify(value));\n}\n\nexport function decryptJson<T = unknown>(masterKey: Buffer, context: string, payload: string): T {\n return JSON.parse(decryptSecret(masterKey, context, payload)) as T;\n}\n\n// ------------------------------------------------------------------ llaves de API\n\nconst BASE62 = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\nexport function randomToken(length: number): string {\n const bytes = randomBytes(length);\n let out = \"\";\n for (let i = 0; i < length; i++) out += BASE62[bytes[i]! % 62];\n return out;\n}\n\nexport type ApiKeyMode = \"test\" | \"live\";\n\n/** Genera una llave de API. El valor completo solo se muestra una vez. */\nexport function generateApiKey(mode: ApiKeyMode): { key: string; prefix: string; hash: string } {\n const key = `sk_${mode}_${randomToken(32)}`;\n return { key, prefix: key.slice(0, 12), hash: hashApiKey(key) };\n}\n\nexport function hashApiKey(key: string): string {\n return createHash(\"sha256\").update(key).digest(\"hex\");\n}\n\nexport function safeEqual(a: string, b: string): boolean {\n const ab = Buffer.from(a);\n const bb = Buffer.from(b);\n return ab.length === bb.length && timingSafeEqual(ab, bb);\n}\n\nexport function hmacSha256Hex(secret: string, data: string): string {\n return createHmac(\"sha256\", secret).update(data).digest(\"hex\");\n}\n","import { hmacSha256Hex, randomToken, safeEqual } from \"./crypto.js\";\n\n/**\n * Firma de los webhooks que PagosRD envía a las plataformas.\n *\n * Cabecera: `PagosRD-Signature: t=<unix>,v1=<hex>`\n * donde v1 = HMAC-SHA256(secreto, `${t}.${cuerpo}`).\n */\n\nexport const SIGNATURE_HEADER = \"PagosRD-Signature\";\nexport const DEFAULT_TOLERANCE_SECONDS = 300;\n\nexport class WebhookSignatureError extends Error {\n override readonly name = \"WebhookSignatureError\";\n}\n\nexport function generateWebhookSecret(): string {\n return `whsec_${randomToken(32)}`;\n}\n\nexport function signWebhook(secret: string, body: string, timestamp = Math.floor(Date.now() / 1000)): string {\n return `t=${timestamp},v1=${hmacSha256Hex(secret, `${timestamp}.${body}`)}`;\n}\n\n/**\n * Verifica la firma de un webhook. Lanza WebhookSignatureError si no es válida.\n * Usa el cuerpo CRUDO tal como llegó (no el JSON re-serializado).\n */\nexport function verifyWebhook(\n secret: string,\n body: string,\n header: string | null | undefined,\n options: { toleranceSeconds?: number; now?: number } = {},\n): void {\n if (!header) throw new WebhookSignatureError(`Falta la cabecera ${SIGNATURE_HEADER}.`);\n const parts = Object.fromEntries(\n header.split(\",\").map((p) => {\n const i = p.indexOf(\"=\");\n return [p.slice(0, i).trim(), p.slice(i + 1).trim()];\n }),\n );\n const t = Number(parts.t);\n const v1 = parts.v1;\n if (!Number.isInteger(t) || !v1) throw new WebhookSignatureError(\"Cabecera de firma con formato inválido.\");\n const now = options.now ?? Math.floor(Date.now() / 1000);\n const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;\n if (Math.abs(now - t) > tolerance) {\n throw new WebhookSignatureError(\"La firma es demasiado vieja o del futuro (posible reenvío malicioso).\");\n }\n if (!safeEqual(hmacSha256Hex(secret, `${t}.${body}`), v1)) {\n throw new WebhookSignatureError(\"La firma no coincide.\");\n }\n}\n","import { SIGNATURE_HEADER, verifyWebhook, WebhookSignatureError } from \"@pagosrd/core\";\nimport type { Event } from \"./types.js\";\n\nexport { SIGNATURE_HEADER, WebhookSignatureError };\n\n/**\n * Verifica la firma de un webhook y devuelve el evento tipado.\n *\n * IMPORTANTE: pasa el cuerpo CRUDO (string o Buffer), tal como llegó. Si tu\n * framework ya lo convirtió a JSON, la firma no va a coincidir.\n *\n * ```ts\n * app.post(\"/webhooks/pagosrd\", express.raw({ type: \"application/json\" }), (req, res) => {\n * const event = constructEvent(req.body, req.header(\"PagosRD-Signature\"), process.env.PAGOSRD_WEBHOOK_SECRET!);\n * if (event.type === \"payment.succeeded\") marcarCuotaPagada(event.data.payment_link_reference);\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function constructEvent(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string, toleranceSeconds = 300): Event {\n const body = typeof rawBody === \"string\" ? rawBody : new TextDecoder().decode(rawBody);\n verifyWebhook(secret, body, signatureHeader, { toleranceSeconds });\n return JSON.parse(body) as Event;\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMoney, PagosRD, PagosRDError, toCents, type Payment, type PaymentLink } from \"@pagosrd/node\";\nimport { z } from \"zod\";\n\n/**\n * Servidor MCP de PagosRD. Permite a Claude (u otro cliente MCP) crear links de\n * cobro, consultar pagos y reembolsar usando la llave de API de la plataforma.\n *\n * Los montos se piden en PESOS (\"4,500.00\") porque es como habla la gente; el\n * servidor los convierte a centavos sin errores de redondeo.\n */\n\nconst pesos = z.string().describe('Monto en pesos dominicanos, por ejemplo \"4,500.00\" o \"1500\".');\n\nfunction linkText(l: PaymentLink): string {\n return [\n `Link ${l.id} (${l.status})`,\n `Monto: ${formatMoney(l.amount, l.currency)}${l.itbis ? ` (ITBIS ${formatMoney(l.itbis, l.currency)})` : \"\"}`,\n `Concepto: ${l.description}`,\n l.reference ? `Referencia: ${l.reference}` : null,\n l.customer.name ? `Cliente: ${l.customer.name}` : null,\n `URL para pagar: ${l.url}`,\n l.expires_at ? `Vence: ${l.expires_at}` : null,\n l.livemode ? null : \"Modo de prueba: no se cobra dinero real.\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction paymentText(p: Payment): string {\n return [\n `Pago ${p.id}: ${p.status}${p.requires_review ? \" (requiere revisión)\" : \"\"}`,\n `Monto: ${formatMoney(p.amount, p.currency)}${p.amount_refunded ? `, reembolsado ${formatMoney(p.amount_refunded, p.currency)}` : \"\"}`,\n p.card ? `Tarjeta: ${p.card.brand ?? \"\"} terminada en ${p.card.last4}` : null,\n p.failure_message ? `Detalle: ${p.failure_message}` : null,\n p.paid_at ? `Pagado: ${p.paid_at}` : null,\n p.payment_link_id ? `Link: ${p.payment_link_id}` : null,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\ntype ToolResult = { content: { type: \"text\"; text: string }[]; isError?: boolean };\n\nasync function run(fn: () => Promise<string>): Promise<ToolResult> {\n try {\n return { content: [{ type: \"text\", text: await fn() }] };\n } catch (err) {\n const message = err instanceof PagosRDError ? `${err.message}${err.field ? ` (campo: ${err.field})` : \"\"} [${err.code}]` : (err as Error).message;\n return { content: [{ type: \"text\", text: `Error: ${message}` }], isError: true };\n }\n}\n\nexport function createServer(client: PagosRD): McpServer {\n const server = new McpServer({ name: \"pagosrd\", version: \"0.1.0\" });\n\n server.registerTool(\n \"crear_link_de_cobro\",\n {\n title: \"Crear link de cobro\",\n description:\n \"Crea un link de cobro con tarjeta para un comercio y devuelve la URL para enviársela al cliente (por WhatsApp o correo). Si no sabes el comercio, usa primero listar_comercios.\",\n inputSchema: {\n comercio_id: z.string().describe(\"Id del comercio (mer_…).\"),\n monto: pesos.describe('Total a cobrar en pesos, ITBIS incluido. Ej: \"4,500.00\".'),\n itbis: pesos.optional().describe(\"Parte del monto que es ITBIS, en pesos. Omítelo si está exento.\"),\n concepto: z.string().min(1).max(140).describe('Qué se cobra. Ej: \"Cuota de mantenimiento octubre, Apto 4B\".'),\n referencia: z.string().max(100).optional().describe(\"Tu referencia interna (factura, cuota). Vuelve en los webhooks.\"),\n cliente_nombre: z.string().optional(),\n cliente_correo: z.string().optional(),\n cliente_telefono: z.string().optional(),\n idioma: z.enum([\"es\", \"en\"]).optional().describe(\"Idioma de la página de pago. Por defecto es.\"),\n vence_en_dias: z.number().int().min(1).max(90).optional().describe(\"Días hasta que vence el link. Por defecto 30.\"),\n },\n annotations: { destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (a) =>\n run(async () => {\n const link = await client.paymentLinks.create({\n merchant_id: a.comercio_id,\n amount: toCents(a.monto),\n itbis: a.itbis ? toCents(a.itbis) : undefined,\n description: a.concepto,\n reference: a.referencia,\n customer: { name: a.cliente_nombre, email: a.cliente_correo, phone: a.cliente_telefono },\n locale: a.idioma,\n expires_at: a.vence_en_dias ? new Date(Date.now() + a.vence_en_dias * 86_400_000).toISOString() : undefined,\n });\n return `Link de cobro creado.\\n${linkText(link)}`;\n }),\n );\n\n server.registerTool(\n \"consultar_link\",\n { title: \"Consultar link de cobro\", description: \"Muestra el estado de un link de cobro (abierto, pagado, vencido o cancelado).\", inputSchema: { link_id: z.string() }, annotations: { readOnlyHint: true } },\n async (a) => run(async () => linkText(await client.paymentLinks.retrieve(a.link_id))),\n );\n\n server.registerTool(\n \"listar_links\",\n {\n title: \"Listar links de cobro\",\n description: \"Lista links de cobro recientes. Puede filtrar por estado o por referencia.\",\n inputSchema: {\n estado: z.enum([\"open\", \"paid\", \"expired\", \"canceled\"]).optional().describe(\"open = pendiente de pago.\"),\n referencia: z.string().optional(),\n cantidad: z.number().int().min(1).max(50).optional(),\n },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const list = await client.paymentLinks.list({ status: a.estado, reference: a.referencia, limit: a.cantidad ?? 10 });\n if (list.data.length === 0) return \"No hay links que coincidan.\";\n return list.data.map(linkText).join(\"\\n\\n\") + (list.has_more ? \"\\n\\nHay más resultados.\" : \"\");\n }),\n );\n\n server.registerTool(\n \"cancelar_link\",\n {\n title: \"Cancelar link de cobro\",\n description: \"Cancela un link que aún no se ha pagado, para que ya no se pueda pagar. Confirma con el usuario antes de usarla.\",\n inputSchema: { link_id: z.string() },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async (a) => run(async () => `Link cancelado.\\n${linkText(await client.paymentLinks.cancel(a.link_id))}`),\n );\n\n server.registerTool(\n \"consultar_pago\",\n { title: \"Consultar pago\", description: \"Muestra el detalle de un pago: estado, monto, tarjeta y reembolsos.\", inputSchema: { pago_id: z.string() }, annotations: { readOnlyHint: true } },\n async (a) => run(async () => paymentText(await client.payments.retrieve(a.pago_id))),\n );\n\n server.registerTool(\n \"listar_pagos\",\n {\n title: \"Listar pagos\",\n description: \"Lista pagos recientes, con filtro opcional por estado.\",\n inputSchema: {\n estado: z.enum([\"pending\", \"processing\", \"succeeded\", \"failed\", \"canceled\"]).optional().describe(\"succeeded = cobrado.\"),\n cantidad: z.number().int().min(1).max(50).optional(),\n },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const list = await client.payments.list({ status: a.estado, limit: a.cantidad ?? 10 });\n if (list.data.length === 0) return \"No hay pagos que coincidan.\";\n return list.data.map(paymentText).join(\"\\n\\n\");\n }),\n );\n\n server.registerTool(\n \"reembolsar_pago\",\n {\n title: \"Reembolsar pago\",\n description:\n \"Devuelve dinero de un pago cobrado al cliente, total o parcial. Mueve dinero real en modo live: confirma SIEMPRE con el usuario el pago y el monto antes de llamarla.\",\n inputSchema: {\n pago_id: z.string(),\n monto: pesos.optional().describe(\"Monto a reembolsar en pesos. Omítelo para reembolsar todo lo disponible.\"),\n motivo: z.string().max(300).optional(),\n },\n annotations: { destructiveHint: true, idempotentHint: false, openWorldHint: true },\n },\n async (a) =>\n run(async () => {\n const r = await client.refunds.create(a.pago_id, { amount: a.monto ? toCents(a.monto) : undefined, reason: a.motivo });\n return `Reembolso ${r.id}: ${r.status}, ${formatMoney(r.amount)}${r.failure_message ? `. Detalle: ${r.failure_message}` : \"\"}`;\n }),\n );\n\n server.registerTool(\n \"listar_comercios\",\n { title: \"Listar comercios\", description: \"Lista los comercios de la plataforma, con su procesador y estado.\", inputSchema: {}, annotations: { readOnlyHint: true } },\n async () =>\n run(async () => {\n const list = await client.merchants.list({ limit: 100 });\n if (list.data.length === 0) return \"No hay comercios.\";\n return list.data.map((m) => `${m.id}: ${m.name} (${m.processor}, ${m.status}${m.processor_configured ? \"\" : \", sin credenciales\"})`).join(\"\\n\");\n }),\n );\n\n server.registerTool(\n \"resumen_de_cobros\",\n {\n title: \"Resumen de cobros\",\n description: \"Totales de los últimos días: cobrado, cantidad de pagos, reembolsado, fallidos y pagos en revisión.\",\n inputSchema: { dias: z.number().int().min(1).max(365).optional().describe(\"Por defecto 30.\") },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const dias = a.dias ?? 30;\n const s = await client.summary({ from: new Date(Date.now() - dias * 86_400_000).toISOString() });\n return [\n `Últimos ${dias} días:`,\n `Cobrado: ${formatMoney(s.succeeded_amount)} en ${s.succeeded_count} ${s.succeeded_count === 1 ? \"pago\" : \"pagos\"}`,\n `Reembolsado: ${formatMoney(s.refunded_amount)}`,\n `Pagos fallidos: ${s.failed_count}`,\n s.review_count ? `Pagos que requieren revisión: ${s.review_count}` : null,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n }),\n );\n\n return server;\n}\n"],"mappings":";;;AACA,SAAS,4BAA4B;;;ACDrC,SAAS,kBAAkB;AA4BpB,IAAM,mBAAmB;AAGzB,IAAM,eAAN,cAA2B,MAAM;EAEtC,YACE,SACS,QACA,MACA,OACA,WACA,KACT;AACA,UAAM,OAAO;AANJ,SAAA,SAAA;AACA,SAAA,OAAA;AACA,SAAA,QAAA;AACA,SAAA,YAAA;AACA,SAAA,MAAA;EAGX;EAPW;EACA;EACA;EACA;EACA;EAPO,OAAO;AAW3B;AAGO,IAAM,yBAAN,cAAqC,MAAM;EAC9B,OAAO;AAC3B;AAcO,IAAM,UAAN,MAAc;EAMnB,YACmBA,SACjB,UAA0B,CAAC,GAC3B;AAFiB,SAAA,SAAAA;AAGjB,QAAI,CAACA,WAAU,CAAC,mBAAmB,KAAKA,OAAM,GAAG;AAC/C,YAAM,IAAI,MAAM,iEAA8D;IAChF;AACA,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,OAAO,EAAE;AACtE,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,YAAY,QAAQ,aAAa;AACtC,SAAK,YAAY,QAAQ,SAAS,WAAW;EAC/C;EAVmB;EANF;EACA;EACA;EACA;;EAgBjB,IAAI,WAAoB;AACtB,WAAO,KAAK,OAAO,WAAW,UAAU;EAC1C;;EAIS,eAAe;IACtB,QAAQ,CAAC,QAAiC,SAA0B,KAAK,KAAkB,qBAAqB,QAAQ,IAAI;IAC5H,UAAU,CAAC,OAAe,KAAK,IAAiB,qBAAqB,IAAI,EAAE,CAAC,EAAE;IAC9E,MAAM,CAAC,SAAoG,CAAC,MAC1G,KAAK,IAAuB,qBAAqB,MAAM;IACzD,QAAQ,CAAC,OAAe,KAAK,KAAkB,qBAAqB,IAAI,EAAE,CAAC,WAAW,MAAS;EACjG;EAES,WAAW;;;;;;IAMlB,QAAQ,CAAC,QAA6B,SAAsD,KAAK,KAAc,gBAAgB,QAAQ,IAAI;IAC3I,UAAU,CAAC,OAAe,KAAK,IAAa,gBAAgB,IAAI,EAAE,CAAC,EAAE;IACrE,MAAM,CACJ,SAQI,CAAC,MACF,KAAK,IAAmB,gBAAgB,MAAM;EACrD;EAES,iBAAiB;IACxB,UAAU,CAAC,OAAe,KAAK,IAAmB,uBAAuB,IAAI,EAAE,CAAC,EAAE;IAClF,MAAM,CAAC,SAA+G,CAAC,MACrH,KAAK,IAAyB,uBAAuB,MAAM;;IAE7D,KAAK,CAAC,OAAe,KAAK,QAAuB,UAAU,uBAAuB,IAAI,EAAE,CAAC,IAAI,QAAW,WAAW,CAAC;EACtH;EAES,gBAAgB;;;;;IAKvB,QAAQ,CAAC,QAAkC,SAAsD,KAAK,KAAmB,qBAAqB,QAAQ,IAAI;IAC1J,UAAU,CAAC,OAAe,KAAK,IAAkB,qBAAqB,IAAI,EAAE,CAAC,EAAE;IAC/E,MAAM,CAAC,SAAsI,CAAC,MAC5I,KAAK,IAAwB,qBAAqB,MAAM;IAC1D,QAAQ,CAAC,IAAY,WAAqC,KAAK,QAAsB,SAAS,qBAAqB,IAAI,EAAE,CAAC,IAAI,MAAM;;IAEpI,OAAO,CAAC,OAAe,KAAK,KAAmB,qBAAqB,IAAI,EAAE,CAAC,UAAU,MAAS;IAC9F,QAAQ,CAAC,OAAe,KAAK,KAAmB,qBAAqB,IAAI,EAAE,CAAC,WAAW,MAAS;EAClG;EAES,UAAU;IACjB,QAAQ,CAAC,WAAmB,SAA6B,CAAC,GAAG,SAA0B,KAAK,KAAa,gBAAgB,IAAI,SAAS,CAAC,YAAY,QAAQ,IAAI;IAC/J,MAAM,CAAC,SAA+C,CAAC,MAAM,KAAK,IAAkB,eAAe,MAAM;EAC3G;EAES,YAAY;IACnB,QAAQ,CAAC,WAAiC,KAAK,KAAe,iBAAiB,MAAM;IACrF,UAAU,CAAC,OAAe,KAAK,IAAc,iBAAiB,IAAI,EAAE,CAAC,EAAE;IACvE,MAAM,CAAC,SAAqB,CAAC,MAAM,KAAK,IAAoB,iBAAiB,MAAM;IACnF,QAAQ,CAAC,IAAY,WACnB,KAAK,QAAkB,SAAS,iBAAiB,IAAI,EAAE,CAAC,IAAI,MAAM;IACpE,oBAAoB,CAAC,IAAY,WAAuC,KAAK,QAAkB,OAAO,iBAAiB,IAAI,EAAE,CAAC,cAAc,MAAM;IAClJ,SAAS,CAAC,OAAe,KAAK,IAA8E,iBAAiB,IAAI,EAAE,CAAC,UAAU;EAChJ;EAES,mBAAmB;IAC1B,QAAQ,CAAC,WAA4E,KAAK,KAA2C,yBAAyB,MAAM;IACpK,MAAM,MAAM,KAAK,IAA2B,uBAAuB;IACnE,UAAU,CAAC,OAAe,KAAK,IAAqB,yBAAyB,IAAI,EAAE,CAAC,EAAE;IACtF,QAAQ,CAAC,IAAY,WACnB,KAAK,QAAyB,SAAS,yBAAyB,IAAI,EAAE,CAAC,IAAI,MAAM;IACnF,KAAK,CAAC,OAAe,KAAK,QAAuC,UAAU,yBAAyB,IAAI,EAAE,CAAC,EAAE;IAC7G,YAAY,CAAC,OAAe,KAAK,KAAyB,yBAAyB,IAAI,EAAE,CAAC,gBAAgB,MAAS;IACnH,YAAY,CAAC,SAAuG,CAAC,MACnH,KAAK,IAA2B,0BAA0B,MAAM;EACpE;EAES,SAAS;IAChB,UAAU,CAAC,OAAe,KAAK,IAAW,cAAc,IAAI,EAAE,CAAC,EAAE;IACjE,MAAM,CAAC,SAAyC,CAAC,MAAM,KAAK,IAAiB,cAAc,MAAM;IACjG,QAAQ,CAAC,IAAY,eAAwB,KAAK,KAA4B,cAAc,IAAI,EAAE,CAAC,WAAW,aAAa,EAAE,aAAa,WAAW,IAAI,CAAC,CAAC;EAC7J;EAEA,QAAQ,SAAyC,CAAC,GAAG;AACnD,WAAO,KAAK,IAAa,eAAe,MAAM;EAChD;;;;;;EAOA,OAAO,aAAuC,WAAmD,WAAW,KAAwB;AAClI,QAAI;AACJ,WAAO,MAAM;AACX,YAAM,OAAO,MAAM,UAAU,EAAE,OAAO,UAAU,gBAAgB,cAAc,CAAC;AAC/E,iBAAW,QAAQ,KAAK,KAAM,OAAM;AACpC,UAAI,CAAC,KAAK,YAAY,KAAK,KAAK,WAAW,EAAG;AAC9C,sBAAgB,KAAK,KAAK,KAAK,KAAK,SAAS,CAAC,EAAG;IACnD;EACF;;EAIQ,IAAO,MAAc,OAAe;AAC1C,WAAO,KAAK,QAAW,OAAO,MAAM,QAAW,QAAW,KAAK;EACjE;EAEQ,KAAQ,MAAc,MAAe,MAAuB;AAClE,WAAO,KAAK,QAAW,QAAQ,MAAM,MAAM,MAAM,kBAAkB,WAAW,CAAC;EACjF;EAEA,MAAc,QAAW,QAAgB,MAAc,MAAgB,gBAAyB,OAA2B;AACzH,UAAM,MAAM,IAAI,IAAI,KAAK,UAAU,IAAI;AACvC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,CAAC,CAAC,EAA0B,KAAI,MAAM,UAAa,MAAM,KAAM,KAAI,aAAa,IAAI,GAAG,OAAO,CAAC,CAAC;AAC7I,UAAM,UAAkC;MACtC,eAAe,UAAU,KAAK,MAAM;MACpC,cAAc;MACd,QAAQ;IACV;AACA,QAAI,SAAS,OAAW,SAAQ,cAAc,IAAI;AAClD,QAAI,eAAgB,SAAQ,iBAAiB,IAAI;AAEjD,UAAM,YAAY,WAAW,SAAS,QAAQ,cAAc;AAE5D,aAAS,UAAU,KAAK,WAAW;AACjC,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,KAAK,UAAU,KAAK;UAC9B;UACA;UACA,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;UAC1D,QAAQ,YAAY,QAAQ,KAAK,SAAS;QAC5C,CAAC;MACH,SAAS,KAAK;AACZ,YAAI,aAAa,UAAU,KAAK,YAAY;AAC1C,gBAAM,MAAM,QAAQ,OAAO,CAAC;AAC5B;QACF;AACA,cAAM,IAAI,uBAAuB,oCAAqC,IAAc,OAAO,EAAE;MAC/F;AACA,WAAK,IAAI,WAAW,OAAO,IAAI,UAAU,QAAQ,aAAa,UAAU,KAAK,YAAY;AACvF,cAAM,aAAa,OAAO,IAAI,QAAQ,IAAI,aAAa,CAAC;AACxD,cAAM,MAAM,OAAO,SAAS,UAAU,KAAK,aAAa,IAAI,aAAa,MAAO,QAAQ,OAAO,CAAC;AAChG;MACF;AACA,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAM,OAAgB,OAAO,SAAS,IAAI,IAAI;AAC9C,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAK,QAAQ,CAAC;AACpB,cAAM,IAAI,aAAa,EAAE,UAAU,cAAc,IAAI,MAAM,IAAI,IAAI,QAAQ,EAAE,QAAQ,cAAc,EAAE,OAAO,IAAI,QAAQ,IAAI,cAAc,KAAK,QAAW,IAAI;MAChK;AACA,aAAO;IACT;EACF;AACF;AAEA,IAAM,MAAM;AACZ,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAClE,IAAM,UAAU,CAAC,YAAoB,KAAK,IAAI,KAAO,MAAM,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO,IAAI;AAClG,SAAS,SAAS,MAAuB;AACvC,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;EACxB,QAAQ;AACN,WAAO,EAAE,QAAQ,KAAK;EACxB;AACF;AC1PO,IAAM,aAAN,cAAyB,MAAM;EAClB,OAAO;AAC3B;AAGO,SAAS,MAAM,OAAe,QAAQ,UAAiB;AAC5D,MAAI,CAAC,OAAO,cAAc,KAAK,EAAG,OAAM,IAAI,WAAW,GAAG,KAAK,kCAAkC;AACjG,MAAI,QAAQ,EAAG,OAAM,IAAI,WAAW,GAAG,KAAK,yBAAyB;AACrE,SAAO;AACT;AAGO,SAAS,QAAQ,QAAgC;AACtD,QAAM,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,IAAI;AAC9D,QAAM,WAAW,KAAK,KAAK,EAAE,QAAQ,uBAAuB,EAAE;AAG9D,MAAI,SAAS,SAAS,GAAG,KAAK,CAAC,iCAAiC,KAAK,QAAQ,GAAG;AAC9E,UAAM,IAAI,WAAW,mBAAmB,IAAI,qFAAqF;EACnI;AACA,QAAM,UAAU,SAAS,QAAQ,MAAM,EAAE;AACzC,QAAM,QAAQ,0BAA0B,KAAK,OAAO;AACpD,MAAI,CAAC,MAAO,OAAM,IAAI,WAAW,uBAAoB,IAAI,oCAAiC;AAC1F,QAAM,QAAQ,OAAO,MAAM,CAAC,CAAC,IAAI,MAAM,QAAQ,MAAM,CAAC,KAAK,IAAI,OAAO,GAAG,GAAG,CAAC;AAC7E,SAAO,MAAM,KAAK;AACpB;AAEA,IAAM,aAAa,oBAAI,IAAiC;AAGjD,SAAS,YAAY,OAAe,WAAqB,OAAe;AAC7E,MAAI,IAAI,WAAW,IAAI,QAAQ;AAC/B,MAAI,CAAC,GAAG;AACN,QAAI,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,UAAU,uBAAuB,EAAE,CAAC;AAC5F,eAAW,IAAI,UAAU,CAAC;EAC5B;AACA,SAAO,EAAE,OAAO,QAAQ,GAAG;AAC7B;;;AI3CA,SAAS,iBAAiB;AAE1B,SAAS,SAAS;AAUlB,IAAM,QAAQ,EAAE,OAAO,EAAE,SAAS,8DAA8D;AAEhG,SAAS,SAAS,GAAwB;AACxC,SAAO;AAAA,IACL,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM;AAAA,IACzB,UAAU,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,WAAW,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE;AAAA,IAC3G,aAAa,EAAE,WAAW;AAAA,IAC1B,EAAE,YAAY,eAAe,EAAE,SAAS,KAAK;AAAA,IAC7C,EAAE,SAAS,OAAO,YAAY,EAAE,SAAS,IAAI,KAAK;AAAA,IAClD,mBAAmB,EAAE,GAAG;AAAA,IACxB,EAAE,aAAa,UAAU,EAAE,UAAU,KAAK;AAAA,IAC1C,EAAE,WAAW,OAAO;AAAA,EACtB,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AACd;AAEA,SAAS,YAAY,GAAoB;AACvC,SAAO;AAAA,IACL,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,kBAAkB,4BAAyB,EAAE;AAAA,IAC3E,UAAU,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,EAAE,kBAAkB,iBAAiB,YAAY,EAAE,iBAAiB,EAAE,QAAQ,CAAC,KAAK,EAAE;AAAA,IACpI,EAAE,OAAO,YAAY,EAAE,KAAK,SAAS,EAAE,iBAAiB,EAAE,KAAK,KAAK,KAAK;AAAA,IACzE,EAAE,kBAAkB,YAAY,EAAE,eAAe,KAAK;AAAA,IACtD,EAAE,UAAU,WAAW,EAAE,OAAO,KAAK;AAAA,IACrC,EAAE,kBAAkB,SAAS,EAAE,eAAe,KAAK;AAAA,EACrD,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AACd;AAIA,eAAe,IAAI,IAAgD;AACjE,MAAI;AACF,WAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE;AAAA,EACzD,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,eAAe,GAAG,IAAI,OAAO,GAAG,IAAI,QAAQ,YAAY,IAAI,KAAK,MAAM,EAAE,KAAK,IAAI,IAAI,MAAO,IAAc;AAC1I,WAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,OAAO,GAAG,CAAC,GAAG,SAAS,KAAK;AAAA,EACjF;AACF;AAEO,SAAS,aAAaC,SAA4B;AACvD,QAAMC,UAAS,IAAI,UAAU,EAAE,MAAM,WAAW,SAAS,QAAQ,CAAC;AAElE,EAAAA,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,aAAa;AAAA,QACX,aAAa,EAAE,OAAO,EAAE,SAAS,+BAA0B;AAAA,QAC3D,OAAO,MAAM,SAAS,0DAA0D;AAAA,QAChF,OAAO,MAAM,SAAS,EAAE,SAAS,uEAAiE;AAAA,QAClG,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,iEAA8D;AAAA,QAC5G,YAAY,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,iEAAiE;AAAA,QACrH,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,QACpC,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,QACpC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,QACtC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,iDAA8C;AAAA,QAC/F,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,kDAA+C;AAAA,MACpH;AAAA,MACA,aAAa,EAAE,iBAAiB,OAAO,gBAAgB,OAAO,eAAe,KAAK;AAAA,IACpF;AAAA,IACA,OAAO,MACL,IAAI,YAAY;AACd,YAAM,OAAO,MAAMD,QAAO,aAAa,OAAO;AAAA,QAC5C,aAAa,EAAE;AAAA,QACf,QAAQ,QAAQ,EAAE,KAAK;AAAA,QACvB,OAAO,EAAE,QAAQ,QAAQ,EAAE,KAAK,IAAI;AAAA,QACpC,aAAa,EAAE;AAAA,QACf,WAAW,EAAE;AAAA,QACb,UAAU,EAAE,MAAM,EAAE,gBAAgB,OAAO,EAAE,gBAAgB,OAAO,EAAE,iBAAiB;AAAA,QACvF,QAAQ,EAAE;AAAA,QACV,YAAY,EAAE,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,EAAE,gBAAgB,KAAU,EAAE,YAAY,IAAI;AAAA,MACpG,CAAC;AACD,aAAO;AAAA,EAA0B,SAAS,IAAI,CAAC;AAAA,IACjD,CAAC;AAAA,EACL;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA,EAAE,OAAO,2BAA2B,aAAa,iFAAiF,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,cAAc,KAAK,EAAE;AAAA,IAC5M,OAAO,MAAM,IAAI,YAAY,SAAS,MAAMD,QAAO,aAAa,SAAS,EAAE,OAAO,CAAC,CAAC;AAAA,EACtF;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,QAAQ,EAAE,KAAK,CAAC,QAAQ,QAAQ,WAAW,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,QACvG,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,QAChC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,MACrD;AAAA,MACA,aAAa,EAAE,cAAc,KAAK;AAAA,IACpC;AAAA,IACA,OAAO,MACL,IAAI,YAAY;AACd,YAAM,OAAO,MAAMD,QAAO,aAAa,KAAK,EAAE,QAAQ,EAAE,QAAQ,WAAW,EAAE,YAAY,OAAO,EAAE,YAAY,GAAG,CAAC;AAClH,UAAI,KAAK,KAAK,WAAW,EAAG,QAAO;AACnC,aAAO,KAAK,KAAK,IAAI,QAAQ,EAAE,KAAK,MAAM,KAAK,KAAK,WAAW,+BAA4B;AAAA,IAC7F,CAAC;AAAA,EACL;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE;AAAA,MACnC,aAAa,EAAE,iBAAiB,MAAM,gBAAgB,KAAK;AAAA,IAC7D;AAAA,IACA,OAAO,MAAM,IAAI,YAAY;AAAA,EAAoB,SAAS,MAAMD,QAAO,aAAa,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE;AAAA,EAC1G;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA,EAAE,OAAO,kBAAkB,aAAa,uEAAuE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,cAAc,KAAK,EAAE;AAAA,IACzL,OAAO,MAAM,IAAI,YAAY,YAAY,MAAMD,QAAO,SAAS,SAAS,EAAE,OAAO,CAAC,CAAC;AAAA,EACrF;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,QACX,QAAQ,EAAE,KAAK,CAAC,WAAW,cAAc,aAAa,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,SAAS,sBAAsB;AAAA,QACvH,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,MACrD;AAAA,MACA,aAAa,EAAE,cAAc,KAAK;AAAA,IACpC;AAAA,IACA,OAAO,MACL,IAAI,YAAY;AACd,YAAM,OAAO,MAAMD,QAAO,SAAS,KAAK,EAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,YAAY,GAAG,CAAC;AACrF,UAAI,KAAK,KAAK,WAAW,EAAG,QAAO;AACnC,aAAO,KAAK,KAAK,IAAI,WAAW,EAAE,KAAK,MAAM;AAAA,IAC/C,CAAC;AAAA,EACL;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,aAAa;AAAA,QACX,SAAS,EAAE,OAAO;AAAA,QAClB,OAAO,MAAM,SAAS,EAAE,SAAS,6EAA0E;AAAA,QAC3G,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,MACvC;AAAA,MACA,aAAa,EAAE,iBAAiB,MAAM,gBAAgB,OAAO,eAAe,KAAK;AAAA,IACnF;AAAA,IACA,OAAO,MACL,IAAI,YAAY;AACd,YAAM,IAAI,MAAMD,QAAO,QAAQ,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,QAAQ,EAAE,KAAK,IAAI,QAAW,QAAQ,EAAE,OAAO,CAAC;AACrH,aAAO,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,YAAY,EAAE,MAAM,CAAC,GAAG,EAAE,kBAAkB,cAAc,EAAE,eAAe,KAAK,EAAE;AAAA,IAC9H,CAAC;AAAA,EACL;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA,EAAE,OAAO,oBAAoB,aAAa,qEAAqE,aAAa,CAAC,GAAG,aAAa,EAAE,cAAc,KAAK,EAAE;AAAA,IACpK,YACE,IAAI,YAAY;AACd,YAAM,OAAO,MAAMD,QAAO,UAAU,KAAK,EAAE,OAAO,IAAI,CAAC;AACvD,UAAI,KAAK,KAAK,WAAW,EAAG,QAAO;AACnC,aAAO,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE,SAAS,KAAK,EAAE,MAAM,GAAG,EAAE,uBAAuB,KAAK,oBAAoB,GAAG,EAAE,KAAK,IAAI;AAAA,IAChJ,CAAC;AAAA,EACL;AAEA,EAAAC,QAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,iBAAiB,EAAE;AAAA,MAC7F,aAAa,EAAE,cAAc,KAAK;AAAA,IACpC;AAAA,IACA,OAAO,MACL,IAAI,YAAY;AACd,YAAM,OAAO,EAAE,QAAQ;AACvB,YAAM,IAAI,MAAMD,QAAO,QAAQ,EAAE,MAAM,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAU,EAAE,YAAY,EAAE,CAAC;AAC/F,aAAO;AAAA,QACL,cAAW,IAAI;AAAA,QACf,YAAY,YAAY,EAAE,gBAAgB,CAAC,OAAO,EAAE,eAAe,IAAI,EAAE,oBAAoB,IAAI,SAAS,OAAO;AAAA,QACjH,gBAAgB,YAAY,EAAE,eAAe,CAAC;AAAA,QAC9C,mBAAmB,EAAE,YAAY;AAAA,QACjC,EAAE,eAAe,oCAAiC,EAAE,YAAY,KAAK;AAAA,MACvE,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AAAA,IACd,CAAC;AAAA,EACL;AAEA,SAAOC;AACT;;;AN7MA,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAI,CAAC,QAAQ;AACX,UAAQ,MAAM,mEAAyD;AACvE,UAAQ,KAAK,CAAC;AAChB;AACA,IAAM,SAAS,IAAI,QAAQ,QAAQ,EAAE,SAAS,QAAQ,IAAI,gBAAgB,CAAC;AAC3E,IAAM,SAAS,aAAa,MAAM;AAClC,MAAM,OAAO,QAAQ,IAAI,qBAAqB,CAAC;AAE/C,QAAQ,MAAM,sBAAsB,OAAO,WAAW,mBAAmB,WAAW,IAAI;","names":["apiKey","client","server"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { PagosRD } from \"@pagosrd/node\";\nimport { createServer } from \"./server.js\";\n\nconst apiKey = process.env.PAGOSRD_API_KEY;\nif (!apiKey) {\n console.error(\"Falta PAGOSRD_API_KEY (tu llave sk_test_… o sk_live_…).\");\n process.exit(1);\n}\nconst client = new PagosRD(apiKey, { baseUrl: process.env.PAGOSRD_API_URL });\nconst server = createServer(client);\nawait server.connect(new StdioServerTransport());\n// stdout es del protocolo MCP: los mensajes para humanos van a stderr.\nconsole.error(`PagosRD MCP listo (${client.testMode ? \"modo de prueba\" : \"modo live\"}).`);\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMoney, PagosRD, PagosRDError, toCents, type Payment, type PaymentLink } from \"@pagosrd/node\";\nimport { z } from \"zod\";\n\n/**\n * Servidor MCP de PagosRD. Permite a Claude (u otro cliente MCP) crear links de\n * cobro, consultar pagos y reembolsar usando la llave de API de la plataforma.\n *\n * Los montos se piden en PESOS (\"4,500.00\") porque es como habla la gente; el\n * servidor los convierte a centavos sin errores de redondeo.\n */\n\nconst pesos = z.string().describe('Monto en pesos dominicanos, por ejemplo \"4,500.00\" o \"1500\".');\n\nfunction linkText(l: PaymentLink): string {\n return [\n `Link ${l.id} (${l.status})`,\n `Monto: ${formatMoney(l.amount, l.currency)}${l.itbis ? ` (ITBIS ${formatMoney(l.itbis, l.currency)})` : \"\"}`,\n `Concepto: ${l.description}`,\n l.reference ? `Referencia: ${l.reference}` : null,\n l.customer.name ? `Cliente: ${l.customer.name}` : null,\n `URL para pagar: ${l.url}`,\n l.expires_at ? `Vence: ${l.expires_at}` : null,\n l.livemode ? null : \"Modo de prueba: no se cobra dinero real.\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction paymentText(p: Payment): string {\n return [\n `Pago ${p.id}: ${p.status}${p.requires_review ? \" (requiere revisión)\" : \"\"}`,\n `Monto: ${formatMoney(p.amount, p.currency)}${p.amount_refunded ? `, reembolsado ${formatMoney(p.amount_refunded, p.currency)}` : \"\"}`,\n p.card ? `Tarjeta: ${p.card.brand ?? \"\"} terminada en ${p.card.last4}` : null,\n p.failure_message ? `Detalle: ${p.failure_message}` : null,\n p.paid_at ? `Pagado: ${p.paid_at}` : null,\n p.payment_link_id ? `Link: ${p.payment_link_id}` : null,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\ntype ToolResult = { content: { type: \"text\"; text: string }[]; isError?: boolean };\n\nasync function run(fn: () => Promise<string>): Promise<ToolResult> {\n try {\n return { content: [{ type: \"text\", text: await fn() }] };\n } catch (err) {\n const message = err instanceof PagosRDError ? `${err.message}${err.field ? ` (campo: ${err.field})` : \"\"} [${err.code}]` : (err as Error).message;\n return { content: [{ type: \"text\", text: `Error: ${message}` }], isError: true };\n }\n}\n\nexport function createServer(client: PagosRD): McpServer {\n const server = new McpServer({ name: \"pagosrd\", version: \"0.1.1\" });\n\n server.registerTool(\n \"crear_link_de_cobro\",\n {\n title: \"Crear link de cobro\",\n description:\n \"Crea un link de cobro con tarjeta para un comercio y devuelve la URL para enviársela al cliente (por WhatsApp o correo). Si no sabes el comercio, usa primero listar_comercios.\",\n inputSchema: {\n comercio_id: z.string().describe(\"Id del comercio (mer_…).\"),\n monto: pesos.describe('Total a cobrar en pesos, ITBIS incluido. Ej: \"4,500.00\".'),\n itbis: pesos.optional().describe(\"Parte del monto que es ITBIS, en pesos. Omítelo si está exento.\"),\n concepto: z.string().min(1).max(140).describe('Qué se cobra. Ej: \"Cuota de mantenimiento octubre, Apto 4B\".'),\n referencia: z.string().max(100).optional().describe(\"Tu referencia interna (factura, cuota). Vuelve en los webhooks.\"),\n cliente_nombre: z.string().optional(),\n cliente_correo: z.string().optional(),\n cliente_telefono: z.string().optional(),\n idioma: z.enum([\"es\", \"en\"]).optional().describe(\"Idioma de la página de pago. Por defecto es.\"),\n vence_en_dias: z.number().int().min(1).max(90).optional().describe(\"Días hasta que vence el link. Por defecto 30.\"),\n },\n annotations: { destructiveHint: false, idempotentHint: false, openWorldHint: true },\n },\n async (a) =>\n run(async () => {\n const link = await client.paymentLinks.create({\n merchant_id: a.comercio_id,\n amount: toCents(a.monto),\n itbis: a.itbis ? toCents(a.itbis) : undefined,\n description: a.concepto,\n reference: a.referencia,\n customer: { name: a.cliente_nombre, email: a.cliente_correo, phone: a.cliente_telefono },\n locale: a.idioma,\n expires_at: a.vence_en_dias ? new Date(Date.now() + a.vence_en_dias * 86_400_000).toISOString() : undefined,\n });\n return `Link de cobro creado.\\n${linkText(link)}`;\n }),\n );\n\n server.registerTool(\n \"consultar_link\",\n { title: \"Consultar link de cobro\", description: \"Muestra el estado de un link de cobro (abierto, pagado, vencido o cancelado).\", inputSchema: { link_id: z.string() }, annotations: { readOnlyHint: true } },\n async (a) => run(async () => linkText(await client.paymentLinks.retrieve(a.link_id))),\n );\n\n server.registerTool(\n \"listar_links\",\n {\n title: \"Listar links de cobro\",\n description: \"Lista links de cobro recientes. Puede filtrar por estado o por referencia.\",\n inputSchema: {\n estado: z.enum([\"open\", \"paid\", \"expired\", \"canceled\"]).optional().describe(\"open = pendiente de pago.\"),\n referencia: z.string().optional(),\n cantidad: z.number().int().min(1).max(50).optional(),\n },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const list = await client.paymentLinks.list({ status: a.estado, reference: a.referencia, limit: a.cantidad ?? 10 });\n if (list.data.length === 0) return \"No hay links que coincidan.\";\n return list.data.map(linkText).join(\"\\n\\n\") + (list.has_more ? \"\\n\\nHay más resultados.\" : \"\");\n }),\n );\n\n server.registerTool(\n \"cancelar_link\",\n {\n title: \"Cancelar link de cobro\",\n description: \"Cancela un link que aún no se ha pagado, para que ya no se pueda pagar. Confirma con el usuario antes de usarla.\",\n inputSchema: { link_id: z.string() },\n annotations: { destructiveHint: true, idempotentHint: true },\n },\n async (a) => run(async () => `Link cancelado.\\n${linkText(await client.paymentLinks.cancel(a.link_id))}`),\n );\n\n server.registerTool(\n \"consultar_pago\",\n { title: \"Consultar pago\", description: \"Muestra el detalle de un pago: estado, monto, tarjeta y reembolsos.\", inputSchema: { pago_id: z.string() }, annotations: { readOnlyHint: true } },\n async (a) => run(async () => paymentText(await client.payments.retrieve(a.pago_id))),\n );\n\n server.registerTool(\n \"listar_pagos\",\n {\n title: \"Listar pagos\",\n description: \"Lista pagos recientes, con filtro opcional por estado.\",\n inputSchema: {\n estado: z.enum([\"pending\", \"processing\", \"succeeded\", \"failed\", \"canceled\"]).optional().describe(\"succeeded = cobrado.\"),\n cantidad: z.number().int().min(1).max(50).optional(),\n },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const list = await client.payments.list({ status: a.estado, limit: a.cantidad ?? 10 });\n if (list.data.length === 0) return \"No hay pagos que coincidan.\";\n return list.data.map(paymentText).join(\"\\n\\n\");\n }),\n );\n\n server.registerTool(\n \"reembolsar_pago\",\n {\n title: \"Reembolsar pago\",\n description:\n \"Devuelve dinero de un pago cobrado al cliente, total o parcial. Mueve dinero real en modo live: confirma SIEMPRE con el usuario el pago y el monto antes de llamarla.\",\n inputSchema: {\n pago_id: z.string(),\n monto: pesos.optional().describe(\"Monto a reembolsar en pesos. Omítelo para reembolsar todo lo disponible.\"),\n motivo: z.string().max(300).optional(),\n },\n annotations: { destructiveHint: true, idempotentHint: false, openWorldHint: true },\n },\n async (a) =>\n run(async () => {\n const r = await client.refunds.create(a.pago_id, { amount: a.monto ? toCents(a.monto) : undefined, reason: a.motivo });\n return `Reembolso ${r.id}: ${r.status}, ${formatMoney(r.amount)}${r.failure_message ? `. Detalle: ${r.failure_message}` : \"\"}`;\n }),\n );\n\n server.registerTool(\n \"listar_comercios\",\n { title: \"Listar comercios\", description: \"Lista los comercios de la plataforma, con su procesador y estado.\", inputSchema: {}, annotations: { readOnlyHint: true } },\n async () =>\n run(async () => {\n const list = await client.merchants.list({ limit: 100 });\n if (list.data.length === 0) return \"No hay comercios.\";\n return list.data.map((m) => `${m.id}: ${m.name} (${m.processor}, ${m.status}${m.processor_configured ? \"\" : \", sin credenciales\"})`).join(\"\\n\");\n }),\n );\n\n server.registerTool(\n \"resumen_de_cobros\",\n {\n title: \"Resumen de cobros\",\n description: \"Totales de los últimos días: cobrado, cantidad de pagos, reembolsado, fallidos y pagos en revisión.\",\n inputSchema: { dias: z.number().int().min(1).max(365).optional().describe(\"Por defecto 30.\") },\n annotations: { readOnlyHint: true },\n },\n async (a) =>\n run(async () => {\n const dias = a.dias ?? 30;\n const s = await client.summary({ from: new Date(Date.now() - dias * 86_400_000).toISOString() });\n return [\n `Últimos ${dias} días:`,\n `Cobrado: ${formatMoney(s.succeeded_amount)} en ${s.succeeded_count} ${s.succeeded_count === 1 ? \"pago\" : \"pagos\"}`,\n `Reembolsado: ${formatMoney(s.refunded_amount)}`,\n `Pagos fallidos: ${s.failed_count}`,\n s.review_count ? `Pagos que requieren revisión: ${s.review_count}` : null,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n }),\n );\n\n return server;\n}\n"],"mappings":"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","names":["PagosRD","client","server","PagosRD"]}
|
package/package.json
CHANGED
|
@@ -1,30 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagosrd/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Servidor MCP de PagosRD: crea links de cobro, consulta pagos y reembolsa desde Claude u otros clientes MCP.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": "Cristian Encarnación",
|
|
7
|
+
"homepage": "https://github.com/cristianEncarnacion/pagosrd-mcp#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/cristianEncarnacion/pagosrd-mcp.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/cristianEncarnacion/pagosrd-mcp/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"pagosrd",
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"claude",
|
|
20
|
+
"pagos",
|
|
21
|
+
"republica-dominicana"
|
|
22
|
+
],
|
|
6
23
|
"type": "module",
|
|
7
24
|
"bin": {
|
|
8
25
|
"pagosrd-mcp": "./dist/index.js"
|
|
9
26
|
},
|
|
10
27
|
"files": [
|
|
11
28
|
"dist",
|
|
12
|
-
"README.md"
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"LICENSE"
|
|
13
32
|
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "npm run build && npm test"
|
|
38
|
+
},
|
|
14
39
|
"dependencies": {
|
|
15
40
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
41
|
+
"@pagosrd/node": "^0.1.0",
|
|
42
|
+
"zod": "^4.6.7"
|
|
18
43
|
},
|
|
19
44
|
"devDependencies": {
|
|
20
45
|
"@types/node": "^22.20.5",
|
|
21
46
|
"tsup": "^8.5.1",
|
|
22
47
|
"typescript": "^5.9.3",
|
|
23
48
|
"vitest": "^5.0.5"
|
|
24
|
-
},
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "tsup",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"typecheck": "tsc --noEmit"
|
|
29
49
|
}
|
|
30
|
-
}
|
|
50
|
+
}
|