@ar-agents/mercadopago 0.5.0 → 0.7.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 CHANGED
@@ -960,6 +960,280 @@ var MercadoPagoClient = class {
960
960
  async cancelOrder(id) {
961
961
  return this.request("POST", `/v1/orders/${id}/cancel`);
962
962
  }
963
+ // ──────────────────────────────────────────────────────────────────────────
964
+ // v0.6 — Account Balance + Movements
965
+ //
966
+ // Inspect the seller's MP wallet — what's available to withdraw, what's
967
+ // in retention (pending release), and the movement log.
968
+ //
969
+ // For per-seller marketplace setups, instantiate the client AS THE SELLER
970
+ // (with their OAuth access_token) before calling these — `getAccountBalance`
971
+ // returns the balance of WHOEVER's accessToken is active.
972
+ // ──────────────────────────────────────────────────────────────────────────
973
+ /**
974
+ * Get the seller's current MP wallet balance (available + unavailable).
975
+ * - `available_balance`: spendable / withdrawable right now.
976
+ * - `unavailable_balance`: in retention (e.g., 14-21 days for new sellers).
977
+ * - `total_amount` = sum of both.
978
+ */
979
+ async getAccountBalance() {
980
+ return this.request("GET", "/users/me/mercadopago_account/balance");
981
+ }
982
+ /**
983
+ * List wallet movements (incoming payments, transfers, refunds, holdings).
984
+ * Defaults to most-recent-first, paginated. Filter by date range with
985
+ * `from`/`to` (ISO 8601).
986
+ */
987
+ async listAccountMovements(params = {}) {
988
+ const query = {};
989
+ if (params.from) query.begin_date = params.from;
990
+ if (params.to) query.end_date = params.to;
991
+ if (params.limit !== void 0) query.limit = params.limit;
992
+ if (params.offset !== void 0) query.offset = params.offset;
993
+ const result = await this.request("GET", "/users/me/mercadopago_account/movements/search", void 0, { query });
994
+ return {
995
+ movements: result.results ?? [],
996
+ paging: result.paging ?? { limit: params.limit ?? 25, offset: params.offset ?? 0, total: 0 }
997
+ };
998
+ }
999
+ // ──────────────────────────────────────────────────────────────────────────
1000
+ // v0.6 — Settlements (release_money)
1001
+ //
1002
+ // When MP transfers funds from your MP wallet to your registered CBU.
1003
+ // ──────────────────────────────────────────────────────────────────────────
1004
+ /**
1005
+ * List settlements (transfers from MP wallet to your bank account).
1006
+ * Useful for monthly conciliation reports.
1007
+ */
1008
+ async listSettlements(params = {}) {
1009
+ const query = {};
1010
+ if (params.from) query.begin_date = params.from;
1011
+ if (params.to) query.end_date = params.to;
1012
+ if (params.status) query.status = params.status;
1013
+ if (params.limit !== void 0) query.limit = params.limit;
1014
+ if (params.offset !== void 0) query.offset = params.offset;
1015
+ const result = await this.request("GET", "/v1/account/release_money/search", void 0, { query });
1016
+ return {
1017
+ settlements: result.results ?? [],
1018
+ paging: result.paging ?? { limit: params.limit ?? 25, offset: params.offset ?? 0, total: 0 }
1019
+ };
1020
+ }
1021
+ /**
1022
+ * Get a single settlement by id. Returns the full Settlement object
1023
+ * including bank_account info (CBU, bank name).
1024
+ */
1025
+ async getSettlement(id) {
1026
+ return this.request("GET", `/v1/account/release_money/${id}`);
1027
+ }
1028
+ // ──────────────────────────────────────────────────────────────────────────
1029
+ // v0.7 — Customer + Card extensions (close gaps)
1030
+ // ──────────────────────────────────────────────────────────────────────────
1031
+ /**
1032
+ * Update a customer's profile (name, last name, address, etc.). MP merges
1033
+ * the patch — fields you don't send remain unchanged.
1034
+ */
1035
+ async updateCustomer(id, patch) {
1036
+ return this.request("PUT", `/v1/customers/${id}`, patch);
1037
+ }
1038
+ /**
1039
+ * Add a saved card to a customer using a card token (one-time, get from
1040
+ * MP's frontend Cardform). The card is then chargeable with charge_saved_card.
1041
+ */
1042
+ async createCustomerCard(customerId, cardToken) {
1043
+ return this.request(
1044
+ "POST",
1045
+ `/v1/customers/${customerId}/cards`,
1046
+ { token: cardToken }
1047
+ );
1048
+ }
1049
+ // ──────────────────────────────────────────────────────────────────────────
1050
+ // v0.7 — Subscription extensions
1051
+ // ──────────────────────────────────────────────────────────────────────────
1052
+ /**
1053
+ * Update an existing subscription. Common patches:
1054
+ * - `transaction_amount` to change the recurring amount
1055
+ * - `card_token_id` to switch payment method (e.g., expired card)
1056
+ * - `status: "cancelled" | "paused"` (alternative to dedicated cancel/pause endpoints)
1057
+ * - `reason` to update the description shown to the buyer
1058
+ */
1059
+ async updatePreapproval(id, patch) {
1060
+ return this.request("PUT", `/preapproval/${id}`, patch);
1061
+ }
1062
+ /**
1063
+ * Search subscriptions across the seller's account. Common filters:
1064
+ * `status` (pending/authorized/paused/cancelled), `payer_email`,
1065
+ * `external_reference`. Paginated.
1066
+ */
1067
+ async searchPreapprovals(params = {}) {
1068
+ const query = {};
1069
+ if (params.status) query.status = params.status;
1070
+ if (params.payerEmail) query.payer_email = params.payerEmail;
1071
+ if (params.externalReference) query.external_reference = params.externalReference;
1072
+ if (params.preapproval_plan_id) query.preapproval_plan_id = params.preapproval_plan_id;
1073
+ if (params.limit !== void 0) query.limit = params.limit;
1074
+ if (params.offset !== void 0) query.offset = params.offset;
1075
+ const result = await this.request("GET", "/preapproval/search", void 0, { query });
1076
+ return {
1077
+ results: result.results ?? [],
1078
+ paging: result.paging ?? { limit: params.limit ?? 25, offset: params.offset ?? 0, total: 0 }
1079
+ };
1080
+ }
1081
+ // ──────────────────────────────────────────────────────────────────────────
1082
+ // v0.7 — Merchant Orders (parent of Payments grouped under a Preference)
1083
+ // ──────────────────────────────────────────────────────────────────────────
1084
+ /**
1085
+ * Get a merchant_order with all its associated payments + shipments.
1086
+ * Useful for reconciling "which payments belong to which preference"
1087
+ * — typical webhook handler use case.
1088
+ */
1089
+ async getMerchantOrder(id) {
1090
+ return this.request("GET", `/merchant_orders/${id}`);
1091
+ }
1092
+ /**
1093
+ * Search merchant_orders by external_reference, preference_id, or status.
1094
+ */
1095
+ async searchMerchantOrders(params = {}) {
1096
+ const query = {};
1097
+ if (params.preferenceId) query.preference_id = params.preferenceId;
1098
+ if (params.externalReference) query.external_reference = params.externalReference;
1099
+ if (params.status) query.status = params.status;
1100
+ if (params.limit !== void 0) query.limit = params.limit;
1101
+ if (params.offset !== void 0) query.offset = params.offset;
1102
+ const result = await this.request("GET", "/merchant_orders/search", void 0, { query });
1103
+ return {
1104
+ elements: result.elements ?? [],
1105
+ paging: result.paging ?? { limit: params.limit ?? 25, offset: params.offset ?? 0, total: 0 }
1106
+ };
1107
+ }
1108
+ /**
1109
+ * Update a merchant_order — typically to add items or update shipping.
1110
+ */
1111
+ async updateMerchantOrder(id, patch) {
1112
+ return this.request("PUT", `/merchant_orders/${id}`, patch);
1113
+ }
1114
+ // ──────────────────────────────────────────────────────────────────────────
1115
+ // v0.7 — Stores + POS CRUD completion
1116
+ // ──────────────────────────────────────────────────────────────────────────
1117
+ async getStore(userId, storeId) {
1118
+ return this.request("GET", `/users/${userId}/stores/${storeId}`);
1119
+ }
1120
+ async updateStore(userId, storeId, patch) {
1121
+ return this.request("PUT", `/users/${userId}/stores/${storeId}`, patch);
1122
+ }
1123
+ async deleteStore(userId, storeId) {
1124
+ await this.request("DELETE", `/users/${userId}/stores/${storeId}`);
1125
+ }
1126
+ async getPos(posId) {
1127
+ return this.request("GET", `/pos/${posId}`);
1128
+ }
1129
+ async updatePos(posId, patch) {
1130
+ return this.request("PUT", `/pos/${posId}`, patch);
1131
+ }
1132
+ async deletePos(posId) {
1133
+ await this.request("DELETE", `/pos/${posId}`);
1134
+ }
1135
+ // ──────────────────────────────────────────────────────────────────────────
1136
+ // v0.7 — Bank Accounts (the CBUs the seller has registered for payouts)
1137
+ // ──────────────────────────────────────────────────────────────────────────
1138
+ /**
1139
+ * List bank accounts registered by the seller. The default is the one
1140
+ * that receives `release_money` settlements.
1141
+ */
1142
+ async listBankAccounts() {
1143
+ const result = await this.request(
1144
+ "GET",
1145
+ "/users/me/bank_accounts"
1146
+ );
1147
+ if (Array.isArray(result)) return result;
1148
+ return result.results ?? [];
1149
+ }
1150
+ /**
1151
+ * Register a new bank account (CBU) for the seller. Note: MP usually
1152
+ * requires this through the dashboard for compliance — this endpoint may
1153
+ * not work for all sellers.
1154
+ */
1155
+ async registerBankAccount(params) {
1156
+ return this.request("POST", "/users/me/bank_accounts", params);
1157
+ }
1158
+ // ──────────────────────────────────────────────────────────────────────────
1159
+ // v0.7 — Point Devices (physical terminal hardware: Smart, Tap to Pay)
1160
+ //
1161
+ // Distinct from the logical `Pos` entity. PointDevices are the actual
1162
+ // physical terminals you have at brick-and-mortar shops.
1163
+ // ──────────────────────────────────────────────────────────────────────────
1164
+ /**
1165
+ * List the Point devices linked to the seller's MP account. Each device
1166
+ * has an id (the device serial), an operating_mode (PDV vs STANDALONE),
1167
+ * and an optional pos_id (when bound to a logical POS).
1168
+ */
1169
+ async listPointDevices(params = {}) {
1170
+ const query = {};
1171
+ if (params.posId !== void 0) query["pos.id"] = params.posId;
1172
+ if (params.limit !== void 0) query.limit = params.limit;
1173
+ if (params.offset !== void 0) query.offset = params.offset;
1174
+ const result = await this.request("GET", "/point/integration-api/devices", void 0, { query });
1175
+ return {
1176
+ devices: result.devices ?? [],
1177
+ paging: result.paging ?? { total: 0, limit: params.limit ?? 50, offset: params.offset ?? 0 }
1178
+ };
1179
+ }
1180
+ /**
1181
+ * Switch a Point device's operating mode:
1182
+ * - "PDV": device is bound to a logical Pos and only takes payments
1183
+ * triggered through that Pos (typical for cash-register integrations).
1184
+ * - "STANDALONE": device works independently, accepts any payment.
1185
+ */
1186
+ async updatePointDeviceOperatingMode(deviceId, operatingMode) {
1187
+ return this.request(
1188
+ "PATCH",
1189
+ `/point/integration-api/devices/${encodeURIComponent(deviceId)}`,
1190
+ { operating_mode: operatingMode }
1191
+ );
1192
+ }
1193
+ /**
1194
+ * Create a payment intent on a Point device — the device prompts the buyer
1195
+ * to tap/insert/swipe. Returns immediately with intent id; query state via
1196
+ * `getPointPaymentIntent()` or wait for `point_integration_wh` webhook.
1197
+ *
1198
+ * NOTE: amount is in CENTAVOS (Point API differs from Payments API which
1199
+ * uses pesos). 100 = $1 ARS, 1000 = $10, 10000 = $100, etc.
1200
+ */
1201
+ async createPointPaymentIntent(deviceId, params) {
1202
+ const body = {
1203
+ amount: params.amount,
1204
+ ...params.description ? { description: params.description } : {},
1205
+ ...params.externalReference ? { additional_info: { external_reference: params.externalReference } } : {},
1206
+ payment: {
1207
+ installments: params.installments ?? 1,
1208
+ ...params.installmentsCost ? { installments_cost: params.installmentsCost } : {},
1209
+ ...params.printOnTerminal !== void 0 ? { print_on_terminal: params.printOnTerminal } : {},
1210
+ ...params.ticketNumber ? { ticket_number: params.ticketNumber } : {}
1211
+ }
1212
+ };
1213
+ return this.request(
1214
+ "POST",
1215
+ `/point/integration-api/devices/${encodeURIComponent(deviceId)}/payment-intents`,
1216
+ body
1217
+ );
1218
+ }
1219
+ /** Get the current state of a Point payment intent. */
1220
+ async getPointPaymentIntent(intentId) {
1221
+ return this.request(
1222
+ "GET",
1223
+ `/point/integration-api/payment-intents/${encodeURIComponent(intentId)}`
1224
+ );
1225
+ }
1226
+ /**
1227
+ * Cancel an OPEN payment intent before the buyer interacts with the device.
1228
+ * Only works while state is "OPEN" — once the buyer taps, you can't cancel.
1229
+ */
1230
+ async cancelPointPaymentIntent(deviceId, intentId) {
1231
+ await this.request(
1232
+ "DELETE",
1233
+ `/point/integration-api/devices/${encodeURIComponent(deviceId)}/payment-intents/${encodeURIComponent(intentId)}`
1234
+ );
1235
+ return { id: intentId, canceled: true };
1236
+ }
963
1237
  };
964
1238
  zod.z.enum(["MLA", "MLB", "MLM", "MCO", "MLC", "MLU"]);
965
1239
  var CurrencyIdSchema = zod.z.enum(["ARS", "USD", "BRL", "MXN"]);
@@ -1308,6 +1582,95 @@ zod.z.object({
1308
1582
  /** Capture mode: "automatic" (charges immediately) or "manual" (auth-only). */
1309
1583
  capture_mode: zod.z.string().optional()
1310
1584
  }).passthrough();
1585
+ zod.z.object({
1586
+ user_id: zod.z.union([zod.z.string(), zod.z.number()]).transform(String).optional(),
1587
+ available_balance: zod.z.number(),
1588
+ unavailable_balance: zod.z.number(),
1589
+ total_amount: zod.z.number(),
1590
+ currency_id: zod.z.string().default("ARS")
1591
+ }).passthrough();
1592
+ zod.z.object({
1593
+ id: zod.z.union([zod.z.string(), zod.z.number()]).transform(String),
1594
+ type: zod.z.string(),
1595
+ description: zod.z.string().optional(),
1596
+ amount: zod.z.number(),
1597
+ currency_id: zod.z.string().optional(),
1598
+ status: zod.z.string().optional(),
1599
+ date_created: zod.z.string().optional(),
1600
+ date_released: zod.z.string().optional(),
1601
+ reference_id: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
1602
+ payment_id: zod.z.union([zod.z.string(), zod.z.number()]).optional()
1603
+ }).passthrough();
1604
+ zod.z.object({
1605
+ id: zod.z.union([zod.z.string(), zod.z.number()]).transform(String),
1606
+ status: zod.z.string().optional(),
1607
+ amount: zod.z.number().optional(),
1608
+ currency_id: zod.z.string().optional(),
1609
+ date_created: zod.z.string().optional(),
1610
+ date_scheduled: zod.z.string().optional(),
1611
+ date_processed: zod.z.string().optional(),
1612
+ bank_account: zod.z.object({
1613
+ cbu: zod.z.string().optional(),
1614
+ bank_name: zod.z.string().optional()
1615
+ }).passthrough().optional()
1616
+ }).passthrough();
1617
+ zod.z.object({
1618
+ id: zod.z.union([zod.z.string(), zod.z.number()]).transform(String),
1619
+ status: zod.z.string().optional(),
1620
+ external_reference: zod.z.string().nullable().optional(),
1621
+ preference_id: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
1622
+ payments: zod.z.array(zod.z.unknown()).optional(),
1623
+ shipments: zod.z.array(zod.z.unknown()).optional(),
1624
+ payer: zod.z.unknown().optional(),
1625
+ collector: zod.z.unknown().optional(),
1626
+ marketplace: zod.z.string().optional(),
1627
+ total_amount: zod.z.number().optional(),
1628
+ paid_amount: zod.z.number().optional(),
1629
+ refunded_amount: zod.z.number().optional(),
1630
+ shipping_cost: zod.z.number().optional(),
1631
+ date_created: zod.z.string().optional(),
1632
+ last_updated: zod.z.string().optional(),
1633
+ site_id: zod.z.string().optional(),
1634
+ order_status: zod.z.string().optional()
1635
+ }).passthrough();
1636
+ zod.z.object({
1637
+ id: zod.z.union([zod.z.string(), zod.z.number()]).transform(String),
1638
+ cbu: zod.z.string().optional(),
1639
+ alias: zod.z.string().nullable().optional(),
1640
+ bank_name: zod.z.string().optional(),
1641
+ account_type: zod.z.string().optional(),
1642
+ status: zod.z.string().optional(),
1643
+ is_default: zod.z.boolean().optional(),
1644
+ date_created: zod.z.string().optional()
1645
+ }).passthrough();
1646
+ zod.z.object({
1647
+ id: zod.z.string(),
1648
+ pos_id: zod.z.union([zod.z.string(), zod.z.number()]).nullable().optional(),
1649
+ store_id: zod.z.union([zod.z.string(), zod.z.number()]).nullable().optional(),
1650
+ external_pos_id: zod.z.string().nullable().optional(),
1651
+ operating_mode: zod.z.union([zod.z.literal("PDV"), zod.z.literal("STANDALONE"), zod.z.string()]).optional()
1652
+ }).passthrough();
1653
+ zod.z.object({
1654
+ id: zod.z.string(),
1655
+ device_id: zod.z.string().optional(),
1656
+ amount: zod.z.number().optional(),
1657
+ state: zod.z.union([
1658
+ zod.z.literal("OPEN"),
1659
+ zod.z.literal("PROCESSING"),
1660
+ zod.z.literal("FINISHED"),
1661
+ zod.z.literal("CANCELED"),
1662
+ zod.z.literal("ERROR"),
1663
+ zod.z.string()
1664
+ ]).optional(),
1665
+ payment: zod.z.object({
1666
+ id: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
1667
+ type: zod.z.string().optional(),
1668
+ installments: zod.z.number().optional(),
1669
+ installments_cost: zod.z.string().optional(),
1670
+ print_on_terminal: zod.z.boolean().optional()
1671
+ }).passthrough().optional(),
1672
+ additional_info: zod.z.unknown().optional()
1673
+ }).passthrough();
1311
1674
 
1312
1675
  // src/oauth.ts
1313
1676
  var DEFAULT_AUTHORIZE_URL = "https://auth.mercadopago.com.ar/authorization";
@@ -1374,6 +1737,371 @@ function expirationTimeMs(issuedAtMs, expiresInSeconds) {
1374
1737
  function isExpiringSoon(expirationMs, skewSeconds = 300) {
1375
1738
  return Date.now() + skewSeconds * 1e3 >= expirationMs;
1376
1739
  }
1740
+
1741
+ // src/helpers.ts
1742
+ function computeMarketplaceFee(amountArs, rule) {
1743
+ if (amountArs <= 0) return 0;
1744
+ const percentPart = (rule.percent ?? 0) > 0 ? amountArs * (rule.percent / 100) : 0;
1745
+ const flatPart = rule.flatArs ?? 0;
1746
+ let fee = percentPart + flatPart;
1747
+ if (rule.minArs !== void 0) fee = Math.max(fee, rule.minArs);
1748
+ if (rule.maxArs !== void 0) fee = Math.min(fee, rule.maxArs);
1749
+ if (fee > amountArs) fee = amountArs;
1750
+ if (rule.round !== false) fee = Math.round(fee);
1751
+ return fee;
1752
+ }
1753
+ var STATUS_DETAIL_MAP = {
1754
+ // Approved
1755
+ accredited: {
1756
+ summary: "Pago aprobado y acreditado.",
1757
+ recommendedAction: "Confirmar al cliente y continuar con el flujo (env\xEDo, factura).",
1758
+ retryable: false
1759
+ },
1760
+ partially_refunded: {
1761
+ summary: "Pago aprobado con reembolso parcial.",
1762
+ recommendedAction: "Mostrar el monto neto y el reembolso. Verificar inventario si corresponde.",
1763
+ retryable: false
1764
+ },
1765
+ // Pending
1766
+ pending_contingency: {
1767
+ summary: "Pago en proceso por contingencia. MP est\xE1 procesando \u2014 puede demorar minutos a horas.",
1768
+ recommendedAction: "No reintentar todav\xEDa. Esperar webhook de actualizaci\xF3n (puede demorar 24h m\xE1x).",
1769
+ retryable: false
1770
+ },
1771
+ pending_review_manual: {
1772
+ summary: "Pago en revisi\xF3n manual por el equipo de seguridad de MP.",
1773
+ recommendedAction: "No reintentar. MP responder\xE1 con webhook en 24-72h. Avisar al cliente sobre la demora.",
1774
+ retryable: false
1775
+ },
1776
+ pending_waiting_payment: {
1777
+ summary: "Esperando que el comprador complete el pago (t\xEDpico de account_money / boleto / Rapipago).",
1778
+ recommendedAction: "Mostrar instrucciones de pago. MP avisar\xE1 por webhook cuando se complete.",
1779
+ retryable: false
1780
+ },
1781
+ pending_waiting_transfer: {
1782
+ summary: "Esperando confirmaci\xF3n de transferencia bancaria.",
1783
+ recommendedAction: "Esperar webhook. Sin acci\xF3n del agente.",
1784
+ retryable: false
1785
+ },
1786
+ pending_challenge: {
1787
+ summary: "El emisor de la tarjeta requiri\xF3 autenticaci\xF3n 3DS. El comprador debe completar el desaf\xEDo.",
1788
+ recommendedAction: "Redirigir al comprador a `payment.three_ds_info.external_resource_url` (us\xE1 analyze_payment_3ds para obtenerlo).",
1789
+ retryable: false
1790
+ },
1791
+ // Rejected — RETRYABLE (user can fix and try again)
1792
+ cc_rejected_bad_filled_card_number: {
1793
+ summary: "El n\xFAmero de tarjeta es incorrecto.",
1794
+ recommendedAction: "Pedir al cliente que verifique el n\xFAmero. Reintentable.",
1795
+ retryable: true
1796
+ },
1797
+ cc_rejected_bad_filled_security_code: {
1798
+ summary: "El CVV es incorrecto.",
1799
+ recommendedAction: "Pedir el CVV nuevamente. Reintentable.",
1800
+ retryable: true
1801
+ },
1802
+ cc_rejected_bad_filled_date: {
1803
+ summary: "La fecha de vencimiento es incorrecta.",
1804
+ recommendedAction: "Pedir al cliente que verifique mes/a\xF1o. Reintentable.",
1805
+ retryable: true
1806
+ },
1807
+ cc_rejected_bad_filled_other: {
1808
+ summary: "Alg\xFAn dato de la tarjeta es incorrecto.",
1809
+ recommendedAction: "Pedir al cliente que revise todos los datos. Reintentable.",
1810
+ retryable: true
1811
+ },
1812
+ cc_rejected_call_for_authorize: {
1813
+ summary: "El emisor requiere que el cliente autorice el pago llamando al banco.",
1814
+ recommendedAction: "Mostrar el tel\xE9fono del banco emisor (us\xE1 list_issuers para obtenerlo). Reintentable despu\xE9s de la llamada.",
1815
+ retryable: true
1816
+ },
1817
+ cc_rejected_card_disabled: {
1818
+ summary: "La tarjeta est\xE1 inhabilitada por el banco emisor.",
1819
+ recommendedAction: "El cliente debe contactar a su banco. Probar con otra tarjeta. Reintentable con otra tarjeta.",
1820
+ retryable: true
1821
+ },
1822
+ cc_rejected_insufficient_amount: {
1823
+ summary: "Saldo insuficiente en la tarjeta.",
1824
+ recommendedAction: "Sugerir tarjeta alternativa o monto menor. Reintentable.",
1825
+ retryable: true
1826
+ },
1827
+ cc_rejected_invalid_installments: {
1828
+ summary: "El emisor no soporta esa cantidad de cuotas para esta tarjeta.",
1829
+ recommendedAction: "Llamar a calculate_installments para ver opciones v\xE1lidas y sugerir otra. Reintentable.",
1830
+ retryable: true
1831
+ },
1832
+ cc_rejected_other_reason: {
1833
+ summary: "Pago rechazado por raz\xF3n no especificada del emisor.",
1834
+ recommendedAction: "Sugerir otra tarjeta o m\xE9todo de pago. Reintentable con otra tarjeta.",
1835
+ retryable: true
1836
+ },
1837
+ // Rejected — NON-RETRYABLE (don't retry the same card)
1838
+ cc_rejected_blacklist: {
1839
+ summary: "Pago rechazado por blacklist de seguridad de MP. NO REINTENTAR con esta tarjeta.",
1840
+ recommendedAction: "Sugerir un m\xE9todo de pago alternativo (account_money, otra tarjeta de otro titular).",
1841
+ retryable: false
1842
+ },
1843
+ cc_rejected_high_risk: {
1844
+ summary: "Rechazo por an\xE1lisis de riesgo de MP. La tarjeta es v\xE1lida pero MP detect\xF3 fraude potencial.",
1845
+ recommendedAction: "Sugerir otro medio de pago o pedirle al cliente que verifique su identidad en MP.",
1846
+ retryable: false
1847
+ },
1848
+ cc_rejected_max_attempts: {
1849
+ summary: "Excedi\xF3 el n\xFAmero m\xE1ximo de intentos con esta tarjeta.",
1850
+ recommendedAction: "Pedir al cliente que use otra tarjeta. NO REINTENTAR la misma.",
1851
+ retryable: false
1852
+ },
1853
+ cc_rejected_duplicated_payment: {
1854
+ summary: "Ya se proces\xF3 un pago id\xE9ntico en los \xFAltimos minutos (deduplicaci\xF3n de MP).",
1855
+ recommendedAction: "Verificar con search_payments si el pago anterior se acredit\xF3. Sin acci\xF3n adicional necesaria.",
1856
+ retryable: false
1857
+ },
1858
+ // Cancelled / refunded / mediation
1859
+ by_collector: {
1860
+ summary: "El vendedor cancel\xF3 el pago.",
1861
+ recommendedAction: "Sin acci\xF3n. Estado final.",
1862
+ retryable: false
1863
+ },
1864
+ by_payer: {
1865
+ summary: "El comprador cancel\xF3 el pago.",
1866
+ recommendedAction: "Sin acci\xF3n. Estado final.",
1867
+ retryable: false
1868
+ },
1869
+ refunded: {
1870
+ summary: "Pago reembolsado al comprador.",
1871
+ recommendedAction: "Estado final. Reflejar el reembolso en tu sistema.",
1872
+ retryable: false
1873
+ },
1874
+ charged_back: {
1875
+ summary: "Contracargo (chargeback) iniciado por el banco emisor.",
1876
+ recommendedAction: "Revisar list_payment_disputes para responder. Surface al equipo de ops.",
1877
+ retryable: false
1878
+ }
1879
+ };
1880
+ function explainPaymentStatus(payment) {
1881
+ const status = payment.status;
1882
+ const statusDetail = payment.status_detail ?? "";
1883
+ const detail = STATUS_DETAIL_MAP[statusDetail];
1884
+ if (detail) {
1885
+ const isFinal = status === "approved" || status === "rejected" || status === "cancelled" || status === "refunded" || status === "charged_back";
1886
+ return {
1887
+ summary: detail.summary,
1888
+ recommendedAction: detail.recommendedAction,
1889
+ final: isFinal,
1890
+ paid: status === "approved",
1891
+ retryable: detail.retryable
1892
+ };
1893
+ }
1894
+ switch (status) {
1895
+ case "approved":
1896
+ return {
1897
+ summary: "Pago aprobado.",
1898
+ recommendedAction: "Continuar con el flujo posterior (env\xEDo, factura, notificaci\xF3n).",
1899
+ final: true,
1900
+ paid: true,
1901
+ retryable: false
1902
+ };
1903
+ case "authorized":
1904
+ return {
1905
+ summary: "Pago autorizado pero no capturado (auth-only).",
1906
+ recommendedAction: "Llamar a capture_payment cuando complet\xE9s el servicio. Vence en 7 d\xEDas si no captur\xE1s.",
1907
+ final: false,
1908
+ paid: false,
1909
+ retryable: false
1910
+ };
1911
+ case "in_process":
1912
+ return {
1913
+ summary: "Pago en proceso.",
1914
+ recommendedAction: "Esperar webhook. Sin acci\xF3n inmediata.",
1915
+ final: false,
1916
+ paid: false,
1917
+ retryable: false
1918
+ };
1919
+ case "in_mediation":
1920
+ return {
1921
+ summary: "Pago en mediaci\xF3n con MP por disputa del comprador.",
1922
+ recommendedAction: "Revisar list_payment_disputes y responder via dashboard.",
1923
+ final: false,
1924
+ paid: false,
1925
+ retryable: false
1926
+ };
1927
+ case "pending":
1928
+ return {
1929
+ summary: "Pago pendiente. El comprador no complet\xF3 el pago todav\xEDa o MP est\xE1 procesando.",
1930
+ recommendedAction: "Esperar webhook (puede demorar minutos a 72h seg\xFAn el m\xE9todo).",
1931
+ final: false,
1932
+ paid: false,
1933
+ retryable: false
1934
+ };
1935
+ case "rejected":
1936
+ return {
1937
+ summary: `Pago rechazado (status_detail: ${statusDetail || "no especificado"}).`,
1938
+ recommendedAction: "Verificar status_detail. Considerar otro m\xE9todo de pago.",
1939
+ final: true,
1940
+ paid: false,
1941
+ retryable: true
1942
+ };
1943
+ case "cancelled":
1944
+ return {
1945
+ summary: "Pago cancelado.",
1946
+ recommendedAction: "Estado final. Sin acci\xF3n.",
1947
+ final: true,
1948
+ paid: false,
1949
+ retryable: false
1950
+ };
1951
+ case "refunded":
1952
+ return {
1953
+ summary: "Pago reembolsado.",
1954
+ recommendedAction: "Estado final. Reflejar el reembolso.",
1955
+ final: true,
1956
+ paid: false,
1957
+ retryable: false
1958
+ };
1959
+ case "charged_back":
1960
+ return {
1961
+ summary: "Pago con contracargo del banco.",
1962
+ recommendedAction: "Surfacear a ops. Revisar disputas.",
1963
+ final: true,
1964
+ paid: false,
1965
+ retryable: false
1966
+ };
1967
+ default:
1968
+ return {
1969
+ summary: `Status no reconocido: '${status}'.`,
1970
+ recommendedAction: "Inspeccionar payment.status + payment.status_detail manualmente.",
1971
+ final: false,
1972
+ paid: false,
1973
+ retryable: false
1974
+ };
1975
+ }
1976
+ }
1977
+
1978
+ // src/test-cards.ts
1979
+ var TEST_CARDS_AR = {
1980
+ VISA_CREDIT: {
1981
+ brand: "Visa (cr\xE9dito)",
1982
+ number: "4509 9535 6623 3704".replace(/\s/g, ""),
1983
+ cvv: "123",
1984
+ exp: "11/30",
1985
+ paymentMethodId: "visa",
1986
+ holderNameToTest: {
1987
+ APRO: "approved",
1988
+ OTHE: "cc_rejected_other_reason",
1989
+ CONT: "pending_contingency",
1990
+ CALL: "cc_rejected_call_for_authorize",
1991
+ FUND: "cc_rejected_insufficient_amount",
1992
+ SECU: "cc_rejected_bad_filled_security_code",
1993
+ EXPI: "cc_rejected_bad_filled_date",
1994
+ FORM: "cc_rejected_bad_filled_other"
1995
+ }
1996
+ },
1997
+ MASTERCARD_CREDIT: {
1998
+ brand: "Mastercard (cr\xE9dito)",
1999
+ number: "5031 7557 3453 0604".replace(/\s/g, ""),
2000
+ cvv: "123",
2001
+ exp: "11/30",
2002
+ paymentMethodId: "master",
2003
+ holderNameToTest: {
2004
+ APRO: "approved",
2005
+ OTHE: "cc_rejected_other_reason",
2006
+ CONT: "pending_contingency",
2007
+ CALL: "cc_rejected_call_for_authorize",
2008
+ FUND: "cc_rejected_insufficient_amount"
2009
+ }
2010
+ },
2011
+ AMEX_CREDIT: {
2012
+ brand: "American Express (cr\xE9dito)",
2013
+ number: "3711 803032 57522".replace(/\s/g, ""),
2014
+ cvv: "1234",
2015
+ exp: "11/30",
2016
+ paymentMethodId: "amex",
2017
+ holderNameToTest: { APRO: "approved", OTHE: "cc_rejected_other_reason" }
2018
+ },
2019
+ VISA_DEBIT: {
2020
+ brand: "Visa (d\xE9bito)",
2021
+ number: "4002 7686 9439 5619".replace(/\s/g, ""),
2022
+ cvv: "123",
2023
+ exp: "11/30",
2024
+ paymentMethodId: "debvisa",
2025
+ holderNameToTest: { APRO: "approved", OTHE: "cc_rejected_other_reason" }
2026
+ },
2027
+ MASTERCARD_DEBIT: {
2028
+ brand: "Mastercard (d\xE9bito)",
2029
+ number: "5287 3383 0125 4634".replace(/\s/g, ""),
2030
+ cvv: "123",
2031
+ exp: "11/30",
2032
+ paymentMethodId: "debmaster",
2033
+ holderNameToTest: { APRO: "approved", OTHE: "cc_rejected_other_reason" }
2034
+ }
2035
+ };
2036
+ var TEST_PAYERS_AR = {
2037
+ approvedBuyer: () => ({
2038
+ email: `test_user_${Date.now()}@testuser.com`,
2039
+ identification: { type: "DNI", number: "12345678" }
2040
+ })
2041
+ };
2042
+ function buildTestCardScenario(cardKey, scenario, amountArs) {
2043
+ const card = TEST_CARDS_AR[cardKey];
2044
+ if (!card) throw new Error(`Unknown test card: ${cardKey}`);
2045
+ if (!card.holderNameToTest[scenario]) {
2046
+ throw new Error(
2047
+ `Card ${cardKey} doesn't define scenario ${scenario}. Available: ${Object.keys(card.holderNameToTest).join(", ")}`
2048
+ );
2049
+ }
2050
+ return {
2051
+ transactionAmount: amountArs,
2052
+ paymentMethodId: card.paymentMethodId,
2053
+ payerEmail: TEST_PAYERS_AR.approvedBuyer().email,
2054
+ description: `TEST ${scenario} via ${cardKey}`,
2055
+ installments: 1,
2056
+ holderName: scenario
2057
+ };
2058
+ }
2059
+
2060
+ // src/three-ds.ts
2061
+ function analyze3DS(payment) {
2062
+ const raw = payment;
2063
+ const mode = raw.three_d_secure_mode ?? null;
2064
+ const statusDetail = payment.status_detail ?? null;
2065
+ if (!mode || mode === "not_supported" || mode === "off") {
2066
+ return {
2067
+ status: "not_required",
2068
+ mode,
2069
+ challengeUrl: null,
2070
+ description: "3DS no fue requerido para este pago (riesgo bajo o emisor sin 3DS habilitado)."
2071
+ };
2072
+ }
2073
+ const threeDsInfo = raw.three_ds_info ?? void 0;
2074
+ if (statusDetail === "pending_challenge" && threeDsInfo?.external_resource_url) {
2075
+ return {
2076
+ status: "challenge_required",
2077
+ mode,
2078
+ challengeUrl: threeDsInfo.external_resource_url,
2079
+ description: "El emisor de la tarjeta requiri\xF3 autenticaci\xF3n 3DS. Redirig\xED al comprador a challengeUrl para completar el desaf\xEDo. El pago queda pending hasta que lo haga."
2080
+ };
2081
+ }
2082
+ if (payment.status === "approved") {
2083
+ return {
2084
+ status: "frictionless",
2085
+ mode,
2086
+ challengeUrl: null,
2087
+ description: "3DS frictionless: el emisor autoriz\xF3 sin desafiar al comprador."
2088
+ };
2089
+ }
2090
+ if (payment.status === "rejected" && typeof statusDetail === "string" && statusDetail.includes("3ds")) {
2091
+ return {
2092
+ status: "rejected",
2093
+ mode,
2094
+ challengeUrl: null,
2095
+ description: `Autenticaci\xF3n 3DS rechazada (${statusDetail}). El comprador debe usar otra tarjeta o validarla con el emisor.`
2096
+ };
2097
+ }
2098
+ return {
2099
+ status: "unknown",
2100
+ mode,
2101
+ challengeUrl: threeDsInfo?.external_resource_url ?? null,
2102
+ description: "No se pudo determinar el estado 3DS \u2014 revisar payment.three_d_secure_mode + payment.status_detail manualmente."
2103
+ };
2104
+ }
1377
2105
  function parseWebhookEvent(body, searchParams) {
1378
2106
  const parseResult = WebhookBodySchema.safeParse(body ?? {});
1379
2107
  const parsedBody = parseResult.success ? parseResult.data : {};
@@ -1475,7 +2203,50 @@ var DEFAULT_DESCRIPTIONS = {
1475
2203
  get_order: "Fetch an Order by ID. Returns the Order with its lifecycle status and any attached payments/refunds.",
1476
2204
  update_order: "Patch an existing Order before it's captured/canceled. Common use: update items or external_reference.",
1477
2205
  capture_order: "Capture a previously-authorized Order (only for orders created with capture_mode='manual'). Captures up to the originally-authorized amount; pass amount for partial capture. Common use: ride-share marks ride complete \u2192 capture; hotel checks-out guest \u2192 capture.",
1478
- cancel_order: "Cancel an Order. Releases any auth-holds and marks the Order as canceled. For orders that have already been CAPTURED, use refund_payment instead \u2014 cancel only works pre-capture."
2206
+ cancel_order: "Cancel an Order. Releases any auth-holds and marks the Order as canceled. For orders that have already been CAPTURED, use refund_payment instead \u2014 cancel only works pre-capture.",
2207
+ // ── Account / Balance / Movements / Settlements (v0.6) ───────────────────
2208
+ get_account_balance: "Get the seller's current MP wallet balance. Returns { available_balance, unavailable_balance, total_amount, currency_id }. The available balance is what the seller can withdraw or pay with right now; unavailable is in retention (typically 14-21 days for new sellers or risk-flagged transactions). For per-seller marketplace setups, instantiate the client AS THE SELLER first.",
2209
+ list_account_movements: "List wallet movements (incoming payments, transfers, refunds, holdings) for the active MP account. Filter by date range with `from`/`to` (ISO 8601). Useful for monthly conciliation or 'show me what came in this month' workflows.",
2210
+ list_settlements: "List settlements (release_money) \u2014 i.e. transfers from the MP wallet to the seller's registered bank account (CBU). USE WHEN the user asks 'cu\xE1ndo me deposita MP' or for monthly bank-conciliation reports. Filter by date range and status.",
2211
+ get_settlement: "Get details of a single settlement: amount, date_scheduled, date_processed, bank_account info (CBU + bank name).",
2212
+ // ── 3DS analyzer (v0.6 — pure) ───────────────────────────────────────────
2213
+ analyze_payment_3ds: "Pure local analyzer for a Payment's 3DS (Strong Customer Authentication) state. Pass a payment_id (string) and the tool fetches the Payment then derives { status: 'not_required'|'frictionless'|'challenge_required'|'rejected'|'unknown', mode, challengeUrl, description }. USE THIS after every create_payment for credit cards: when challengeUrl !== null, you MUST redirect the buyer there before the payment can complete. Without 3DS, payments stay in 'pending' indefinitely if the issuer demanded a challenge.",
2214
+ // ── Test cards (v0.6 — pure) ─────────────────────────────────────────────
2215
+ get_test_cards: "Pure helper that returns the official MP test cards for AR (MLA): VISA/Mastercard/Amex credit + debit, with the 'magic' holder names that route the payment to specific status_detail values (APRO=approved, OTHE=rejected, CONT=pending, FUND=insufficient_amount, etc.). USE WHEN you need to demo a payment flow without a real card, or to script integration tests. Pure data \u2014 no network call.",
2216
+ // ── Customer + Card extensions (v0.7) ────────────────────────────────────
2217
+ get_customer: "Get a customer by id. Returns full Customer object: email, first_name, last_name, identification, address, default_card, registered cards. PURE READ. USE WHEN you have the customer_id from a previous create_customer / find_customer_by_email / payment.payer.id and want the full record.",
2218
+ update_customer: "Update a customer's profile (first_name, last_name, phone, identification, address, default_card). MP merges the patch \u2014 fields you don't send remain unchanged. Use to keep customer records in sync (e.g., shipping address changes) or to set a default card for charge_saved_card.",
2219
+ create_customer_card: "Add a saved card to an existing customer using a card_token (one-time token from MP frontend Cardform \u2014 agents should NEVER take raw card data, that's a PCI violation). Returns the saved CustomerCard with id usable in charge_saved_card. Persists across charges (no need to re-tokenize each time).",
2220
+ get_customer_card: "Get details of a single saved card by (customer_id, card_id). Returns last 4 digits, expiration, brand, issuer. PURE READ \u2014 useful before charge_saved_card to confirm the card is still valid.",
2221
+ // ── Subscription / Plan / Refund / Preference extensions (v0.7) ─────────
2222
+ get_subscription_plan: "Fetch a subscription plan by id. Returns plan config: amount, frequency, status, init_point. Use to inspect a plan before subscribing customers, or to display plan details to the user.",
2223
+ update_subscription: "Update a subscription's amount, status, reason, external_reference, OR card_token_id (to switch payment method when the buyer's card is expired/declined). For card swap: pass card_token_id from a fresh tokenization. CONSTRAINTS: status changes only support 'paused' | 'cancelled' (use authorize via init_point flow to re-activate).",
2224
+ search_subscriptions: "Search subscriptions across the seller's account. Filter by status (pending/authorized/paused/cancelled), payer_email, external_reference, or preapproval_plan_id (to find all subscribers of a plan). Paginated. USE WHEN you need to enumerate active subscribers, audit cancellations, or find a subscription by external reference.",
2225
+ get_refund: "Fetch a single refund by (payment_id, refund_id). Returns the Refund object with amount, status, date_created. PURE READ \u2014 useful to verify a refund processed or to reconcile partial-refund history.",
2226
+ update_payment_preference: "Update a Checkout Pro preference (notification_url, back_urls, items, payer info, payment_methods exclusion list). Only works on preferences NOT yet paid. Common use: regenerate the link with a new notification_url after deployment, or change items if the buyer requested adjustments before paying.",
2227
+ // ── Merchant Orders (v0.7) ────────────────────────────────────────────────
2228
+ get_merchant_order: "Get a merchant_order with all its associated payments + shipments. MerchantOrder is the parent entity for Payments associated with a single Preference \u2014 one Order can have multiple partial Payments (retries, installments). USE THIS in webhooks with topic='merchant_order' to get the aggregate paid_amount, refunded_amount, and shipping status in one call.",
2229
+ search_merchant_orders: "Search merchant_orders by preference_id, external_reference, or status. Paginated. Returns up to 50 per page. USE WHEN you have a preference_id and want all its derived merchant_orders, or when reconciling 'which payments belong to which preference'.",
2230
+ update_merchant_order: "Update a merchant_order \u2014 typically to add items or shipping info. Most agent flows don't need this; use only when integrating with a custom shipping flow that requires updating the MO mid-lifecycle.",
2231
+ // ── Stores + POS CRUD completion (v0.7) ──────────────────────────────────
2232
+ get_store: "Fetch a single store by (user_id, store_id). Returns store details: name, location, business_hours, external_id. PURE READ.",
2233
+ update_store: "Update a store's properties (name, location, business_hours, external_id). MP merges the patch.",
2234
+ delete_store: "Delete a store. IRREVERSIBLE. Confirm with user before calling. Will fail if the store has associated POSes \u2014 delete those first.",
2235
+ get_pos: "Fetch a POS by id. Returns: name, store_id, category, external_id, qr_template (if configured). PURE READ. Use when you need to find the external_id for create_qr_payment.",
2236
+ update_pos: "Update a POS's properties (name, category, external_id). MP merges the patch.",
2237
+ delete_pos: "Delete a POS. IRREVERSIBLE. Cancels any pending QR orders attached to it. Confirm with user before calling.",
2238
+ // ── Bank Accounts (v0.7) ─────────────────────────────────────────────────
2239
+ list_bank_accounts: "List the bank accounts (CBUs) the seller has registered with MP for receiving payouts. Returns an array \u2014 the one with `is_default: true` is where settlements (release_money) go. USE BEFORE list_settlements when the user asks 'a qu\xE9 cuenta me deposita MP'.",
2240
+ register_bank_account: "Register a new bank account (CBU) for the seller. NOTE: MP usually requires this through the dashboard for compliance \u2014 this endpoint may not work for all accounts. If it fails with 403, redirect the user to https://www.mercadopago.com.ar/banking/dashboard.",
2241
+ // ── Point Devices físicos (v0.7) ─────────────────────────────────────────
2242
+ list_point_devices: "List the physical Point devices (Smart, Tap to Pay, etc.) linked to the seller's MP account. Distinct from logical POS \u2014 these are actual terminals at brick-and-mortar shops. Returns each device's id (serial), operating_mode (PDV vs STANDALONE), and pos_id (when bound to a logical POS). Filter by pos_id to find devices for a specific cash register.",
2243
+ update_point_device_mode: "Switch a Point device's operating_mode between 'PDV' (bound to a logical POS, takes payments triggered through that POS) and 'STANDALONE' (works independently, accepts any payment). PDV is for cash-register integrations; STANDALONE is for free-form retail. Affects how payments hit the device.",
2244
+ create_point_payment_intent: "Create a payment intent on a physical Point device \u2014 the device prompts the buyer to tap/insert/swipe their card. Returns immediately with intent_id; query state via get_point_payment_intent or wait for point_integration_wh webhook. **AMOUNT IS IN CENTAVOS**, NOT pesos (Point API differs from Payments API): 100 = $1, 1000 = $10, 10000 = $100.",
2245
+ get_point_payment_intent: "Get the current state of a Point payment intent (OPEN, PROCESSING, FINISHED, CANCELED, ERROR). USE in polling loops if you can't wait for the webhook. When state=FINISHED, the intent.payment.id is the resulting Payment id usable with get_payment.",
2246
+ cancel_point_payment_intent: "Cancel an OPEN point payment intent before the buyer interacts with the device. ONLY WORKS while state='OPEN' \u2014 once the buyer taps, you can't cancel; refund_payment after the fact instead.",
2247
+ // ── Pure helpers (v0.7) ──────────────────────────────────────────────────
2248
+ compute_marketplace_fee: "PURE HELPER (no network) \u2014 given a transaction amount + fee rule (% or flat ARS, with optional min/max floors), returns the exact `marketplace_fee` value in ARS to pass to create_order or create_payment_preference. USE WHEN your platform takes a commission and you need to compute the exact fee per transaction. Examples: { percent: 5, minArs: 50, maxArs: 5000 } for percentage with floor + cap; { flatArs: 200, percent: 2 } for fixed + percentage.",
2249
+ explain_payment_status: "PURE HELPER (no network) \u2014 given a Payment object (from get_payment / create_payment / handle_webhook), returns { summary, recommendedAction, final, paid, retryable } in Spanish. Translates MP's cryptic status_detail codes to plain Spanish + actionable guidance ('reintentar con otra tarjeta' vs 'esperar webhook' vs 'estado final'). USE THIS instead of having to memorize 30+ status_detail codes \u2014 surface summary + recommendedAction directly to the user."
1479
2250
  };
1480
2251
  function mercadoPagoTools(client, options) {
1481
2252
  const desc = (name) => options.descriptions?.[name] ?? DEFAULT_DESCRIPTIONS[name];
@@ -2782,6 +3553,483 @@ function mercadoPagoTools(client, options) {
2782
3553
  status: order.status ?? "canceled"
2783
3554
  };
2784
3555
  }
3556
+ }),
3557
+ // ─────────────────────────────────────────────────────────────────────────
3558
+ // v0.6 — Account / Balance / Movements / Settlements
3559
+ // ─────────────────────────────────────────────────────────────────────────
3560
+ get_account_balance: ai.tool({
3561
+ description: desc("get_account_balance"),
3562
+ inputSchema: zod.z.object({}),
3563
+ execute: async () => {
3564
+ const balance = await client.getAccountBalance();
3565
+ return balance;
3566
+ }
3567
+ }),
3568
+ list_account_movements: ai.tool({
3569
+ description: desc("list_account_movements"),
3570
+ inputSchema: zod.z.object({
3571
+ from: zod.z.string().optional().describe("ISO 8601 start date (e.g. 2026-05-01)"),
3572
+ to: zod.z.string().optional().describe("ISO 8601 end date"),
3573
+ limit: zod.z.number().int().min(1).max(100).optional(),
3574
+ offset: zod.z.number().int().min(0).optional()
3575
+ }),
3576
+ execute: async ({ from, to, limit, offset }) => {
3577
+ const params = {};
3578
+ if (from !== void 0) params.from = from;
3579
+ if (to !== void 0) params.to = to;
3580
+ if (limit !== void 0) params.limit = limit;
3581
+ if (offset !== void 0) params.offset = offset;
3582
+ return client.listAccountMovements(params);
3583
+ }
3584
+ }),
3585
+ list_settlements: ai.tool({
3586
+ description: desc("list_settlements"),
3587
+ inputSchema: zod.z.object({
3588
+ from: zod.z.string().optional(),
3589
+ to: zod.z.string().optional(),
3590
+ status: zod.z.string().optional(),
3591
+ limit: zod.z.number().int().min(1).max(100).optional(),
3592
+ offset: zod.z.number().int().min(0).optional()
3593
+ }),
3594
+ execute: async ({ from, to, status, limit, offset }) => {
3595
+ const params = {};
3596
+ if (from !== void 0) params.from = from;
3597
+ if (to !== void 0) params.to = to;
3598
+ if (status !== void 0) params.status = status;
3599
+ if (limit !== void 0) params.limit = limit;
3600
+ if (offset !== void 0) params.offset = offset;
3601
+ return client.listSettlements(params);
3602
+ }
3603
+ }),
3604
+ get_settlement: ai.tool({
3605
+ description: desc("get_settlement"),
3606
+ inputSchema: zod.z.object({ settlement_id: zod.z.string() }),
3607
+ execute: async ({ settlement_id }) => {
3608
+ return client.getSettlement(settlement_id);
3609
+ }
3610
+ }),
3611
+ // ─────────────────────────────────────────────────────────────────────────
3612
+ // v0.6 — 3DS analyzer (combined: fetch payment + analyze)
3613
+ // ─────────────────────────────────────────────────────────────────────────
3614
+ analyze_payment_3ds: ai.tool({
3615
+ description: desc("analyze_payment_3ds"),
3616
+ inputSchema: zod.z.object({
3617
+ payment_id: zod.z.string().describe("MP payment id")
3618
+ }),
3619
+ execute: async ({ payment_id }) => {
3620
+ const payment = await client.getPayment(payment_id);
3621
+ const info = analyze3DS(payment);
3622
+ return {
3623
+ payment_id,
3624
+ payment_status: payment.status,
3625
+ payment_status_detail: payment.status_detail ?? null,
3626
+ ...info
3627
+ };
3628
+ }
3629
+ }),
3630
+ // ─────────────────────────────────────────────────────────────────────────
3631
+ // v0.6 — Test cards (pure)
3632
+ // ─────────────────────────────────────────────────────────────────────────
3633
+ get_test_cards: ai.tool({
3634
+ description: desc("get_test_cards"),
3635
+ inputSchema: zod.z.object({}),
3636
+ execute: async () => {
3637
+ return {
3638
+ site: "MLA",
3639
+ cards: TEST_CARDS_AR,
3640
+ usage: "Pass holderName='APRO' for an approved payment, 'OTHE' for rejected, 'CONT' for pending, 'FUND' for insufficient amount, 'CALL' for call-for-authorize. Use a NEW payer email per call (append a timestamp) to avoid MP idempotency-on-email deduping."
3641
+ };
3642
+ }
3643
+ }),
3644
+ // ─────────────────────────────────────────────────────────────────────────
3645
+ // v0.7 — Customer + Card extensions
3646
+ // ─────────────────────────────────────────────────────────────────────────
3647
+ get_customer: ai.tool({
3648
+ description: desc("get_customer"),
3649
+ inputSchema: zod.z.object({ customer_id: zod.z.string() }),
3650
+ execute: async ({ customer_id }) => {
3651
+ return client.getCustomer(customer_id);
3652
+ }
3653
+ }),
3654
+ update_customer: ai.tool({
3655
+ description: desc("update_customer"),
3656
+ inputSchema: zod.z.object({
3657
+ customer_id: zod.z.string(),
3658
+ first_name: zod.z.string().optional(),
3659
+ last_name: zod.z.string().optional(),
3660
+ phone: zod.z.object({ area_code: zod.z.string().optional(), number: zod.z.string().optional() }).optional(),
3661
+ identification: zod.z.object({ type: zod.z.string(), number: zod.z.string() }).optional(),
3662
+ address: zod.z.object({
3663
+ street_name: zod.z.string().optional(),
3664
+ street_number: zod.z.number().optional(),
3665
+ zip_code: zod.z.string().optional()
3666
+ }).optional(),
3667
+ description: zod.z.string().optional(),
3668
+ default_card: zod.z.string().optional()
3669
+ }),
3670
+ execute: async ({ customer_id, ...patch }) => {
3671
+ const cleaned = {};
3672
+ for (const [k, v] of Object.entries(patch)) {
3673
+ if (v !== void 0) cleaned[k] = v;
3674
+ }
3675
+ return client.updateCustomer(customer_id, cleaned);
3676
+ }
3677
+ }),
3678
+ create_customer_card: ai.tool({
3679
+ description: desc("create_customer_card"),
3680
+ inputSchema: zod.z.object({
3681
+ customer_id: zod.z.string(),
3682
+ card_token: zod.z.string().describe("Card token from MP frontend Cardform OR create_card_token.")
3683
+ }),
3684
+ execute: async ({ customer_id, card_token }) => {
3685
+ return client.createCustomerCard(customer_id, card_token);
3686
+ }
3687
+ }),
3688
+ get_customer_card: ai.tool({
3689
+ description: desc("get_customer_card"),
3690
+ inputSchema: zod.z.object({
3691
+ customer_id: zod.z.string(),
3692
+ card_id: zod.z.string()
3693
+ }),
3694
+ execute: async ({ customer_id, card_id }) => {
3695
+ return client.getCustomerCard(customer_id, card_id);
3696
+ }
3697
+ }),
3698
+ // ─────────────────────────────────────────────────────────────────────────
3699
+ // v0.7 — Subscription / Plan / Refund / Preference extensions
3700
+ // ─────────────────────────────────────────────────────────────────────────
3701
+ get_subscription_plan: ai.tool({
3702
+ description: desc("get_subscription_plan"),
3703
+ inputSchema: zod.z.object({ plan_id: zod.z.string() }),
3704
+ execute: async ({ plan_id }) => {
3705
+ return client.getSubscriptionPlan(plan_id);
3706
+ }
3707
+ }),
3708
+ update_subscription: ai.tool({
3709
+ description: desc("update_subscription"),
3710
+ inputSchema: zod.z.object({
3711
+ subscription_id: zod.z.string(),
3712
+ transaction_amount: zod.z.number().positive().optional(),
3713
+ card_token_id: zod.z.string().optional(),
3714
+ status: zod.z.enum(["authorized", "paused", "cancelled"]).optional(),
3715
+ reason: zod.z.string().optional(),
3716
+ external_reference: zod.z.string().optional()
3717
+ }),
3718
+ execute: async ({ subscription_id, ...patch }) => {
3719
+ const cleaned = {};
3720
+ for (const [k, v] of Object.entries(patch)) {
3721
+ if (v !== void 0) cleaned[k] = v;
3722
+ }
3723
+ return client.updatePreapproval(subscription_id, cleaned);
3724
+ }
3725
+ }),
3726
+ search_subscriptions: ai.tool({
3727
+ description: desc("search_subscriptions"),
3728
+ inputSchema: zod.z.object({
3729
+ status: zod.z.string().optional(),
3730
+ payer_email: zod.z.string().email().optional(),
3731
+ external_reference: zod.z.string().optional(),
3732
+ plan_id: zod.z.string().optional(),
3733
+ limit: zod.z.number().int().min(1).max(100).optional(),
3734
+ offset: zod.z.number().int().min(0).optional()
3735
+ }),
3736
+ execute: async ({ status, payer_email, external_reference, plan_id, limit, offset }) => {
3737
+ const params = {};
3738
+ if (status !== void 0) params.status = status;
3739
+ if (payer_email !== void 0) params.payerEmail = payer_email;
3740
+ if (external_reference !== void 0) params.externalReference = external_reference;
3741
+ if (plan_id !== void 0) params.preapproval_plan_id = plan_id;
3742
+ if (limit !== void 0) params.limit = limit;
3743
+ if (offset !== void 0) params.offset = offset;
3744
+ return client.searchPreapprovals(params);
3745
+ }
3746
+ }),
3747
+ get_refund: ai.tool({
3748
+ description: desc("get_refund"),
3749
+ inputSchema: zod.z.object({
3750
+ payment_id: zod.z.string(),
3751
+ refund_id: zod.z.string()
3752
+ }),
3753
+ execute: async ({ payment_id, refund_id }) => {
3754
+ return client.getRefund(payment_id, refund_id);
3755
+ }
3756
+ }),
3757
+ update_payment_preference: ai.tool({
3758
+ description: desc("update_payment_preference"),
3759
+ inputSchema: zod.z.object({
3760
+ preference_id: zod.z.string(),
3761
+ notification_url: zod.z.string().url().optional(),
3762
+ external_reference: zod.z.string().optional(),
3763
+ statement_descriptor: zod.z.string().optional()
3764
+ }),
3765
+ execute: async ({ preference_id, ...patch }) => {
3766
+ const cleaned = {};
3767
+ for (const [k, v] of Object.entries(patch)) {
3768
+ if (v !== void 0) cleaned[k] = v;
3769
+ }
3770
+ return client.updatePreference(preference_id, cleaned);
3771
+ }
3772
+ }),
3773
+ // ─────────────────────────────────────────────────────────────────────────
3774
+ // v0.7 — Merchant Orders
3775
+ // ─────────────────────────────────────────────────────────────────────────
3776
+ get_merchant_order: ai.tool({
3777
+ description: desc("get_merchant_order"),
3778
+ inputSchema: zod.z.object({ merchant_order_id: zod.z.string() }),
3779
+ execute: async ({ merchant_order_id }) => {
3780
+ return client.getMerchantOrder(merchant_order_id);
3781
+ }
3782
+ }),
3783
+ search_merchant_orders: ai.tool({
3784
+ description: desc("search_merchant_orders"),
3785
+ inputSchema: zod.z.object({
3786
+ preference_id: zod.z.string().optional(),
3787
+ external_reference: zod.z.string().optional(),
3788
+ status: zod.z.string().optional(),
3789
+ limit: zod.z.number().int().min(1).max(100).optional(),
3790
+ offset: zod.z.number().int().min(0).optional()
3791
+ }),
3792
+ execute: async ({ preference_id, external_reference, status, limit, offset }) => {
3793
+ const params = {};
3794
+ if (preference_id !== void 0) params.preferenceId = preference_id;
3795
+ if (external_reference !== void 0) params.externalReference = external_reference;
3796
+ if (status !== void 0) params.status = status;
3797
+ if (limit !== void 0) params.limit = limit;
3798
+ if (offset !== void 0) params.offset = offset;
3799
+ return client.searchMerchantOrders(params);
3800
+ }
3801
+ }),
3802
+ update_merchant_order: ai.tool({
3803
+ description: desc("update_merchant_order"),
3804
+ inputSchema: zod.z.object({
3805
+ merchant_order_id: zod.z.string(),
3806
+ patch: zod.z.record(zod.z.string(), zod.z.unknown())
3807
+ }),
3808
+ execute: async ({ merchant_order_id, patch }) => {
3809
+ return client.updateMerchantOrder(merchant_order_id, patch);
3810
+ }
3811
+ }),
3812
+ // ─────────────────────────────────────────────────────────────────────────
3813
+ // v0.7 — Stores + POS CRUD completion
3814
+ // ─────────────────────────────────────────────────────────────────────────
3815
+ get_store: ai.tool({
3816
+ description: desc("get_store"),
3817
+ inputSchema: zod.z.object({
3818
+ user_id: zod.z.string(),
3819
+ store_id: zod.z.string()
3820
+ }),
3821
+ execute: async ({ user_id, store_id }) => {
3822
+ return client.getStore(user_id, store_id);
3823
+ }
3824
+ }),
3825
+ update_store: ai.tool({
3826
+ description: desc("update_store"),
3827
+ inputSchema: zod.z.object({
3828
+ user_id: zod.z.string(),
3829
+ store_id: zod.z.string(),
3830
+ name: zod.z.string().optional(),
3831
+ external_id: zod.z.string().optional()
3832
+ }),
3833
+ execute: async ({ user_id, store_id, ...patch }) => {
3834
+ const cleaned = {};
3835
+ for (const [k, v] of Object.entries(patch)) {
3836
+ if (v !== void 0) cleaned[k] = v;
3837
+ }
3838
+ return client.updateStore(user_id, store_id, cleaned);
3839
+ }
3840
+ }),
3841
+ delete_store: ai.tool({
3842
+ description: desc("delete_store"),
3843
+ inputSchema: zod.z.object({
3844
+ user_id: zod.z.string(),
3845
+ store_id: zod.z.string()
3846
+ }),
3847
+ execute: async ({ user_id, store_id }) => {
3848
+ await client.deleteStore(user_id, store_id);
3849
+ return { user_id, store_id, deleted: true };
3850
+ }
3851
+ }),
3852
+ get_pos: ai.tool({
3853
+ description: desc("get_pos"),
3854
+ inputSchema: zod.z.object({ pos_id: zod.z.string() }),
3855
+ execute: async ({ pos_id }) => {
3856
+ return client.getPos(pos_id);
3857
+ }
3858
+ }),
3859
+ update_pos: ai.tool({
3860
+ description: desc("update_pos"),
3861
+ inputSchema: zod.z.object({
3862
+ pos_id: zod.z.string(),
3863
+ name: zod.z.string().optional(),
3864
+ external_id: zod.z.string().optional(),
3865
+ category: zod.z.number().optional()
3866
+ }),
3867
+ execute: async ({ pos_id, ...patch }) => {
3868
+ const cleaned = {};
3869
+ for (const [k, v] of Object.entries(patch)) {
3870
+ if (v !== void 0) cleaned[k] = v;
3871
+ }
3872
+ return client.updatePos(pos_id, cleaned);
3873
+ }
3874
+ }),
3875
+ delete_pos: ai.tool({
3876
+ description: desc("delete_pos"),
3877
+ inputSchema: zod.z.object({ pos_id: zod.z.string() }),
3878
+ execute: async ({ pos_id }) => {
3879
+ await client.deletePos(pos_id);
3880
+ return { pos_id, deleted: true };
3881
+ }
3882
+ }),
3883
+ // ─────────────────────────────────────────────────────────────────────────
3884
+ // v0.7 — Bank Accounts
3885
+ // ─────────────────────────────────────────────────────────────────────────
3886
+ list_bank_accounts: ai.tool({
3887
+ description: desc("list_bank_accounts"),
3888
+ inputSchema: zod.z.object({}),
3889
+ execute: async () => {
3890
+ const accounts = await client.listBankAccounts();
3891
+ return { accounts };
3892
+ }
3893
+ }),
3894
+ register_bank_account: ai.tool({
3895
+ description: desc("register_bank_account"),
3896
+ inputSchema: zod.z.object({
3897
+ cbu: zod.z.string().regex(/^\d{22}$/),
3898
+ alias: zod.z.string().optional()
3899
+ }),
3900
+ execute: async (input) => {
3901
+ const params = {
3902
+ cbu: input.cbu
3903
+ };
3904
+ if (input.alias !== void 0) params.alias = input.alias;
3905
+ return client.registerBankAccount(params);
3906
+ }
3907
+ }),
3908
+ // ─────────────────────────────────────────────────────────────────────────
3909
+ // v0.7 — Point Devices físicos
3910
+ // ─────────────────────────────────────────────────────────────────────────
3911
+ list_point_devices: ai.tool({
3912
+ description: desc("list_point_devices"),
3913
+ inputSchema: zod.z.object({
3914
+ pos_id: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
3915
+ limit: zod.z.number().int().min(1).max(100).optional(),
3916
+ offset: zod.z.number().int().min(0).optional()
3917
+ }),
3918
+ execute: async ({ pos_id, limit, offset }) => {
3919
+ const params = {};
3920
+ if (pos_id !== void 0) params.posId = pos_id;
3921
+ if (limit !== void 0) params.limit = limit;
3922
+ if (offset !== void 0) params.offset = offset;
3923
+ return client.listPointDevices(params);
3924
+ }
3925
+ }),
3926
+ update_point_device_mode: ai.tool({
3927
+ description: desc("update_point_device_mode"),
3928
+ inputSchema: zod.z.object({
3929
+ device_id: zod.z.string(),
3930
+ operating_mode: zod.z.enum(["PDV", "STANDALONE"])
3931
+ }),
3932
+ execute: async ({ device_id, operating_mode }) => {
3933
+ return client.updatePointDeviceOperatingMode(device_id, operating_mode);
3934
+ }
3935
+ }),
3936
+ create_point_payment_intent: ai.tool({
3937
+ description: desc("create_point_payment_intent"),
3938
+ inputSchema: zod.z.object({
3939
+ device_id: zod.z.string(),
3940
+ amount_centavos: zod.z.number().int().positive().describe("Amount in CENTAVOS (NOT pesos). 100 = $1, 1000 = $10, 10000 = $100."),
3941
+ description: zod.z.string().optional(),
3942
+ external_reference: zod.z.string().optional(),
3943
+ installments: zod.z.number().int().min(1).max(24).optional(),
3944
+ installments_cost: zod.z.enum(["seller", "buyer"]).optional(),
3945
+ print_on_terminal: zod.z.boolean().optional(),
3946
+ ticket_number: zod.z.string().optional()
3947
+ }),
3948
+ execute: async (input) => {
3949
+ const params = {
3950
+ amount: input.amount_centavos
3951
+ };
3952
+ if (input.description !== void 0) params.description = input.description;
3953
+ if (input.external_reference !== void 0) params.externalReference = input.external_reference;
3954
+ if (input.installments !== void 0) params.installments = input.installments;
3955
+ if (input.installments_cost !== void 0) params.installmentsCost = input.installments_cost;
3956
+ if (input.print_on_terminal !== void 0) params.printOnTerminal = input.print_on_terminal;
3957
+ if (input.ticket_number !== void 0) params.ticketNumber = input.ticket_number;
3958
+ return client.createPointPaymentIntent(input.device_id, params);
3959
+ }
3960
+ }),
3961
+ get_point_payment_intent: ai.tool({
3962
+ description: desc("get_point_payment_intent"),
3963
+ inputSchema: zod.z.object({ intent_id: zod.z.string() }),
3964
+ execute: async ({ intent_id }) => {
3965
+ return client.getPointPaymentIntent(intent_id);
3966
+ }
3967
+ }),
3968
+ cancel_point_payment_intent: ai.tool({
3969
+ description: desc("cancel_point_payment_intent"),
3970
+ inputSchema: zod.z.object({
3971
+ device_id: zod.z.string(),
3972
+ intent_id: zod.z.string()
3973
+ }),
3974
+ execute: async ({ device_id, intent_id }) => {
3975
+ return client.cancelPointPaymentIntent(device_id, intent_id);
3976
+ }
3977
+ }),
3978
+ // ─────────────────────────────────────────────────────────────────────────
3979
+ // v0.7 — Pure helpers
3980
+ // ─────────────────────────────────────────────────────────────────────────
3981
+ compute_marketplace_fee: ai.tool({
3982
+ description: desc("compute_marketplace_fee"),
3983
+ inputSchema: zod.z.object({
3984
+ amount_ars: zod.z.number().positive(),
3985
+ flat_ars: zod.z.number().nonnegative().optional(),
3986
+ percent: zod.z.number().min(0).max(100).optional(),
3987
+ min_ars: zod.z.number().nonnegative().optional(),
3988
+ max_ars: zod.z.number().nonnegative().optional(),
3989
+ round: zod.z.boolean().optional()
3990
+ }),
3991
+ execute: async (input) => {
3992
+ const rule = {};
3993
+ if (input.flat_ars !== void 0) rule.flatArs = input.flat_ars;
3994
+ if (input.percent !== void 0) rule.percent = input.percent;
3995
+ if (input.min_ars !== void 0) rule.minArs = input.min_ars;
3996
+ if (input.max_ars !== void 0) rule.maxArs = input.max_ars;
3997
+ if (input.round !== void 0) rule.round = input.round;
3998
+ const fee = computeMarketplaceFee(input.amount_ars, rule);
3999
+ return {
4000
+ amount_ars: input.amount_ars,
4001
+ marketplace_fee: fee,
4002
+ seller_receives: input.amount_ars - fee,
4003
+ rule_applied: rule
4004
+ };
4005
+ }
4006
+ }),
4007
+ explain_payment_status: ai.tool({
4008
+ description: desc("explain_payment_status"),
4009
+ inputSchema: zod.z.object({
4010
+ payment_id: zod.z.string().optional().describe("If provided, fetches the Payment first."),
4011
+ payment: zod.z.record(zod.z.string(), zod.z.unknown()).optional().describe("Alternatively, pass a Payment object directly (saves a network round-trip).")
4012
+ }),
4013
+ execute: async ({ payment_id, payment }) => {
4014
+ let p;
4015
+ if (payment) {
4016
+ p = payment;
4017
+ } else if (payment_id) {
4018
+ p = await client.getPayment(payment_id);
4019
+ } else {
4020
+ return {
4021
+ ok: false,
4022
+ error: "Pass either payment_id or payment."
4023
+ };
4024
+ }
4025
+ const explanation = explainPaymentStatus(p);
4026
+ return {
4027
+ ok: true,
4028
+ payment_status: p.status,
4029
+ payment_status_detail: p.status_detail ?? null,
4030
+ ...explanation
4031
+ };
4032
+ }
2785
4033
  })
2786
4034
  };
2787
4035
  }
@@ -2817,10 +4065,16 @@ exports.MercadoPagoPaymentRejectedError = MercadoPagoPaymentRejectedError;
2817
4065
  exports.MercadoPagoRateLimitError = MercadoPagoRateLimitError;
2818
4066
  exports.MercadoPagoSelfPaymentError = MercadoPagoSelfPaymentError;
2819
4067
  exports.MercadoPagoTimeoutError = MercadoPagoTimeoutError;
4068
+ exports.TEST_CARDS_AR = TEST_CARDS_AR;
4069
+ exports.TEST_PAYERS_AR = TEST_PAYERS_AR;
4070
+ exports.analyze3DS = analyze3DS;
2820
4071
  exports.buildAuthorizeUrl = buildAuthorizeUrl;
4072
+ exports.buildTestCardScenario = buildTestCardScenario;
2821
4073
  exports.classifyError = classifyError;
4074
+ exports.computeMarketplaceFee = computeMarketplaceFee;
2822
4075
  exports.exchangeCodeForToken = exchangeCodeForToken;
2823
4076
  exports.expirationTimeMs = expirationTimeMs;
4077
+ exports.explainPaymentStatus = explainPaymentStatus;
2824
4078
  exports.isExpiringSoon = isExpiringSoon;
2825
4079
  exports.mercadoPagoTools = mercadoPagoTools;
2826
4080
  exports.parseWebhookEvent = parseWebhookEvent;