@ar-agents/mercadopago 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +72 -2
- package/CHANGELOG.md +59 -0
- package/README.md +74 -10
- package/dist/index.cjs +1924 -1089
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +560 -2
- package/dist/index.d.ts +560 -2
- package/dist/index.js +1916 -1090
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/tools.manifest.json +70 -1
package/dist/index.js
CHANGED
|
@@ -505,6 +505,9 @@ var MercadoPagoClient = class {
|
|
|
505
505
|
if (params.expires !== void 0) body.expires = params.expires;
|
|
506
506
|
if (params.expirationDateFrom) body.expiration_date_from = params.expirationDateFrom;
|
|
507
507
|
if (params.expirationDateTo) body.expiration_date_to = params.expirationDateTo;
|
|
508
|
+
if (params.marketplace) body.marketplace = params.marketplace;
|
|
509
|
+
if (params.marketplaceFee !== void 0) body.marketplace_fee = params.marketplaceFee;
|
|
510
|
+
if (params.collectorId !== void 0) body.collector_id = params.collectorId;
|
|
508
511
|
return this.request("POST", "/checkout/preferences", body);
|
|
509
512
|
}
|
|
510
513
|
async getPreference(id) {
|
|
@@ -894,1081 +897,132 @@ var MercadoPagoClient = class {
|
|
|
894
897
|
async deleteWebhook(id) {
|
|
895
898
|
await this.request("DELETE", `/v1/webhooks/${id}`);
|
|
896
899
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
//
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
//
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}),
|
|
1024
|
-
cancel_subscription: tool({
|
|
1025
|
-
description: desc("cancel_subscription"),
|
|
1026
|
-
inputSchema: z.object({
|
|
1027
|
-
subscription_id: z.string().describe("The Mercado Pago subscription/preapproval ID to cancel")
|
|
1028
|
-
}),
|
|
1029
|
-
execute: async ({ subscription_id }) => {
|
|
1030
|
-
const cancelled = await client.cancelPreapproval(subscription_id);
|
|
1031
|
-
await options.state.set(subscription_id, {
|
|
1032
|
-
status: cancelled.status,
|
|
1033
|
-
cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1034
|
-
});
|
|
1035
|
-
return {
|
|
1036
|
-
subscription_id: cancelled.id,
|
|
1037
|
-
status: cancelled.status,
|
|
1038
|
-
message: "Subscription cancelled. No further charges will occur."
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
}),
|
|
1042
|
-
pause_subscription: tool({
|
|
1043
|
-
description: desc("pause_subscription"),
|
|
1044
|
-
inputSchema: z.object({ subscription_id: z.string() }),
|
|
1045
|
-
execute: async ({ subscription_id }) => {
|
|
1046
|
-
const paused = await client.pausePreapproval(subscription_id);
|
|
1047
|
-
await options.state.set(subscription_id, { status: paused.status });
|
|
1048
|
-
return {
|
|
1049
|
-
subscription_id: paused.id,
|
|
1050
|
-
status: paused.status,
|
|
1051
|
-
message: "Subscription paused. Use resume_subscription to reactivate."
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
}),
|
|
1055
|
-
resume_subscription: tool({
|
|
1056
|
-
description: desc("resume_subscription"),
|
|
1057
|
-
inputSchema: z.object({ subscription_id: z.string() }),
|
|
1058
|
-
execute: async ({ subscription_id }) => {
|
|
1059
|
-
const resumed = await client.resumePreapproval(subscription_id);
|
|
1060
|
-
await options.state.set(subscription_id, { status: resumed.status });
|
|
1061
|
-
return {
|
|
1062
|
-
subscription_id: resumed.id,
|
|
1063
|
-
status: resumed.status,
|
|
1064
|
-
message: "Subscription resumed. Charges will continue on next scheduled date."
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
|
-
}),
|
|
1068
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1069
|
-
// Payments (v0.2)
|
|
1070
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1071
|
-
create_payment: tool({
|
|
1072
|
-
description: desc("create_payment"),
|
|
1073
|
-
inputSchema: z.object({
|
|
1074
|
-
amount_ars: z.number().positive().describe("Amount in ARS"),
|
|
1075
|
-
payment_method_id: z.string().describe("MP payment method id (e.g. 'account_money', 'rapipago', 'visa', 'master', 'naranja')"),
|
|
1076
|
-
payer_email: z.string().email().describe("Email of the payer. Cannot equal seller email."),
|
|
1077
|
-
token: z.string().optional().describe("Card token from MP frontend Cardform. Required for credit/debit; omit for cash/account_money."),
|
|
1078
|
-
installments: z.number().int().min(1).max(24).optional().describe("Number of installments (cuotas). Default 1. Use calculate_installments first to see options."),
|
|
1079
|
-
description: z.string().max(255).optional().describe("Short description"),
|
|
1080
|
-
external_reference: z.string().optional().describe("Your-system identifier"),
|
|
1081
|
-
identification: z.object({
|
|
1082
|
-
type: z.enum(["DNI", "CUIT", "CUIL"]),
|
|
1083
|
-
number: z.string()
|
|
1084
|
-
}).optional().describe("Payer identification \u2014 required for some payment types in AR"),
|
|
1085
|
-
statement_descriptor: z.string().max(13).optional().describe("Shows on buyer's card statement (max 13 chars)")
|
|
1086
|
-
}),
|
|
1087
|
-
execute: async (input) => {
|
|
1088
|
-
const payment = await client.createPayment({
|
|
1089
|
-
transactionAmount: input.amount_ars,
|
|
1090
|
-
paymentMethodId: input.payment_method_id,
|
|
1091
|
-
payerEmail: input.payer_email,
|
|
1092
|
-
...input.token !== void 0 ? { token: input.token } : {},
|
|
1093
|
-
...input.installments !== void 0 ? { installments: input.installments } : {},
|
|
1094
|
-
...input.description !== void 0 ? { description: input.description } : {},
|
|
1095
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1096
|
-
...input.identification !== void 0 ? { identification: input.identification } : {},
|
|
1097
|
-
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
1098
|
-
...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
|
|
1099
|
-
// Deterministic idempotency key — safe to retry, same inputs always
|
|
1100
|
-
// produce the same key (MP dedupes on its side).
|
|
1101
|
-
idempotencyKey: deterministicIdempotencyKey(
|
|
1102
|
-
"create_payment",
|
|
1103
|
-
input.external_reference ?? input.payer_email,
|
|
1104
|
-
input.amount_ars,
|
|
1105
|
-
input.payment_method_id,
|
|
1106
|
-
input.token
|
|
1107
|
-
)
|
|
1108
|
-
});
|
|
1109
|
-
return {
|
|
1110
|
-
payment_id: payment.id,
|
|
1111
|
-
status: payment.status,
|
|
1112
|
-
status_detail: payment.status_detail,
|
|
1113
|
-
amount: payment.transaction_amount,
|
|
1114
|
-
currency: payment.currency_id,
|
|
1115
|
-
installments: payment.installments,
|
|
1116
|
-
payment_method: payment.payment_method_id,
|
|
1117
|
-
payer_email: payment.payer?.email ?? null,
|
|
1118
|
-
external_reference: payment.external_reference,
|
|
1119
|
-
date_created: payment.date_created,
|
|
1120
|
-
date_approved: payment.date_approved
|
|
1121
|
-
};
|
|
1122
|
-
}
|
|
1123
|
-
}),
|
|
1124
|
-
get_payment: tool({
|
|
1125
|
-
description: desc("get_payment"),
|
|
1126
|
-
inputSchema: z.object({
|
|
1127
|
-
payment_id: z.string().describe("The MP payment ID")
|
|
1128
|
-
}),
|
|
1129
|
-
execute: async ({ payment_id }) => {
|
|
1130
|
-
const p = await client.getPayment(payment_id);
|
|
1131
|
-
return {
|
|
1132
|
-
payment_id: p.id,
|
|
1133
|
-
status: p.status,
|
|
1134
|
-
status_detail: p.status_detail,
|
|
1135
|
-
amount: p.transaction_amount,
|
|
1136
|
-
currency: p.currency_id,
|
|
1137
|
-
payment_method: p.payment_method_id,
|
|
1138
|
-
installments: p.installments,
|
|
1139
|
-
payer_email: p.payer?.email ?? null,
|
|
1140
|
-
external_reference: p.external_reference,
|
|
1141
|
-
date_created: p.date_created,
|
|
1142
|
-
date_approved: p.date_approved,
|
|
1143
|
-
net_received: p.transaction_details?.net_received_amount ?? null
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
}),
|
|
1147
|
-
search_payments: tool({
|
|
1148
|
-
description: desc("search_payments"),
|
|
1149
|
-
inputSchema: z.object({
|
|
1150
|
-
external_reference: z.string().optional(),
|
|
1151
|
-
status: z.string().optional().describe("'approved' | 'pending' | 'rejected' | 'cancelled' | 'refunded' etc."),
|
|
1152
|
-
payer_email: z.string().optional(),
|
|
1153
|
-
begin_date: z.string().optional().describe("ISO 8601, e.g. 2026-01-01T00:00:00Z"),
|
|
1154
|
-
end_date: z.string().optional().describe("ISO 8601"),
|
|
1155
|
-
limit: z.number().int().min(1).max(100).optional().describe("Default 30, max 100"),
|
|
1156
|
-
offset: z.number().int().min(0).optional().describe("Pagination offset (default 0)")
|
|
1157
|
-
}),
|
|
1158
|
-
execute: async (input) => {
|
|
1159
|
-
const result = await client.searchPayments({
|
|
1160
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1161
|
-
...input.status !== void 0 ? { status: input.status } : {},
|
|
1162
|
-
...input.payer_email !== void 0 ? { payerEmail: input.payer_email } : {},
|
|
1163
|
-
...input.begin_date !== void 0 ? { beginDate: input.begin_date } : {},
|
|
1164
|
-
...input.end_date !== void 0 ? { endDate: input.end_date } : {},
|
|
1165
|
-
...input.limit !== void 0 ? { limit: input.limit } : {},
|
|
1166
|
-
...input.offset !== void 0 ? { offset: input.offset } : {}
|
|
1167
|
-
});
|
|
1168
|
-
return {
|
|
1169
|
-
total: result.paging.total,
|
|
1170
|
-
returned: result.results.length,
|
|
1171
|
-
offset: result.paging.offset,
|
|
1172
|
-
payments: result.results.map((p) => ({
|
|
1173
|
-
payment_id: p.id,
|
|
1174
|
-
status: p.status,
|
|
1175
|
-
amount: p.transaction_amount,
|
|
1176
|
-
currency: p.currency_id,
|
|
1177
|
-
payer_email: p.payer?.email ?? null,
|
|
1178
|
-
external_reference: p.external_reference,
|
|
1179
|
-
date_created: p.date_created
|
|
1180
|
-
}))
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
}),
|
|
1184
|
-
cancel_payment: tool({
|
|
1185
|
-
description: desc("cancel_payment"),
|
|
1186
|
-
inputSchema: z.object({ payment_id: z.string() }),
|
|
1187
|
-
execute: async ({ payment_id }) => {
|
|
1188
|
-
const cancelled = await client.cancelPayment(payment_id);
|
|
1189
|
-
return {
|
|
1190
|
-
payment_id: cancelled.id,
|
|
1191
|
-
status: cancelled.status,
|
|
1192
|
-
message: "Payment cancelled. If it was already approved, use refund_payment instead."
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
}),
|
|
1196
|
-
capture_payment: tool({
|
|
1197
|
-
description: desc("capture_payment"),
|
|
1198
|
-
inputSchema: z.object({
|
|
1199
|
-
payment_id: z.string(),
|
|
1200
|
-
amount_ars: z.number().positive().optional().describe("Optional partial-capture amount. Omit to capture full authorized amount.")
|
|
1201
|
-
}),
|
|
1202
|
-
execute: async ({ payment_id, amount_ars }) => {
|
|
1203
|
-
const captured = await client.capturePayment(payment_id, amount_ars);
|
|
1204
|
-
return {
|
|
1205
|
-
payment_id: captured.id,
|
|
1206
|
-
status: captured.status,
|
|
1207
|
-
amount: captured.transaction_amount
|
|
1208
|
-
};
|
|
1209
|
-
}
|
|
1210
|
-
}),
|
|
1211
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1212
|
-
// Refunds
|
|
1213
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1214
|
-
refund_payment: tool({
|
|
1215
|
-
description: desc("refund_payment"),
|
|
1216
|
-
inputSchema: z.object({
|
|
1217
|
-
payment_id: z.string(),
|
|
1218
|
-
amount_ars: z.number().positive().optional().describe("Partial-refund amount in ARS. Omit for full refund.")
|
|
1219
|
-
}),
|
|
1220
|
-
execute: async ({ payment_id, amount_ars }) => {
|
|
1221
|
-
const refund = await client.createRefund({
|
|
1222
|
-
paymentId: payment_id,
|
|
1223
|
-
...amount_ars !== void 0 ? { amount: amount_ars } : {},
|
|
1224
|
-
idempotencyKey: deterministicIdempotencyKey("refund", payment_id, amount_ars ?? "full")
|
|
1225
|
-
});
|
|
1226
|
-
return {
|
|
1227
|
-
refund_id: refund.id,
|
|
1228
|
-
payment_id: refund.payment_id,
|
|
1229
|
-
amount: refund.amount,
|
|
1230
|
-
status: refund.status,
|
|
1231
|
-
message: amount_ars === void 0 ? "Full refund issued. Funds return to the buyer in 3-10 business days." : `Partial refund of ${amount_ars} ARS issued.`
|
|
1232
|
-
};
|
|
1233
|
-
}
|
|
1234
|
-
}),
|
|
1235
|
-
list_refunds: tool({
|
|
1236
|
-
description: desc("list_refunds"),
|
|
1237
|
-
inputSchema: z.object({ payment_id: z.string() }),
|
|
1238
|
-
execute: async ({ payment_id }) => {
|
|
1239
|
-
const refunds = await client.listRefunds(payment_id);
|
|
1240
|
-
return {
|
|
1241
|
-
payment_id,
|
|
1242
|
-
count: refunds.length,
|
|
1243
|
-
refunds: refunds.map((r) => ({
|
|
1244
|
-
refund_id: r.id,
|
|
1245
|
-
amount: r.amount,
|
|
1246
|
-
status: r.status,
|
|
1247
|
-
date_created: r.date_created
|
|
1248
|
-
}))
|
|
1249
|
-
};
|
|
1250
|
-
}
|
|
1251
|
-
}),
|
|
1252
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1253
|
-
// Checkout Pro
|
|
1254
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1255
|
-
create_payment_preference: tool({
|
|
1256
|
-
description: desc("create_payment_preference"),
|
|
1257
|
-
inputSchema: z.object({
|
|
1258
|
-
items: z.array(z.object({
|
|
1259
|
-
title: z.string().min(1).max(256),
|
|
1260
|
-
quantity: z.number().int().positive(),
|
|
1261
|
-
unit_price: z.number().positive(),
|
|
1262
|
-
description: z.string().optional(),
|
|
1263
|
-
picture_url: z.string().url().optional()
|
|
1264
|
-
})).min(1).describe("Items being charged. At least one required."),
|
|
1265
|
-
payer_email: z.string().email().optional().describe("Pre-fill the payer email on Checkout Pro form"),
|
|
1266
|
-
external_reference: z.string().optional(),
|
|
1267
|
-
max_installments: z.number().int().min(1).max(24).optional().describe("Limit max cuotas offered. Defaults to MP account config."),
|
|
1268
|
-
statement_descriptor: z.string().max(13).optional(),
|
|
1269
|
-
excluded_payment_types: z.array(z.enum(["credit_card", "debit_card", "ticket", "atm", "bank_transfer"])).optional().describe("Block payment types \u2014 e.g., ['ticket'] to disable Rapipago/Pago F\xE1cil")
|
|
1270
|
-
}),
|
|
1271
|
-
execute: async (input) => {
|
|
1272
|
-
const pref = await client.createPreference({
|
|
1273
|
-
items: input.items.map((it) => ({
|
|
1274
|
-
title: it.title,
|
|
1275
|
-
quantity: it.quantity,
|
|
1276
|
-
unit_price: it.unit_price,
|
|
1277
|
-
currency_id: "ARS",
|
|
1278
|
-
...it.description !== void 0 ? { description: it.description } : {},
|
|
1279
|
-
...it.picture_url !== void 0 ? { picture_url: it.picture_url } : {}
|
|
1280
|
-
})),
|
|
1281
|
-
...input.payer_email !== void 0 ? { payer: { email: input.payer_email } } : {},
|
|
1282
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1283
|
-
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
1284
|
-
backUrls: { success: options.backUrl, failure: options.backUrl, pending: options.backUrl },
|
|
1285
|
-
autoReturn: "approved",
|
|
1286
|
-
...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
|
|
1287
|
-
...input.max_installments !== void 0 || input.excluded_payment_types !== void 0 ? {
|
|
1288
|
-
paymentMethods: {
|
|
1289
|
-
...input.max_installments !== void 0 ? { installments: input.max_installments } : {},
|
|
1290
|
-
...input.excluded_payment_types !== void 0 ? { excluded_payment_types: input.excluded_payment_types.map((id) => ({ id })) } : {}
|
|
1291
|
-
}
|
|
1292
|
-
} : {}
|
|
1293
|
-
});
|
|
1294
|
-
return {
|
|
1295
|
-
preference_id: pref.id,
|
|
1296
|
-
init_point_url: pref.init_point ?? null,
|
|
1297
|
-
sandbox_init_point_url: pref.sandbox_init_point ?? null,
|
|
1298
|
-
external_reference: pref.external_reference,
|
|
1299
|
-
date_created: pref.date_created,
|
|
1300
|
-
next_step: "Send init_point_url (or sandbox_init_point_url in sandbox) to the customer. After they pay, MP fires a webhook with the payment_id; use get_payment to confirm status."
|
|
1301
|
-
};
|
|
1302
|
-
}
|
|
1303
|
-
}),
|
|
1304
|
-
get_payment_preference: tool({
|
|
1305
|
-
description: desc("get_payment_preference"),
|
|
1306
|
-
inputSchema: z.object({ preference_id: z.string() }),
|
|
1307
|
-
execute: async ({ preference_id }) => {
|
|
1308
|
-
const pref = await client.getPreference(preference_id);
|
|
1309
|
-
return {
|
|
1310
|
-
preference_id: pref.id,
|
|
1311
|
-
init_point_url: pref.init_point ?? null,
|
|
1312
|
-
sandbox_init_point_url: pref.sandbox_init_point ?? null,
|
|
1313
|
-
external_reference: pref.external_reference,
|
|
1314
|
-
items: pref.items,
|
|
1315
|
-
date_created: pref.date_created
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
}),
|
|
1319
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1320
|
-
// Customers + Saved Cards
|
|
1321
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1322
|
-
create_customer: tool({
|
|
1323
|
-
description: desc("create_customer"),
|
|
1324
|
-
inputSchema: z.object({
|
|
1325
|
-
email: z.string().email(),
|
|
1326
|
-
first_name: z.string().optional(),
|
|
1327
|
-
last_name: z.string().optional(),
|
|
1328
|
-
identification: z.object({
|
|
1329
|
-
type: z.enum(["DNI", "CUIT", "CUIL"]),
|
|
1330
|
-
number: z.string()
|
|
1331
|
-
}).optional(),
|
|
1332
|
-
description: z.string().optional()
|
|
1333
|
-
}),
|
|
1334
|
-
execute: async (input) => {
|
|
1335
|
-
const customer = await client.createCustomer({
|
|
1336
|
-
email: input.email,
|
|
1337
|
-
...input.first_name !== void 0 ? { firstName: input.first_name } : {},
|
|
1338
|
-
...input.last_name !== void 0 ? { lastName: input.last_name } : {},
|
|
1339
|
-
...input.identification !== void 0 ? { identification: input.identification } : {},
|
|
1340
|
-
...input.description !== void 0 ? { description: input.description } : {}
|
|
1341
|
-
});
|
|
1342
|
-
return {
|
|
1343
|
-
customer_id: customer.id,
|
|
1344
|
-
email: customer.email,
|
|
1345
|
-
first_name: customer.first_name,
|
|
1346
|
-
last_name: customer.last_name,
|
|
1347
|
-
date_created: customer.date_created
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
}),
|
|
1351
|
-
find_customer_by_email: tool({
|
|
1352
|
-
description: desc("find_customer_by_email"),
|
|
1353
|
-
inputSchema: z.object({ email: z.string().email() }),
|
|
1354
|
-
execute: async ({ email }) => {
|
|
1355
|
-
const result = await client.searchCustomers({ email, limit: 1 });
|
|
1356
|
-
const customer = result.results[0] ?? null;
|
|
1357
|
-
return customer ? {
|
|
1358
|
-
found: true,
|
|
1359
|
-
customer_id: customer.id,
|
|
1360
|
-
email: customer.email,
|
|
1361
|
-
first_name: customer.first_name,
|
|
1362
|
-
last_name: customer.last_name
|
|
1363
|
-
} : { found: false, customer_id: null };
|
|
1364
|
-
}
|
|
1365
|
-
}),
|
|
1366
|
-
list_customer_cards: tool({
|
|
1367
|
-
description: desc("list_customer_cards"),
|
|
1368
|
-
inputSchema: z.object({ customer_id: z.string() }),
|
|
1369
|
-
execute: async ({ customer_id }) => {
|
|
1370
|
-
const cards = await client.listCustomerCards(customer_id);
|
|
1371
|
-
return {
|
|
1372
|
-
customer_id,
|
|
1373
|
-
count: cards.length,
|
|
1374
|
-
cards: cards.map((c) => ({
|
|
1375
|
-
card_id: c.id,
|
|
1376
|
-
last_four_digits: c.last_four_digits,
|
|
1377
|
-
expiration_month: c.expiration_month,
|
|
1378
|
-
expiration_year: c.expiration_year,
|
|
1379
|
-
payment_method: c.payment_method?.id ?? null,
|
|
1380
|
-
payment_method_name: c.payment_method?.name ?? null
|
|
1381
|
-
}))
|
|
1382
|
-
};
|
|
1383
|
-
}
|
|
1384
|
-
}),
|
|
1385
|
-
delete_customer_card: tool({
|
|
1386
|
-
description: desc("delete_customer_card"),
|
|
1387
|
-
inputSchema: z.object({
|
|
1388
|
-
customer_id: z.string(),
|
|
1389
|
-
card_id: z.string()
|
|
1390
|
-
}),
|
|
1391
|
-
execute: async ({ customer_id, card_id }) => {
|
|
1392
|
-
await client.deleteCustomerCard(customer_id, card_id);
|
|
1393
|
-
return { customer_id, card_id, deleted: true };
|
|
1394
|
-
}
|
|
1395
|
-
}),
|
|
1396
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1397
|
-
// Payment Methods + Installments
|
|
1398
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1399
|
-
list_payment_methods: tool({
|
|
1400
|
-
description: desc("list_payment_methods"),
|
|
1401
|
-
inputSchema: z.object({}),
|
|
1402
|
-
execute: async () => {
|
|
1403
|
-
const methods = await client.listPaymentMethods();
|
|
1404
|
-
return {
|
|
1405
|
-
count: methods.length,
|
|
1406
|
-
methods: methods.map((m) => ({
|
|
1407
|
-
id: m.id,
|
|
1408
|
-
name: m.name,
|
|
1409
|
-
payment_type: m.payment_type_id,
|
|
1410
|
-
status: m.status,
|
|
1411
|
-
min_amount: m.min_allowed_amount,
|
|
1412
|
-
max_amount: m.max_allowed_amount
|
|
1413
|
-
}))
|
|
1414
|
-
};
|
|
1415
|
-
}
|
|
1416
|
-
}),
|
|
1417
|
-
calculate_installments: tool({
|
|
1418
|
-
description: desc("calculate_installments"),
|
|
1419
|
-
inputSchema: z.object({
|
|
1420
|
-
amount_ars: z.number().positive(),
|
|
1421
|
-
payment_method_id: z.string().optional().describe("E.g. 'visa', 'master', 'naranja'. Omit for all available methods."),
|
|
1422
|
-
bin: z.string().min(6).max(8).optional().describe("First 6-8 digits of card for issuer-specific offers (e.g., Naranja interest-free promotions)")
|
|
1423
|
-
}),
|
|
1424
|
-
execute: async (input) => {
|
|
1425
|
-
const offers = await client.getInstallments({
|
|
1426
|
-
amount: input.amount_ars,
|
|
1427
|
-
...input.payment_method_id !== void 0 ? { paymentMethodId: input.payment_method_id } : {},
|
|
1428
|
-
...input.bin !== void 0 ? { bin: input.bin } : {}
|
|
1429
|
-
});
|
|
1430
|
-
return {
|
|
1431
|
-
amount: input.amount_ars,
|
|
1432
|
-
offers: offers.map((o) => ({
|
|
1433
|
-
payment_method_id: o.payment_method_id,
|
|
1434
|
-
payment_type_id: o.payment_type_id,
|
|
1435
|
-
issuer_name: o.issuer?.name ?? null,
|
|
1436
|
-
options: o.payer_costs.map((pc) => ({
|
|
1437
|
-
installments: pc.installments,
|
|
1438
|
-
installment_amount: pc.installment_amount,
|
|
1439
|
-
total_amount: pc.total_amount,
|
|
1440
|
-
installment_rate: pc.installment_rate,
|
|
1441
|
-
recommended_message: pc.recommended_message
|
|
1442
|
-
}))
|
|
1443
|
-
}))
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
}),
|
|
1447
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1448
|
-
// Account
|
|
1449
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1450
|
-
get_account_info: tool({
|
|
1451
|
-
description: desc("get_account_info"),
|
|
1452
|
-
inputSchema: z.object({}),
|
|
1453
|
-
execute: async () => {
|
|
1454
|
-
const me = await client.getMe();
|
|
1455
|
-
return {
|
|
1456
|
-
account_id: me.id,
|
|
1457
|
-
email: me.email,
|
|
1458
|
-
nickname: me.nickname,
|
|
1459
|
-
country_id: me.country_id,
|
|
1460
|
-
site_id: me.site_id,
|
|
1461
|
-
user_type: me.user_type
|
|
1462
|
-
};
|
|
1463
|
-
}
|
|
1464
|
-
}),
|
|
1465
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1466
|
-
// Saved-card charging (v0.3)
|
|
1467
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1468
|
-
charge_saved_card: tool({
|
|
1469
|
-
description: desc("charge_saved_card"),
|
|
1470
|
-
inputSchema: z.object({
|
|
1471
|
-
customer_id: z.string().describe("MP customer id (from create_customer / find_customer_by_email)"),
|
|
1472
|
-
card_id: z.string().describe("Saved card id (from list_customer_cards)"),
|
|
1473
|
-
security_code: z.string().regex(/^\d{3,4}$/).describe("CVV \u2014 3 digits (Visa/Master) or 4 (Amex). User must provide this each charge in AR."),
|
|
1474
|
-
amount_ars: z.number().positive(),
|
|
1475
|
-
description: z.string().min(1).max(255),
|
|
1476
|
-
installments: z.number().int().min(1).max(24).optional().describe("Default 1. Use calculate_installments first to pick a valid count."),
|
|
1477
|
-
external_reference: z.string().optional(),
|
|
1478
|
-
statement_descriptor: z.string().max(13).optional()
|
|
1479
|
-
}),
|
|
1480
|
-
execute: async (input) => {
|
|
1481
|
-
const payment = await client.chargeSavedCard({
|
|
1482
|
-
customerId: input.customer_id,
|
|
1483
|
-
cardId: input.card_id,
|
|
1484
|
-
securityCode: input.security_code,
|
|
1485
|
-
amount: input.amount_ars,
|
|
1486
|
-
description: input.description,
|
|
1487
|
-
...input.installments !== void 0 ? { installments: input.installments } : {},
|
|
1488
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1489
|
-
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
1490
|
-
idempotencyKey: deterministicIdempotencyKey(
|
|
1491
|
-
"charge_saved_card",
|
|
1492
|
-
input.card_id,
|
|
1493
|
-
input.amount_ars,
|
|
1494
|
-
input.external_reference
|
|
1495
|
-
)
|
|
1496
|
-
});
|
|
1497
|
-
return {
|
|
1498
|
-
payment_id: payment.id,
|
|
1499
|
-
status: payment.status,
|
|
1500
|
-
status_detail: payment.status_detail,
|
|
1501
|
-
amount: payment.transaction_amount,
|
|
1502
|
-
installments: payment.installments,
|
|
1503
|
-
payment_method: payment.payment_method_id,
|
|
1504
|
-
customer_id: input.customer_id,
|
|
1505
|
-
card_id: input.card_id,
|
|
1506
|
-
external_reference: payment.external_reference,
|
|
1507
|
-
date_approved: payment.date_approved
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
}),
|
|
1511
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1512
|
-
// QR in-store (v0.3)
|
|
1513
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1514
|
-
create_qr_payment: tool({
|
|
1515
|
-
description: desc("create_qr_payment"),
|
|
1516
|
-
inputSchema: z.object({
|
|
1517
|
-
external_pos_id: z.string().describe("Pre-configured POS external_id from MP dashboard. Required."),
|
|
1518
|
-
amount_ars: z.number().positive(),
|
|
1519
|
-
title: z.string().min(1).max(80).describe("Display title shown when scanning"),
|
|
1520
|
-
description: z.string().max(255).optional(),
|
|
1521
|
-
external_reference: z.string().optional(),
|
|
1522
|
-
notification_url: z.string().url().optional().describe("Webhook URL \u2014 falls back to dashboard config if omitted"),
|
|
1523
|
-
expires_in_seconds: z.number().int().min(60).max(3600).optional().describe("Default 600 (10 min)")
|
|
1524
|
-
}),
|
|
1525
|
-
execute: async (input) => {
|
|
1526
|
-
const QRCode = (await import('qrcode')).default;
|
|
1527
|
-
const me = await client.getMe();
|
|
1528
|
-
const userId = String(me.id);
|
|
1529
|
-
const expiresAt = new Date(
|
|
1530
|
-
Date.now() + (input.expires_in_seconds ?? 600) * 1e3
|
|
1531
|
-
).toISOString();
|
|
1532
|
-
const qr = await client.createQrPayment(userId, {
|
|
1533
|
-
externalPosId: input.external_pos_id,
|
|
1534
|
-
totalAmount: input.amount_ars,
|
|
1535
|
-
title: input.title,
|
|
1536
|
-
...input.description !== void 0 ? { description: input.description } : {},
|
|
1537
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1538
|
-
...input.notification_url !== void 0 ? { notificationUrl: input.notification_url } : {},
|
|
1539
|
-
expirationDate: expiresAt
|
|
1540
|
-
});
|
|
1541
|
-
const qrDataUrl = await QRCode.toDataURL(qr.qr_data, {
|
|
1542
|
-
errorCorrectionLevel: "M",
|
|
1543
|
-
margin: 1,
|
|
1544
|
-
width: 512
|
|
1545
|
-
});
|
|
1546
|
-
return {
|
|
1547
|
-
in_store_order_id: qr.in_store_order_id,
|
|
1548
|
-
qr_data: qr.qr_data,
|
|
1549
|
-
qr_data_url: qrDataUrl,
|
|
1550
|
-
expires_at: expiresAt,
|
|
1551
|
-
external_pos_id: input.external_pos_id,
|
|
1552
|
-
amount: input.amount_ars,
|
|
1553
|
-
next_step: "Display the qr_data_url image to the buyer. Wait for the payment webhook (point_integration_wh fires first, then payment topic). If buyer doesn't scan in time, call cancel_qr_payment to free the POS."
|
|
1554
|
-
};
|
|
1555
|
-
}
|
|
1556
|
-
}),
|
|
1557
|
-
cancel_qr_payment: tool({
|
|
1558
|
-
description: desc("cancel_qr_payment"),
|
|
1559
|
-
inputSchema: z.object({
|
|
1560
|
-
external_pos_id: z.string()
|
|
1561
|
-
}),
|
|
1562
|
-
execute: async ({ external_pos_id }) => {
|
|
1563
|
-
const me = await client.getMe();
|
|
1564
|
-
await client.cancelQrPayment(String(me.id), external_pos_id);
|
|
1565
|
-
return { external_pos_id, cancelled: true };
|
|
1566
|
-
}
|
|
1567
|
-
}),
|
|
1568
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1569
|
-
// Subscription Plans (v0.4)
|
|
1570
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1571
|
-
create_subscription_plan: tool({
|
|
1572
|
-
description: desc("create_subscription_plan"),
|
|
1573
|
-
inputSchema: z.object({
|
|
1574
|
-
reason: z.string().min(3).max(120).describe("Plan name shown at checkout"),
|
|
1575
|
-
amount_ars: z.number().positive(),
|
|
1576
|
-
frequency_months: z.number().int().min(1).max(12),
|
|
1577
|
-
back_url: z.string().url().describe("HTTPS URL where MP redirects after first payment"),
|
|
1578
|
-
external_reference: z.string().optional(),
|
|
1579
|
-
free_trial_days: z.number().int().min(1).max(60).optional().describe("Free trial period in days before first charge")
|
|
1580
|
-
}),
|
|
1581
|
-
execute: async (input) => {
|
|
1582
|
-
const plan = await client.createSubscriptionPlan({
|
|
1583
|
-
reason: input.reason,
|
|
1584
|
-
amount: input.amount_ars,
|
|
1585
|
-
currency: "ARS",
|
|
1586
|
-
frequency: input.frequency_months,
|
|
1587
|
-
frequencyType: "months",
|
|
1588
|
-
backUrl: input.back_url,
|
|
1589
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1590
|
-
...input.free_trial_days !== void 0 ? { freeTrialFrequency: input.free_trial_days, freeTrialFrequencyType: "days" } : {}
|
|
1591
|
-
});
|
|
1592
|
-
return {
|
|
1593
|
-
plan_id: plan.id,
|
|
1594
|
-
status: plan.status,
|
|
1595
|
-
reason: plan.reason,
|
|
1596
|
-
amount: plan.auto_recurring.transaction_amount,
|
|
1597
|
-
currency: plan.auto_recurring.currency_id,
|
|
1598
|
-
frequency: `${plan.auto_recurring.frequency} ${plan.auto_recurring.frequency_type}`,
|
|
1599
|
-
external_reference: plan.external_reference,
|
|
1600
|
-
next_step: "Use subscribe_to_plan to enroll customers in this plan, or share its ID for them to subscribe via your frontend."
|
|
1601
|
-
};
|
|
1602
|
-
}
|
|
1603
|
-
}),
|
|
1604
|
-
list_subscription_plans: tool({
|
|
1605
|
-
description: desc("list_subscription_plans"),
|
|
1606
|
-
inputSchema: z.object({
|
|
1607
|
-
status: z.string().optional(),
|
|
1608
|
-
limit: z.number().int().min(1).max(100).optional()
|
|
1609
|
-
}),
|
|
1610
|
-
execute: async (input) => {
|
|
1611
|
-
const result = await client.listSubscriptionPlans({
|
|
1612
|
-
...input.status !== void 0 ? { status: input.status } : {},
|
|
1613
|
-
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1614
|
-
});
|
|
1615
|
-
return {
|
|
1616
|
-
total: result.paging.total,
|
|
1617
|
-
plans: result.results.map((p) => ({
|
|
1618
|
-
plan_id: p.id,
|
|
1619
|
-
reason: p.reason,
|
|
1620
|
-
status: p.status,
|
|
1621
|
-
amount: p.auto_recurring.transaction_amount,
|
|
1622
|
-
currency: p.auto_recurring.currency_id,
|
|
1623
|
-
frequency: `${p.auto_recurring.frequency} ${p.auto_recurring.frequency_type}`
|
|
1624
|
-
}))
|
|
1625
|
-
};
|
|
1626
|
-
}
|
|
1627
|
-
}),
|
|
1628
|
-
update_subscription_plan: tool({
|
|
1629
|
-
description: desc("update_subscription_plan"),
|
|
1630
|
-
inputSchema: z.object({
|
|
1631
|
-
plan_id: z.string(),
|
|
1632
|
-
reason: z.string().optional(),
|
|
1633
|
-
amount_ars: z.number().positive().optional(),
|
|
1634
|
-
status: z.enum(["active", "cancelled"]).optional(),
|
|
1635
|
-
back_url: z.string().url().optional()
|
|
1636
|
-
}),
|
|
1637
|
-
execute: async (input) => {
|
|
1638
|
-
const updated = await client.updateSubscriptionPlan(input.plan_id, {
|
|
1639
|
-
...input.reason !== void 0 ? { reason: input.reason } : {},
|
|
1640
|
-
...input.amount_ars !== void 0 ? { amount: input.amount_ars } : {},
|
|
1641
|
-
...input.status !== void 0 ? { status: input.status } : {},
|
|
1642
|
-
...input.back_url !== void 0 ? { backUrl: input.back_url } : {}
|
|
1643
|
-
});
|
|
1644
|
-
return {
|
|
1645
|
-
plan_id: updated.id,
|
|
1646
|
-
status: updated.status,
|
|
1647
|
-
reason: updated.reason,
|
|
1648
|
-
amount: updated.auto_recurring.transaction_amount,
|
|
1649
|
-
message: input.amount_ars !== void 0 ? "Updated. Existing subscribers keep their old amount; only NEW subscribers get the new pricing." : "Plan updated."
|
|
1650
|
-
};
|
|
1651
|
-
}
|
|
1652
|
-
}),
|
|
1653
|
-
subscribe_to_plan: tool({
|
|
1654
|
-
description: desc("subscribe_to_plan"),
|
|
1655
|
-
inputSchema: z.object({
|
|
1656
|
-
plan_id: z.string(),
|
|
1657
|
-
customer_email: z.string().email(),
|
|
1658
|
-
external_reference: z.string().optional()
|
|
1659
|
-
}),
|
|
1660
|
-
execute: async (input) => {
|
|
1661
|
-
const sub = await client.subscribeToPlan({
|
|
1662
|
-
planId: input.plan_id,
|
|
1663
|
-
payerEmail: input.customer_email,
|
|
1664
|
-
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {}
|
|
1665
|
-
});
|
|
1666
|
-
return {
|
|
1667
|
-
subscription_id: sub.id,
|
|
1668
|
-
status: sub.status,
|
|
1669
|
-
payer_email: sub.payer_email,
|
|
1670
|
-
init_point_url: sub.init_point,
|
|
1671
|
-
next_step: "Send init_point_url to the customer for first payment with card+CVV."
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
}),
|
|
1675
|
-
list_subscription_payments: tool({
|
|
1676
|
-
description: desc("list_subscription_payments"),
|
|
1677
|
-
inputSchema: z.object({
|
|
1678
|
-
subscription_id: z.string(),
|
|
1679
|
-
limit: z.number().int().min(1).max(100).optional()
|
|
1680
|
-
}),
|
|
1681
|
-
execute: async (input) => {
|
|
1682
|
-
const result = await client.listSubscriptionPayments(input.subscription_id, {
|
|
1683
|
-
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1684
|
-
});
|
|
1685
|
-
return {
|
|
1686
|
-
subscription_id: input.subscription_id,
|
|
1687
|
-
total: result.paging.total,
|
|
1688
|
-
payments: result.results.map((p) => ({
|
|
1689
|
-
authorized_payment_id: p.id,
|
|
1690
|
-
payment_id: p.payment_id ?? null,
|
|
1691
|
-
status: p.status,
|
|
1692
|
-
amount: p.transaction_amount ?? null,
|
|
1693
|
-
currency: p.currency_id ?? null,
|
|
1694
|
-
debit_date: p.debit_date ?? null,
|
|
1695
|
-
next_retry_date: p.next_retry_date ?? null,
|
|
1696
|
-
retry_attempt: p.retry_attempt ?? 0,
|
|
1697
|
-
reason: p.reason ?? null
|
|
1698
|
-
}))
|
|
1699
|
-
};
|
|
1700
|
-
}
|
|
1701
|
-
}),
|
|
1702
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1703
|
-
// Stores + POS (v0.4)
|
|
1704
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1705
|
-
create_store: tool({
|
|
1706
|
-
description: desc("create_store"),
|
|
1707
|
-
inputSchema: z.object({
|
|
1708
|
-
name: z.string().min(1).max(80),
|
|
1709
|
-
external_id: z.string().min(1).max(64).describe("Unique within the seller's stores"),
|
|
1710
|
-
address_line: z.string().optional(),
|
|
1711
|
-
city_name: z.string().optional(),
|
|
1712
|
-
state_name: z.string().optional()
|
|
1713
|
-
}),
|
|
1714
|
-
execute: async (input) => {
|
|
1715
|
-
const me = await client.getMe();
|
|
1716
|
-
const store = await client.createStore(String(me.id), {
|
|
1717
|
-
name: input.name,
|
|
1718
|
-
externalId: input.external_id,
|
|
1719
|
-
...input.address_line || input.city_name || input.state_name ? {
|
|
1720
|
-
location: {
|
|
1721
|
-
...input.address_line ? { addressLine: input.address_line } : {},
|
|
1722
|
-
...input.city_name ? { cityName: input.city_name } : {},
|
|
1723
|
-
...input.state_name ? { stateName: input.state_name } : {},
|
|
1724
|
-
countryId: "AR"
|
|
1725
|
-
}
|
|
1726
|
-
} : {}
|
|
1727
|
-
});
|
|
1728
|
-
return {
|
|
1729
|
-
store_id: store.id,
|
|
1730
|
-
name: store.name,
|
|
1731
|
-
external_id: store.external_id,
|
|
1732
|
-
next_step: "Use create_pos with this store_id to add a Point of Sale where create_qr_payment can issue QRs."
|
|
1733
|
-
};
|
|
1734
|
-
}
|
|
1735
|
-
}),
|
|
1736
|
-
list_stores: tool({
|
|
1737
|
-
description: desc("list_stores"),
|
|
1738
|
-
inputSchema: z.object({
|
|
1739
|
-
limit: z.number().int().min(1).max(100).optional()
|
|
1740
|
-
}),
|
|
1741
|
-
execute: async (input) => {
|
|
1742
|
-
const me = await client.getMe();
|
|
1743
|
-
const result = await client.listStores(String(me.id), {
|
|
1744
|
-
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1745
|
-
});
|
|
1746
|
-
return {
|
|
1747
|
-
total: result.paging.total,
|
|
1748
|
-
stores: result.results.map((s) => ({
|
|
1749
|
-
store_id: s.id,
|
|
1750
|
-
name: s.name ?? null,
|
|
1751
|
-
external_id: s.external_id ?? null
|
|
1752
|
-
}))
|
|
1753
|
-
};
|
|
1754
|
-
}
|
|
1755
|
-
}),
|
|
1756
|
-
create_pos: tool({
|
|
1757
|
-
description: desc("create_pos"),
|
|
1758
|
-
inputSchema: z.object({
|
|
1759
|
-
name: z.string().min(1).max(80),
|
|
1760
|
-
external_id: z.string().min(1).max(64).describe("Unique within the store. This is what create_qr_payment uses."),
|
|
1761
|
-
store_id: z.string().describe("From create_store / list_stores"),
|
|
1762
|
-
category: z.number().int().optional().describe("MP category code, default 621102 (other food/beverage)"),
|
|
1763
|
-
fixed_amount: z.boolean().optional().describe("True for static QR with fixed amount; false (default) for dynamic per-order QR")
|
|
1764
|
-
}),
|
|
1765
|
-
execute: async (input) => {
|
|
1766
|
-
const pos = await client.createPos({
|
|
1767
|
-
name: input.name,
|
|
1768
|
-
externalId: input.external_id,
|
|
1769
|
-
storeId: input.store_id,
|
|
1770
|
-
...input.category !== void 0 ? { category: input.category } : {},
|
|
1771
|
-
...input.fixed_amount !== void 0 ? { fixedAmount: input.fixed_amount } : {}
|
|
1772
|
-
});
|
|
1773
|
-
return {
|
|
1774
|
-
pos_id: pos.id,
|
|
1775
|
-
external_id: pos.external_id,
|
|
1776
|
-
store_id: pos.store_id,
|
|
1777
|
-
name: pos.name,
|
|
1778
|
-
next_step: "Use create_qr_payment with this external_id to start issuing dynamic QRs from this POS."
|
|
1779
|
-
};
|
|
1780
|
-
}
|
|
1781
|
-
}),
|
|
1782
|
-
list_pos: tool({
|
|
1783
|
-
description: desc("list_pos"),
|
|
1784
|
-
inputSchema: z.object({
|
|
1785
|
-
store_id: z.string().optional(),
|
|
1786
|
-
limit: z.number().int().min(1).max(100).optional()
|
|
1787
|
-
}),
|
|
1788
|
-
execute: async (input) => {
|
|
1789
|
-
const result = await client.listPos({
|
|
1790
|
-
...input.store_id !== void 0 ? { storeId: input.store_id } : {},
|
|
1791
|
-
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
1792
|
-
});
|
|
1793
|
-
return {
|
|
1794
|
-
total: result.paging.total,
|
|
1795
|
-
pos: result.results.map((p) => ({
|
|
1796
|
-
pos_id: p.id,
|
|
1797
|
-
external_id: p.external_id ?? null,
|
|
1798
|
-
store_id: p.store_id ?? null,
|
|
1799
|
-
name: p.name ?? null
|
|
1800
|
-
}))
|
|
1801
|
-
};
|
|
1802
|
-
}
|
|
1803
|
-
}),
|
|
1804
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1805
|
-
// Disputes (v0.4 — read-only)
|
|
1806
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1807
|
-
list_payment_disputes: tool({
|
|
1808
|
-
description: desc("list_payment_disputes"),
|
|
1809
|
-
inputSchema: z.object({ payment_id: z.string() }),
|
|
1810
|
-
execute: async ({ payment_id }) => {
|
|
1811
|
-
const disputes = await client.listPaymentDisputes(payment_id);
|
|
1812
|
-
return {
|
|
1813
|
-
payment_id,
|
|
1814
|
-
count: disputes.length,
|
|
1815
|
-
disputes: disputes.map((d) => ({
|
|
1816
|
-
dispute_id: d.id,
|
|
1817
|
-
status: d.status,
|
|
1818
|
-
amount: d.amount ?? null,
|
|
1819
|
-
reason: d.reason ?? null,
|
|
1820
|
-
date_created: d.date_created ?? null,
|
|
1821
|
-
dashboard_url: `https://www.mercadopago.com.ar/disputes/${d.id}`
|
|
1822
|
-
}))
|
|
1823
|
-
};
|
|
1824
|
-
}
|
|
1825
|
-
}),
|
|
1826
|
-
get_dispute: tool({
|
|
1827
|
-
description: desc("get_dispute"),
|
|
1828
|
-
inputSchema: z.object({
|
|
1829
|
-
payment_id: z.string(),
|
|
1830
|
-
dispute_id: z.string()
|
|
1831
|
-
}),
|
|
1832
|
-
execute: async ({ payment_id, dispute_id }) => {
|
|
1833
|
-
const d = await client.getDispute(payment_id, dispute_id);
|
|
1834
|
-
return {
|
|
1835
|
-
dispute_id: d.id,
|
|
1836
|
-
status: d.status,
|
|
1837
|
-
amount: d.amount ?? null,
|
|
1838
|
-
reason: d.reason ?? null,
|
|
1839
|
-
reason_description: d.reason_description ?? null,
|
|
1840
|
-
resolution: d.resolution ?? null,
|
|
1841
|
-
date_created: d.date_created ?? null,
|
|
1842
|
-
dashboard_url: `https://www.mercadopago.com.ar/disputes/${d.id}`
|
|
1843
|
-
};
|
|
1844
|
-
}
|
|
1845
|
-
}),
|
|
1846
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1847
|
-
// Lookup helpers (v0.4)
|
|
1848
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1849
|
-
list_identification_types: tool({
|
|
1850
|
-
description: desc("list_identification_types"),
|
|
1851
|
-
inputSchema: z.object({}),
|
|
1852
|
-
execute: async () => {
|
|
1853
|
-
const types = await client.listIdentificationTypes();
|
|
1854
|
-
return {
|
|
1855
|
-
count: types.length,
|
|
1856
|
-
types: types.map((t) => ({
|
|
1857
|
-
id: t.id,
|
|
1858
|
-
name: t.name,
|
|
1859
|
-
type: t.type,
|
|
1860
|
-
min_length: t.min_length ?? null,
|
|
1861
|
-
max_length: t.max_length ?? null
|
|
1862
|
-
}))
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
}),
|
|
1866
|
-
list_issuers: tool({
|
|
1867
|
-
description: desc("list_issuers"),
|
|
1868
|
-
inputSchema: z.object({
|
|
1869
|
-
payment_method_id: z.string().describe("E.g. 'visa', 'master', 'naranja'"),
|
|
1870
|
-
bin: z.string().min(6).max(8).optional().describe("First 6-8 digits of card for precise issuer detection")
|
|
1871
|
-
}),
|
|
1872
|
-
execute: async (input) => {
|
|
1873
|
-
const issuers = await client.listIssuers({
|
|
1874
|
-
paymentMethodId: input.payment_method_id,
|
|
1875
|
-
...input.bin !== void 0 ? { bin: input.bin } : {}
|
|
1876
|
-
});
|
|
1877
|
-
return {
|
|
1878
|
-
payment_method_id: input.payment_method_id,
|
|
1879
|
-
count: issuers.length,
|
|
1880
|
-
issuers: issuers.map((i) => ({
|
|
1881
|
-
issuer_id: i.id,
|
|
1882
|
-
name: i.name,
|
|
1883
|
-
status: i.status ?? null
|
|
1884
|
-
}))
|
|
1885
|
-
};
|
|
1886
|
-
}
|
|
1887
|
-
}),
|
|
1888
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1889
|
-
// Webhooks management (v0.4)
|
|
1890
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1891
|
-
list_webhooks: tool({
|
|
1892
|
-
description: desc("list_webhooks"),
|
|
1893
|
-
inputSchema: z.object({}),
|
|
1894
|
-
execute: async () => {
|
|
1895
|
-
const hooks = await client.listWebhooks();
|
|
1896
|
-
return {
|
|
1897
|
-
count: hooks.length,
|
|
1898
|
-
webhooks: hooks.map((h) => ({
|
|
1899
|
-
webhook_id: h.id,
|
|
1900
|
-
url: h.url ?? null,
|
|
1901
|
-
topic: h.topic ?? null,
|
|
1902
|
-
status: h.status ?? null,
|
|
1903
|
-
date_created: h.date_created ?? null
|
|
1904
|
-
}))
|
|
1905
|
-
};
|
|
1906
|
-
}
|
|
1907
|
-
}),
|
|
1908
|
-
create_webhook: tool({
|
|
1909
|
-
description: desc("create_webhook"),
|
|
1910
|
-
inputSchema: z.object({
|
|
1911
|
-
url: z.string().url(),
|
|
1912
|
-
topic: z.string().describe("E.g. 'payment', 'subscription_authorized_payment', 'subscription_preapproval', 'merchant_order', 'point_integration_wh'")
|
|
1913
|
-
}),
|
|
1914
|
-
execute: async ({ url, topic }) => {
|
|
1915
|
-
const hook = await client.createWebhook({ url, topic });
|
|
1916
|
-
return {
|
|
1917
|
-
webhook_id: hook.id,
|
|
1918
|
-
url: hook.url ?? url,
|
|
1919
|
-
topic: hook.topic ?? topic,
|
|
1920
|
-
status: hook.status ?? null
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
}),
|
|
1924
|
-
update_webhook: tool({
|
|
1925
|
-
description: desc("update_webhook"),
|
|
1926
|
-
inputSchema: z.object({
|
|
1927
|
-
webhook_id: z.string(),
|
|
1928
|
-
url: z.string().url().optional(),
|
|
1929
|
-
topic: z.string().optional()
|
|
1930
|
-
}),
|
|
1931
|
-
execute: async (input) => {
|
|
1932
|
-
const hook = await client.updateWebhook(input.webhook_id, {
|
|
1933
|
-
...input.url !== void 0 ? { url: input.url } : {},
|
|
1934
|
-
...input.topic !== void 0 ? { topic: input.topic } : {}
|
|
1935
|
-
});
|
|
1936
|
-
return {
|
|
1937
|
-
webhook_id: hook.id,
|
|
1938
|
-
url: hook.url ?? null,
|
|
1939
|
-
topic: hook.topic ?? null,
|
|
1940
|
-
status: hook.status ?? null
|
|
1941
|
-
};
|
|
1942
|
-
}
|
|
1943
|
-
}),
|
|
1944
|
-
delete_webhook: tool({
|
|
1945
|
-
description: desc("delete_webhook"),
|
|
1946
|
-
inputSchema: z.object({ webhook_id: z.string() }),
|
|
1947
|
-
execute: async ({ webhook_id }) => {
|
|
1948
|
-
await client.deleteWebhook(webhook_id);
|
|
1949
|
-
return { webhook_id, deleted: true };
|
|
1950
|
-
}
|
|
1951
|
-
})
|
|
1952
|
-
};
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
// src/state.ts
|
|
1956
|
-
var InMemoryStateAdapter = class {
|
|
1957
|
-
store = /* @__PURE__ */ new Map();
|
|
1958
|
-
async set(id, state) {
|
|
1959
|
-
const existing = this.store.get(id) ?? {};
|
|
1960
|
-
this.store.set(id, { ...existing, ...state });
|
|
1961
|
-
}
|
|
1962
|
-
async get(id) {
|
|
1963
|
-
return this.store.get(id) ?? null;
|
|
1964
|
-
}
|
|
1965
|
-
async list() {
|
|
1966
|
-
return Array.from(this.store.keys());
|
|
1967
|
-
}
|
|
1968
|
-
/** Test helper: drop everything. Not part of the adapter interface. */
|
|
1969
|
-
reset() {
|
|
1970
|
-
this.store.clear();
|
|
1971
|
-
}
|
|
900
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
901
|
+
// v0.5 — Order Management API
|
|
902
|
+
//
|
|
903
|
+
// The Order API is MP's newer abstraction for purchases, replacing some
|
|
904
|
+
// Preference flows. Distinct from Preference: Order is a transactional
|
|
905
|
+
// entity with explicit lifecycle (created → processed → captured/canceled),
|
|
906
|
+
// supports manual capture (auth-only, capture later), and can attach
|
|
907
|
+
// multiple payments to a single Order.
|
|
908
|
+
//
|
|
909
|
+
// Use Order when you need:
|
|
910
|
+
// - Auth-only flow (capture later, e.g. ride-share, hotels)
|
|
911
|
+
// - Multi-payment aggregation (one Order = N partial payments)
|
|
912
|
+
// - In-store + online unified status
|
|
913
|
+
//
|
|
914
|
+
// Stick with Preference (Checkout Pro) when you just need a hosted pay-link.
|
|
915
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
916
|
+
/**
|
|
917
|
+
* Create a new Order. Use `capture_mode: "manual"` for auth-only flows
|
|
918
|
+
* where you want to capture funds later (ride-share, hotels, marketplaces).
|
|
919
|
+
*
|
|
920
|
+
* For marketplace splits, set `marketplace`, `marketplace_fee`,
|
|
921
|
+
* `collector_id` — see `MarketplaceParams`.
|
|
922
|
+
*/
|
|
923
|
+
async createOrder(params, options) {
|
|
924
|
+
const body = {
|
|
925
|
+
type: params.type
|
|
926
|
+
};
|
|
927
|
+
if (params.currency_id) body.currency_id = params.currency_id;
|
|
928
|
+
if (params.external_reference) body.external_reference = params.external_reference;
|
|
929
|
+
if (params.items) body.items = params.items;
|
|
930
|
+
if (params.total_amount !== void 0) body.total_amount = params.total_amount;
|
|
931
|
+
if (params.payer) body.payer = params.payer;
|
|
932
|
+
if (params.capture_mode) body.capture_mode = params.capture_mode;
|
|
933
|
+
if (params.notification_url) body.notification_url = params.notification_url;
|
|
934
|
+
if (params.marketplace) body.marketplace = params.marketplace;
|
|
935
|
+
if (params.marketplace_fee !== void 0) body.marketplace_fee = params.marketplace_fee;
|
|
936
|
+
if (params.collector_id !== void 0) body.collector_id = params.collector_id;
|
|
937
|
+
return this.request("POST", "/v1/orders", body, options);
|
|
938
|
+
}
|
|
939
|
+
async getOrder(id) {
|
|
940
|
+
return this.request("GET", `/v1/orders/${id}`);
|
|
941
|
+
}
|
|
942
|
+
async updateOrder(id, patch) {
|
|
943
|
+
return this.request("PUT", `/v1/orders/${id}`, patch);
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Capture a previously-authorized Order (only for orders created with
|
|
947
|
+
* `capture_mode: "manual"`). Captures up to the originally-authorized
|
|
948
|
+
* amount; pass `amount` for partial capture.
|
|
949
|
+
*/
|
|
950
|
+
async captureOrder(id, amount) {
|
|
951
|
+
const body = amount !== void 0 ? { amount } : {};
|
|
952
|
+
return this.request("POST", `/v1/orders/${id}/capture`, body);
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Cancel an Order. Releases any auth-holds; marks the Order as canceled.
|
|
956
|
+
* For orders that have already been captured, use `createRefund` instead.
|
|
957
|
+
*/
|
|
958
|
+
async cancelOrder(id) {
|
|
959
|
+
return this.request("POST", `/v1/orders/${id}/cancel`);
|
|
960
|
+
}
|
|
961
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
962
|
+
// v0.6 — Account Balance + Movements
|
|
963
|
+
//
|
|
964
|
+
// Inspect the seller's MP wallet — what's available to withdraw, what's
|
|
965
|
+
// in retention (pending release), and the movement log.
|
|
966
|
+
//
|
|
967
|
+
// For per-seller marketplace setups, instantiate the client AS THE SELLER
|
|
968
|
+
// (with their OAuth access_token) before calling these — `getAccountBalance`
|
|
969
|
+
// returns the balance of WHOEVER's accessToken is active.
|
|
970
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
971
|
+
/**
|
|
972
|
+
* Get the seller's current MP wallet balance (available + unavailable).
|
|
973
|
+
* - `available_balance`: spendable / withdrawable right now.
|
|
974
|
+
* - `unavailable_balance`: in retention (e.g., 14-21 days for new sellers).
|
|
975
|
+
* - `total_amount` = sum of both.
|
|
976
|
+
*/
|
|
977
|
+
async getAccountBalance() {
|
|
978
|
+
return this.request("GET", "/users/me/mercadopago_account/balance");
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* List wallet movements (incoming payments, transfers, refunds, holdings).
|
|
982
|
+
* Defaults to most-recent-first, paginated. Filter by date range with
|
|
983
|
+
* `from`/`to` (ISO 8601).
|
|
984
|
+
*/
|
|
985
|
+
async listAccountMovements(params = {}) {
|
|
986
|
+
const query = {};
|
|
987
|
+
if (params.from) query.begin_date = params.from;
|
|
988
|
+
if (params.to) query.end_date = params.to;
|
|
989
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
990
|
+
if (params.offset !== void 0) query.offset = params.offset;
|
|
991
|
+
const result = await this.request("GET", "/users/me/mercadopago_account/movements/search", void 0, { query });
|
|
992
|
+
return {
|
|
993
|
+
movements: result.results ?? [],
|
|
994
|
+
paging: result.paging ?? { limit: params.limit ?? 25, offset: params.offset ?? 0, total: 0 }
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
998
|
+
// v0.6 — Settlements (release_money)
|
|
999
|
+
//
|
|
1000
|
+
// When MP transfers funds from your MP wallet to your registered CBU.
|
|
1001
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1002
|
+
/**
|
|
1003
|
+
* List settlements (transfers from MP wallet to your bank account).
|
|
1004
|
+
* Useful for monthly conciliation reports.
|
|
1005
|
+
*/
|
|
1006
|
+
async listSettlements(params = {}) {
|
|
1007
|
+
const query = {};
|
|
1008
|
+
if (params.from) query.begin_date = params.from;
|
|
1009
|
+
if (params.to) query.end_date = params.to;
|
|
1010
|
+
if (params.status) query.status = params.status;
|
|
1011
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
1012
|
+
if (params.offset !== void 0) query.offset = params.offset;
|
|
1013
|
+
const result = await this.request("GET", "/v1/account/release_money/search", void 0, { query });
|
|
1014
|
+
return {
|
|
1015
|
+
settlements: result.results ?? [],
|
|
1016
|
+
paging: result.paging ?? { limit: params.limit ?? 25, offset: params.offset ?? 0, total: 0 }
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Get a single settlement by id. Returns the full Settlement object
|
|
1021
|
+
* including bank_account info (CBU, bank name).
|
|
1022
|
+
*/
|
|
1023
|
+
async getSettlement(id) {
|
|
1024
|
+
return this.request("GET", `/v1/account/release_money/${id}`);
|
|
1025
|
+
}
|
|
1972
1026
|
};
|
|
1973
1027
|
z.enum(["MLA", "MLB", "MLM", "MCO", "MLC", "MLU"]);
|
|
1974
1028
|
var CurrencyIdSchema = z.enum(["ARS", "USD", "BRL", "MXN"]);
|
|
@@ -2267,8 +1321,282 @@ z.object({
|
|
|
2267
1321
|
date_created: z.string().optional(),
|
|
2268
1322
|
date_modified: z.string().optional()
|
|
2269
1323
|
}).passthrough();
|
|
1324
|
+
var OAuthTokenSchema = z.object({
|
|
1325
|
+
access_token: z.string(),
|
|
1326
|
+
token_type: z.string().optional(),
|
|
1327
|
+
/** Seconds until access_token expires. Typically 21600 (6h). */
|
|
1328
|
+
expires_in: z.number().optional(),
|
|
1329
|
+
scope: z.string().optional(),
|
|
1330
|
+
/** The MP user_id of the seller who authorized your app. */
|
|
1331
|
+
user_id: z.union([z.string(), z.number()]).transform(String),
|
|
1332
|
+
refresh_token: z.string().optional(),
|
|
1333
|
+
public_key: z.string().optional(),
|
|
1334
|
+
live_mode: z.boolean().optional()
|
|
1335
|
+
}).passthrough();
|
|
1336
|
+
var OrderStatusSchema = z.union([
|
|
1337
|
+
z.literal("created"),
|
|
1338
|
+
z.literal("processed"),
|
|
1339
|
+
z.literal("action_required"),
|
|
1340
|
+
z.literal("canceled"),
|
|
1341
|
+
z.literal("expired"),
|
|
1342
|
+
z.literal("refunded"),
|
|
1343
|
+
z.string()
|
|
1344
|
+
]);
|
|
1345
|
+
var OrderItemSchema = z.object({
|
|
1346
|
+
title: z.string(),
|
|
1347
|
+
unit_price: z.number(),
|
|
1348
|
+
quantity: z.number(),
|
|
1349
|
+
description: z.string().optional(),
|
|
1350
|
+
id: z.string().optional(),
|
|
1351
|
+
category_id: z.string().optional(),
|
|
1352
|
+
picture_url: z.string().optional()
|
|
1353
|
+
}).passthrough();
|
|
1354
|
+
z.object({
|
|
1355
|
+
id: z.union([z.string(), z.number()]).transform(String),
|
|
1356
|
+
type: z.string().optional(),
|
|
1357
|
+
status: OrderStatusSchema.optional(),
|
|
1358
|
+
status_detail: z.string().optional(),
|
|
1359
|
+
external_reference: z.string().optional(),
|
|
1360
|
+
total_amount: z.union([z.number(), z.string()]).optional(),
|
|
1361
|
+
/** Currency for this Order (e.g. "ARS"). */
|
|
1362
|
+
currency_id: z.string().optional(),
|
|
1363
|
+
date_created: z.string().optional(),
|
|
1364
|
+
date_last_updated: z.string().optional(),
|
|
1365
|
+
items: z.array(OrderItemSchema).optional(),
|
|
1366
|
+
/** Underlying transactions (payments) attached to this Order. */
|
|
1367
|
+
transactions: z.object({
|
|
1368
|
+
payments: z.array(z.unknown()).optional(),
|
|
1369
|
+
refunds: z.array(z.unknown()).optional()
|
|
1370
|
+
}).passthrough().optional(),
|
|
1371
|
+
/** Capture mode: "automatic" (charges immediately) or "manual" (auth-only). */
|
|
1372
|
+
capture_mode: z.string().optional()
|
|
1373
|
+
}).passthrough();
|
|
1374
|
+
z.object({
|
|
1375
|
+
user_id: z.union([z.string(), z.number()]).transform(String).optional(),
|
|
1376
|
+
available_balance: z.number(),
|
|
1377
|
+
unavailable_balance: z.number(),
|
|
1378
|
+
total_amount: z.number(),
|
|
1379
|
+
currency_id: z.string().default("ARS")
|
|
1380
|
+
}).passthrough();
|
|
1381
|
+
z.object({
|
|
1382
|
+
id: z.union([z.string(), z.number()]).transform(String),
|
|
1383
|
+
type: z.string(),
|
|
1384
|
+
description: z.string().optional(),
|
|
1385
|
+
amount: z.number(),
|
|
1386
|
+
currency_id: z.string().optional(),
|
|
1387
|
+
status: z.string().optional(),
|
|
1388
|
+
date_created: z.string().optional(),
|
|
1389
|
+
date_released: z.string().optional(),
|
|
1390
|
+
reference_id: z.union([z.string(), z.number()]).optional(),
|
|
1391
|
+
payment_id: z.union([z.string(), z.number()]).optional()
|
|
1392
|
+
}).passthrough();
|
|
1393
|
+
z.object({
|
|
1394
|
+
id: z.union([z.string(), z.number()]).transform(String),
|
|
1395
|
+
status: z.string().optional(),
|
|
1396
|
+
amount: z.number().optional(),
|
|
1397
|
+
currency_id: z.string().optional(),
|
|
1398
|
+
date_created: z.string().optional(),
|
|
1399
|
+
date_scheduled: z.string().optional(),
|
|
1400
|
+
date_processed: z.string().optional(),
|
|
1401
|
+
bank_account: z.object({
|
|
1402
|
+
cbu: z.string().optional(),
|
|
1403
|
+
bank_name: z.string().optional()
|
|
1404
|
+
}).passthrough().optional()
|
|
1405
|
+
}).passthrough();
|
|
1406
|
+
|
|
1407
|
+
// src/oauth.ts
|
|
1408
|
+
var DEFAULT_AUTHORIZE_URL = "https://auth.mercadopago.com.ar/authorization";
|
|
1409
|
+
var DEFAULT_TOKEN_URL = "https://api.mercadopago.com/oauth/token";
|
|
1410
|
+
function buildAuthorizeUrl(params) {
|
|
1411
|
+
const url = new URL(params.authorizeUrl ?? DEFAULT_AUTHORIZE_URL);
|
|
1412
|
+
url.searchParams.set("client_id", params.clientId);
|
|
1413
|
+
url.searchParams.set("response_type", "code");
|
|
1414
|
+
url.searchParams.set("platform_id", "mp");
|
|
1415
|
+
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
1416
|
+
if (params.state) url.searchParams.set("state", params.state);
|
|
1417
|
+
return url.toString();
|
|
1418
|
+
}
|
|
1419
|
+
async function exchangeCodeForToken(params) {
|
|
1420
|
+
const url = params.tokenUrl ?? DEFAULT_TOKEN_URL;
|
|
1421
|
+
const fetchFn = params.fetchImpl ?? globalThis.fetch;
|
|
1422
|
+
const res = await fetchFn(url, {
|
|
1423
|
+
method: "POST",
|
|
1424
|
+
headers: {
|
|
1425
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1426
|
+
Accept: "application/json"
|
|
1427
|
+
},
|
|
1428
|
+
body: new URLSearchParams({
|
|
1429
|
+
grant_type: "authorization_code",
|
|
1430
|
+
client_id: params.clientId,
|
|
1431
|
+
client_secret: params.clientSecret,
|
|
1432
|
+
code: params.code,
|
|
1433
|
+
redirect_uri: params.redirectUri
|
|
1434
|
+
}).toString()
|
|
1435
|
+
});
|
|
1436
|
+
return parseTokenResponse(res);
|
|
1437
|
+
}
|
|
1438
|
+
async function refreshAccessToken(params) {
|
|
1439
|
+
const url = params.tokenUrl ?? DEFAULT_TOKEN_URL;
|
|
1440
|
+
const fetchFn = params.fetchImpl ?? globalThis.fetch;
|
|
1441
|
+
const res = await fetchFn(url, {
|
|
1442
|
+
method: "POST",
|
|
1443
|
+
headers: {
|
|
1444
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1445
|
+
Accept: "application/json"
|
|
1446
|
+
},
|
|
1447
|
+
body: new URLSearchParams({
|
|
1448
|
+
grant_type: "refresh_token",
|
|
1449
|
+
client_id: params.clientId,
|
|
1450
|
+
client_secret: params.clientSecret,
|
|
1451
|
+
refresh_token: params.refreshToken
|
|
1452
|
+
}).toString()
|
|
1453
|
+
});
|
|
1454
|
+
return parseTokenResponse(res);
|
|
1455
|
+
}
|
|
1456
|
+
async function parseTokenResponse(res) {
|
|
1457
|
+
const text = await res.text();
|
|
1458
|
+
if (!res.ok) {
|
|
1459
|
+
throw new Error(
|
|
1460
|
+
`MP OAuth ${res.status}: ${text.slice(0, 300)}`
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
const json = JSON.parse(text);
|
|
1464
|
+
return OAuthTokenSchema.parse(json);
|
|
1465
|
+
}
|
|
1466
|
+
function expirationTimeMs(issuedAtMs, expiresInSeconds) {
|
|
1467
|
+
return issuedAtMs + (expiresInSeconds ?? 21600) * 1e3;
|
|
1468
|
+
}
|
|
1469
|
+
function isExpiringSoon(expirationMs, skewSeconds = 300) {
|
|
1470
|
+
return Date.now() + skewSeconds * 1e3 >= expirationMs;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// src/test-cards.ts
|
|
1474
|
+
var TEST_CARDS_AR = {
|
|
1475
|
+
VISA_CREDIT: {
|
|
1476
|
+
brand: "Visa (cr\xE9dito)",
|
|
1477
|
+
number: "4509 9535 6623 3704".replace(/\s/g, ""),
|
|
1478
|
+
cvv: "123",
|
|
1479
|
+
exp: "11/30",
|
|
1480
|
+
paymentMethodId: "visa",
|
|
1481
|
+
holderNameToTest: {
|
|
1482
|
+
APRO: "approved",
|
|
1483
|
+
OTHE: "cc_rejected_other_reason",
|
|
1484
|
+
CONT: "pending_contingency",
|
|
1485
|
+
CALL: "cc_rejected_call_for_authorize",
|
|
1486
|
+
FUND: "cc_rejected_insufficient_amount",
|
|
1487
|
+
SECU: "cc_rejected_bad_filled_security_code",
|
|
1488
|
+
EXPI: "cc_rejected_bad_filled_date",
|
|
1489
|
+
FORM: "cc_rejected_bad_filled_other"
|
|
1490
|
+
}
|
|
1491
|
+
},
|
|
1492
|
+
MASTERCARD_CREDIT: {
|
|
1493
|
+
brand: "Mastercard (cr\xE9dito)",
|
|
1494
|
+
number: "5031 7557 3453 0604".replace(/\s/g, ""),
|
|
1495
|
+
cvv: "123",
|
|
1496
|
+
exp: "11/30",
|
|
1497
|
+
paymentMethodId: "master",
|
|
1498
|
+
holderNameToTest: {
|
|
1499
|
+
APRO: "approved",
|
|
1500
|
+
OTHE: "cc_rejected_other_reason",
|
|
1501
|
+
CONT: "pending_contingency",
|
|
1502
|
+
CALL: "cc_rejected_call_for_authorize",
|
|
1503
|
+
FUND: "cc_rejected_insufficient_amount"
|
|
1504
|
+
}
|
|
1505
|
+
},
|
|
1506
|
+
AMEX_CREDIT: {
|
|
1507
|
+
brand: "American Express (cr\xE9dito)",
|
|
1508
|
+
number: "3711 803032 57522".replace(/\s/g, ""),
|
|
1509
|
+
cvv: "1234",
|
|
1510
|
+
exp: "11/30",
|
|
1511
|
+
paymentMethodId: "amex",
|
|
1512
|
+
holderNameToTest: { APRO: "approved", OTHE: "cc_rejected_other_reason" }
|
|
1513
|
+
},
|
|
1514
|
+
VISA_DEBIT: {
|
|
1515
|
+
brand: "Visa (d\xE9bito)",
|
|
1516
|
+
number: "4002 7686 9439 5619".replace(/\s/g, ""),
|
|
1517
|
+
cvv: "123",
|
|
1518
|
+
exp: "11/30",
|
|
1519
|
+
paymentMethodId: "debvisa",
|
|
1520
|
+
holderNameToTest: { APRO: "approved", OTHE: "cc_rejected_other_reason" }
|
|
1521
|
+
},
|
|
1522
|
+
MASTERCARD_DEBIT: {
|
|
1523
|
+
brand: "Mastercard (d\xE9bito)",
|
|
1524
|
+
number: "5287 3383 0125 4634".replace(/\s/g, ""),
|
|
1525
|
+
cvv: "123",
|
|
1526
|
+
exp: "11/30",
|
|
1527
|
+
paymentMethodId: "debmaster",
|
|
1528
|
+
holderNameToTest: { APRO: "approved", OTHE: "cc_rejected_other_reason" }
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
var TEST_PAYERS_AR = {
|
|
1532
|
+
approvedBuyer: () => ({
|
|
1533
|
+
email: `test_user_${Date.now()}@testuser.com`,
|
|
1534
|
+
identification: { type: "DNI", number: "12345678" }
|
|
1535
|
+
})
|
|
1536
|
+
};
|
|
1537
|
+
function buildTestCardScenario(cardKey, scenario, amountArs) {
|
|
1538
|
+
const card = TEST_CARDS_AR[cardKey];
|
|
1539
|
+
if (!card) throw new Error(`Unknown test card: ${cardKey}`);
|
|
1540
|
+
if (!card.holderNameToTest[scenario]) {
|
|
1541
|
+
throw new Error(
|
|
1542
|
+
`Card ${cardKey} doesn't define scenario ${scenario}. Available: ${Object.keys(card.holderNameToTest).join(", ")}`
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
return {
|
|
1546
|
+
transactionAmount: amountArs,
|
|
1547
|
+
paymentMethodId: card.paymentMethodId,
|
|
1548
|
+
payerEmail: TEST_PAYERS_AR.approvedBuyer().email,
|
|
1549
|
+
description: `TEST ${scenario} via ${cardKey}`,
|
|
1550
|
+
installments: 1,
|
|
1551
|
+
holderName: scenario
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
2270
1554
|
|
|
2271
|
-
// src/
|
|
1555
|
+
// src/three-ds.ts
|
|
1556
|
+
function analyze3DS(payment) {
|
|
1557
|
+
const raw = payment;
|
|
1558
|
+
const mode = raw.three_d_secure_mode ?? null;
|
|
1559
|
+
const statusDetail = payment.status_detail ?? null;
|
|
1560
|
+
if (!mode || mode === "not_supported" || mode === "off") {
|
|
1561
|
+
return {
|
|
1562
|
+
status: "not_required",
|
|
1563
|
+
mode,
|
|
1564
|
+
challengeUrl: null,
|
|
1565
|
+
description: "3DS no fue requerido para este pago (riesgo bajo o emisor sin 3DS habilitado)."
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
const threeDsInfo = raw.three_ds_info ?? void 0;
|
|
1569
|
+
if (statusDetail === "pending_challenge" && threeDsInfo?.external_resource_url) {
|
|
1570
|
+
return {
|
|
1571
|
+
status: "challenge_required",
|
|
1572
|
+
mode,
|
|
1573
|
+
challengeUrl: threeDsInfo.external_resource_url,
|
|
1574
|
+
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."
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
if (payment.status === "approved") {
|
|
1578
|
+
return {
|
|
1579
|
+
status: "frictionless",
|
|
1580
|
+
mode,
|
|
1581
|
+
challengeUrl: null,
|
|
1582
|
+
description: "3DS frictionless: el emisor autoriz\xF3 sin desafiar al comprador."
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
if (payment.status === "rejected" && typeof statusDetail === "string" && statusDetail.includes("3ds")) {
|
|
1586
|
+
return {
|
|
1587
|
+
status: "rejected",
|
|
1588
|
+
mode,
|
|
1589
|
+
challengeUrl: null,
|
|
1590
|
+
description: `Autenticaci\xF3n 3DS rechazada (${statusDetail}). El comprador debe usar otra tarjeta o validarla con el emisor.`
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
return {
|
|
1594
|
+
status: "unknown",
|
|
1595
|
+
mode,
|
|
1596
|
+
challengeUrl: threeDsInfo?.external_resource_url ?? null,
|
|
1597
|
+
description: "No se pudo determinar el estado 3DS \u2014 revisar payment.three_d_secure_mode + payment.status_detail manualmente."
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
2272
1600
|
function parseWebhookEvent(body, searchParams) {
|
|
2273
1601
|
const parseResult = WebhookBodySchema.safeParse(body ?? {});
|
|
2274
1602
|
const parsedBody = parseResult.success ? parseResult.data : {};
|
|
@@ -2284,20 +1612,1518 @@ function parseWebhookEvent(body, searchParams) {
|
|
|
2284
1612
|
raw: parsedBody
|
|
2285
1613
|
};
|
|
2286
1614
|
}
|
|
2287
|
-
function verifyWebhookSignature(params) {
|
|
2288
|
-
if (!params.signatureHeader || !params.requestId) return false;
|
|
2289
|
-
const parts = Object.fromEntries(
|
|
2290
|
-
params.signatureHeader.split(",").map((segment) => segment.trim().split("="))
|
|
2291
|
-
);
|
|
2292
|
-
const ts = parts.ts;
|
|
2293
|
-
const v1 = parts.v1;
|
|
2294
|
-
if (!ts || !v1) return false;
|
|
2295
|
-
const manifest = `id:${params.dataId};request-id:${params.requestId};ts:${ts};`;
|
|
2296
|
-
const expected = createHmac("sha256", params.secret).update(manifest).digest("hex");
|
|
2297
|
-
if (expected.length !== v1.length) return false;
|
|
2298
|
-
return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
|
|
2299
|
-
}
|
|
1615
|
+
function verifyWebhookSignature(params) {
|
|
1616
|
+
if (!params.signatureHeader || !params.requestId) return false;
|
|
1617
|
+
const parts = Object.fromEntries(
|
|
1618
|
+
params.signatureHeader.split(",").map((segment) => segment.trim().split("="))
|
|
1619
|
+
);
|
|
1620
|
+
const ts = parts.ts;
|
|
1621
|
+
const v1 = parts.v1;
|
|
1622
|
+
if (!ts || !v1) return false;
|
|
1623
|
+
const manifest = `id:${params.dataId};request-id:${params.requestId};ts:${ts};`;
|
|
1624
|
+
const expected = createHmac("sha256", params.secret).update(manifest).digest("hex");
|
|
1625
|
+
if (expected.length !== v1.length) return false;
|
|
1626
|
+
return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// src/tools.ts
|
|
1630
|
+
function deterministicIdempotencyKey(...parts) {
|
|
1631
|
+
const payload = parts.filter((p) => p !== void 0 && p !== null).map(String).join("|");
|
|
1632
|
+
return createHash("sha256").update(payload).digest("hex").slice(0, 32);
|
|
1633
|
+
}
|
|
1634
|
+
var DEFAULT_DESCRIPTIONS = {
|
|
1635
|
+
// ── Subscriptions ────────────────────────────────────────────────────────
|
|
1636
|
+
create_subscription: "Create a Mercado Pago recurring subscription. Returns an init_point URL where the customer must complete the FIRST payment with their card and CVV (this is a hard MP requirement; agents cannot bypass it). After they pay, MP will auto-charge at the configured frequency without further intervention.",
|
|
1637
|
+
get_subscription_status: "Check the current status of a Mercado Pago subscription. Use this to confirm the customer completed the first payment (status becomes 'authorized') or to inspect the next charge date.",
|
|
1638
|
+
cancel_subscription: "Cancel an active Mercado Pago subscription. After cancellation, MP will not charge the customer again. This action is irreversible \u2014 confirm with the user before calling.",
|
|
1639
|
+
pause_subscription: "Pause an authorized Mercado Pago subscription. Charges stop until resumed. Only works on subscriptions in 'authorized' status.",
|
|
1640
|
+
resume_subscription: "Resume a paused Mercado Pago subscription. Charges resume on the next scheduled date. Only works on subscriptions in 'paused' status.",
|
|
1641
|
+
// ── Payments ─────────────────────────────────────────────────────────────
|
|
1642
|
+
create_payment: "Create a one-time payment. Two flows: (a) with a card token from MP frontend Cardform \u2014 for transparent checkout; (b) without token, for non-card methods like 'account_money', 'rapipago', 'pagofacil'. For most agent flows where you only have a payer email and want to send them a payment link, use create_payment_preference instead (Checkout Pro hosted form). Returns the Payment object with status \u2014 typically 'approved' for account_money and 'pending' for tickets.",
|
|
1643
|
+
get_payment: "Fetch a single payment by ID. Use to confirm status after webhook arrives, or to inspect details (status_detail explains rejections).",
|
|
1644
|
+
search_payments: "Search payments with filters. Most common: by external_reference (your-system identifier) to find all payments for an order, or by status='approved' to list successful charges in a date range. Returns paginated results.",
|
|
1645
|
+
cancel_payment: "Cancel a pending or in_process payment (only works before approval). Once approved, use refund_payment instead. Common use: cancel an unpaid ticket payment that's still pending.",
|
|
1646
|
+
capture_payment: "Capture an authorized credit-card payment that was created with capture=false. Use for hold-then-capture flows (e.g., authorize on order, capture on shipment). Optional partial amount.",
|
|
1647
|
+
// ── Refunds ──────────────────────────────────────────────────────────────
|
|
1648
|
+
refund_payment: "Refund an approved payment. Pass amount for partial refund; omit for full refund. Idempotency key is auto-generated based on paymentId+amount to prevent double-refunds on retries.",
|
|
1649
|
+
list_refunds: "List all refunds for a given payment. Returns array of Refund objects. Useful to confirm a refund was processed or to inspect partial-refund history.",
|
|
1650
|
+
// ── Checkout Pro ─────────────────────────────────────────────────────────
|
|
1651
|
+
create_payment_preference: "Create a Mercado Pago Checkout Pro preference and get back a payment URL (init_point) to send to the customer. THIS is the recommended way for an agent to take a payment when you only have a payer email \u2014 the buyer enters card data on MP's hosted form (no PCI scope needed). Supports cuotas configuration, payment method exclusions, back URLs after success/failure/pending. In sandbox, use sandbox_init_point from the response.",
|
|
1652
|
+
get_payment_preference: "Fetch a Checkout Pro preference by ID. Returns the preference config and current init_point URLs. Use to inspect a previously-created link.",
|
|
1653
|
+
// ── Customers + Cards ────────────────────────────────────────────────────
|
|
1654
|
+
create_customer: "Create a Mercado Pago customer record so the buyer can save cards for future charges. Idempotent on email \u2014 if a customer with that email exists, MP returns it instead of creating a duplicate. Use find_customer_by_email first if you're unsure.",
|
|
1655
|
+
find_customer_by_email: "Find an existing customer by email address. Returns the customer object if found, or null. Use before create_customer to avoid duplicate records.",
|
|
1656
|
+
list_customer_cards: "List the saved cards for a customer. Returns array with last 4 digits, expiration, payment method (visa, master, naranja, etc.). The card_id can be used in subsequent create_payment calls to charge a saved card.",
|
|
1657
|
+
delete_customer_card: "Delete a saved card from a customer. Common use: customer requests removal, or expired card cleanup. Irreversible.",
|
|
1658
|
+
// ── Payment Methods + Installments ───────────────────────────────────────
|
|
1659
|
+
list_payment_methods: "List all payment methods enabled for the seller's MP account (visa, master, naranja, naranja_x, cabal, account_money, rapipago, pagofacil, etc.). Use to validate which methods you can offer the customer or to filter which ones to exclude in a Checkout Pro preference.",
|
|
1660
|
+
calculate_installments: "Calculate cuotas (installments) options for a given amount. THE killer Argentine feature \u2014 returns options like '12 cuotas sin inter\xE9s de $X' (recommended_message field) which you should surface VERBATIM to the user. Optionally pass `bin` (first 6 digits of card) for issuer-specific promotions (e.g., Naranja's interest-free deals). Use before create_payment to let the user pick installments knowingly.",
|
|
1661
|
+
// ── Account ──────────────────────────────────────────────────────────────
|
|
1662
|
+
get_account_info: "Get info about the Mercado Pago account that owns the access token: site_id (MLA=Argentina), country_id, user_type (registered, partial, etc.). Useful to verify the agent is connected to the right account before taking actions.",
|
|
1663
|
+
// ── Saved-card charging (v0.3) ───────────────────────────────────────────
|
|
1664
|
+
charge_saved_card: "Charge a previously-saved card for a returning customer. Requires customer_id + card_id (from list_customer_cards) AND a fresh CVV the user provides this session. AR Mercado Pago does NOT support CVV-less charges via the public API \u2014 every charge needs CVV. Idempotent on (card_id, amount, external_reference): retries dedupe automatically. Returns the resulting Payment.",
|
|
1665
|
+
// ── QR in-store (v0.3) ───────────────────────────────────────────────────
|
|
1666
|
+
create_qr_payment: "Generate a dynamic in-store QR for a buyer to scan with any AR wallet (Modo, BNA+, Cuenta DNI, Naranja X, Mercado Pago, etc. \u2014 interop is mandated by Transferencias 3.0). Requires a pre-configured POS external_id (use create_pos to set one up first if needed). Returns the qr_data string + a base64 PNG data URL ready to display. The QR expires in `expires_in_seconds` (default 600). MP fires `point_integration_wh` then `payment` webhooks when scanned.",
|
|
1667
|
+
cancel_qr_payment: "Cancel a pending QR order on a POS. Necessary if the buyer never scans \u2014 otherwise the next create_qr_payment on the same POS returns 409.",
|
|
1668
|
+
// ── Subscription Plans (v0.4) ────────────────────────────────────────────
|
|
1669
|
+
create_subscription_plan: "Create a REUSABLE subscription plan (preapproval_plan). Different from create_subscription: a plan defines price + frequency once, then customers subscribe to it via subscribe_to_plan. Use plans for SaaS-style billing (B\xE1sico/Pro/Enterprise tiers). For per-customer custom amounts, use create_subscription directly.",
|
|
1670
|
+
list_subscription_plans: "List all subscription plans defined for this MP account. Useful before create_subscription_plan to check if one already exists, or for surfacing options to a customer.",
|
|
1671
|
+
update_subscription_plan: "Update a subscription plan's reason / amount / status / back_url. Existing customer subscriptions to the plan are NOT automatically updated \u2014 only NEW subscribers get the new pricing.",
|
|
1672
|
+
subscribe_to_plan: "Subscribe a customer to an existing reusable plan. Returns a Preapproval with init_point URL where the customer completes first payment. Cleaner than create_subscription when you have fixed tiers.",
|
|
1673
|
+
list_subscription_payments: "List the auto-charge attempts (authorized_payments) under a subscription. Useful for 'show me the cobros del \xFAltimo mes for this client' or to debug a failing recurring charge.",
|
|
1674
|
+
// ── Stores + POS (v0.4) ──────────────────────────────────────────────────
|
|
1675
|
+
create_store: "Create a store under the seller's MP account. Stores are the parent entity for POSes (which generate QR payments). Required ONE-TIME setup before create_pos. Pass a unique external_id and a display name.",
|
|
1676
|
+
list_stores: "List all stores configured for this MP account. Use this to find an existing store_id before create_pos, or to surface store options to the agent.",
|
|
1677
|
+
create_pos: "Create a POS (Point of Sale) under a store. The POS's external_id is what create_qr_payment uses. Each physical checkout / counter / agent typically has its own POS. Categories are MP-defined (default 621102 = Other Food and Beverage Services).",
|
|
1678
|
+
list_pos: "List all POSes for the seller (or filtered by store_id). Use to find an existing POS before create_qr_payment, or to surface options.",
|
|
1679
|
+
// ── Disputes (v0.4 — read-only) ──────────────────────────────────────────
|
|
1680
|
+
list_payment_disputes: "List all disputes / chargebacks raised against a payment. Read-only \u2014 resolution is dashboard-only. Surface the dashboard URL `https://www.mercadopago.com.ar/disputes/{dispute_id}` to the user when they need to respond.",
|
|
1681
|
+
get_dispute: "Get details of a specific dispute including reason, amount, resolution status. Read-only.",
|
|
1682
|
+
// ── Lookup helpers (v0.4) ────────────────────────────────────────────────
|
|
1683
|
+
list_identification_types: "List valid identification types for the seller's site. AR returns: DNI, CI, LE, LC, Otro, Pasaporte, CUIT, CUIL with their min/max length. Useful to validate an identification before passing to create_payment.",
|
|
1684
|
+
list_issuers: "List card issuers (banks) that support a payment_method_id. Optionally filter by `bin` (first 6 digits of the card) for accurate issuer detection. Useful with calculate_installments \u2014 issuer-specific promos (e.g., Naranja Galicia 6 cuotas sin inter\xE9s) only appear when the issuer is identified.",
|
|
1685
|
+
// ── Webhooks management (v0.4) ───────────────────────────────────────────
|
|
1686
|
+
list_webhooks: "List all webhook subscriptions configured for this MP application. Use to see what topics + URLs are wired before adding new ones.",
|
|
1687
|
+
create_webhook: "Subscribe a webhook URL to a MP topic (payment, subscription_authorized_payment, subscription_preapproval, merchant_order, point_integration_wh). MP will POST to this URL when events of that topic fire.",
|
|
1688
|
+
update_webhook: "Update a webhook's URL or topic. Useful when you change deployment URLs without resubscribing from scratch.",
|
|
1689
|
+
delete_webhook: "Delete a webhook subscription. MP stops POSTing to it immediately.",
|
|
1690
|
+
// ── Webhook handler combo (v0.5) ─────────────────────────────────────────
|
|
1691
|
+
handle_webhook: "Process an incoming MP webhook in ONE call: verify the HMAC-SHA256 signature, parse the event, and (optionally) auto-fetch the underlying resource (Payment, Subscription, Order). Returns the structured event PLUS the full resource. USE THIS in your webhook endpoint INSTEAD of chaining verify_webhook_signature + parse_webhook_event + get_payment manually. Pass the raw request body, x-signature header, x-request-id header, and your MP webhook secret. SAFE: returns { verified: false } when signature mismatches \u2014 caller should respond 401 and stop processing. WHEN auto_fetch is true (default), the resource is fetched as the SAME MP user the client is configured for (so for marketplace integrations, instantiate a per-seller client).",
|
|
1692
|
+
// ── OAuth Marketplace (v0.5) ─────────────────────────────────────────────
|
|
1693
|
+
oauth_authorize_url: "Build the URL the SELLER (third-party MP account) visits to authorize your marketplace app. Pass the seller's redirect uri (must be whitelisted in MP dev panel) and an opaque state token (CSRF protection \u2014 bind it to the user's session). PURE FUNCTION: no network. The seller approves, MP redirects them to your `redirect_uri?code=...&state=...`. Then call oauth_exchange_code with the code.",
|
|
1694
|
+
oauth_exchange_code: "Exchange the authorization code (from the OAuth redirect) for an `OAuthToken`. Returns access_token, refresh_token, user_id, and expires_in. **PERSIST the entire response** \u2014 refresh_token is long-lived and the only way to keep the integration alive past 6h. Use the access_token to instantiate a per-seller MercadoPagoClient for marketplace flows.",
|
|
1695
|
+
oauth_refresh_token: "Refresh a per-seller access_token using the saved refresh_token. Call PROACTIVELY before expires_in elapses, or REACTIVELY on a 401 from a per-seller MercadoPagoClient. Returns a fresh OAuthToken \u2014 persist the new refresh_token (MP often returns the same value, but always replace).",
|
|
1696
|
+
// ── Order Management API (v0.5 — modern Order API) ───────────────────────
|
|
1697
|
+
create_order: "Create a new Order via MP's modern Order Management API. DIFFERENT from create_payment_preference: Order is a transactional entity with explicit lifecycle (created \u2192 processed \u2192 captured/canceled), supports MANUAL CAPTURE (auth-only, capture later \u2014 for ride-share, hotels, marketplaces) and aggregates multiple payments into one Order. Use Preference (Checkout Pro) for simple hosted pay-links; use Order when you need auth-only or multi-payment-per-order semantics. For marketplace splits, set marketplace + marketplace_fee + collector_id (the SELLER's MP user_id from oauth_exchange_code).",
|
|
1698
|
+
get_order: "Fetch an Order by ID. Returns the Order with its lifecycle status and any attached payments/refunds.",
|
|
1699
|
+
update_order: "Patch an existing Order before it's captured/canceled. Common use: update items or external_reference.",
|
|
1700
|
+
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.",
|
|
1701
|
+
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.",
|
|
1702
|
+
// ── Account / Balance / Movements / Settlements (v0.6) ───────────────────
|
|
1703
|
+
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.",
|
|
1704
|
+
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.",
|
|
1705
|
+
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.",
|
|
1706
|
+
get_settlement: "Get details of a single settlement: amount, date_scheduled, date_processed, bank_account info (CBU + bank name).",
|
|
1707
|
+
// ── 3DS analyzer (v0.6 — pure) ───────────────────────────────────────────
|
|
1708
|
+
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.",
|
|
1709
|
+
// ── Test cards (v0.6 — pure) ─────────────────────────────────────────────
|
|
1710
|
+
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."
|
|
1711
|
+
};
|
|
1712
|
+
function mercadoPagoTools(client, options) {
|
|
1713
|
+
const desc = (name) => options.descriptions?.[name] ?? DEFAULT_DESCRIPTIONS[name];
|
|
1714
|
+
return {
|
|
1715
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1716
|
+
// Subscriptions (v0.1 — kept identical for backward compatibility)
|
|
1717
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1718
|
+
create_subscription: tool({
|
|
1719
|
+
description: desc("create_subscription"),
|
|
1720
|
+
inputSchema: z.object({
|
|
1721
|
+
customer_email: z.string().email().describe("Email of the customer who will be charged"),
|
|
1722
|
+
amount_ars: z.number().positive().describe("Amount in Argentine Pesos per recurring charge"),
|
|
1723
|
+
frequency_months: z.number().int().positive().max(12).describe("Frequency in months (1=monthly, 3=quarterly, 12=yearly)"),
|
|
1724
|
+
reason: z.string().min(3).max(120).describe("Short description shown to the customer at checkout"),
|
|
1725
|
+
external_reference: z.string().optional().describe("Optional id from your system to track this subscription")
|
|
1726
|
+
}),
|
|
1727
|
+
execute: async ({ customer_email, amount_ars, frequency_months, reason, external_reference }) => {
|
|
1728
|
+
const created = await client.createPreapproval({
|
|
1729
|
+
reason,
|
|
1730
|
+
payerEmail: customer_email,
|
|
1731
|
+
amount: amount_ars,
|
|
1732
|
+
currency: "ARS",
|
|
1733
|
+
frequency: frequency_months,
|
|
1734
|
+
frequencyType: "months",
|
|
1735
|
+
backUrl: options.backUrl,
|
|
1736
|
+
...external_reference !== void 0 ? { externalReference: external_reference } : {}
|
|
1737
|
+
});
|
|
1738
|
+
await options.state.set(created.id, {
|
|
1739
|
+
status: created.status,
|
|
1740
|
+
payerEmail: customer_email,
|
|
1741
|
+
amount: amount_ars,
|
|
1742
|
+
currency: "ARS",
|
|
1743
|
+
frequency: frequency_months,
|
|
1744
|
+
frequencyType: "months",
|
|
1745
|
+
initPoint: created.init_point,
|
|
1746
|
+
...external_reference !== void 0 ? { externalReference: external_reference } : {},
|
|
1747
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1748
|
+
});
|
|
1749
|
+
return {
|
|
1750
|
+
subscription_id: created.id,
|
|
1751
|
+
status: created.status,
|
|
1752
|
+
init_point_url: created.init_point,
|
|
1753
|
+
next_step: "Send init_point_url to the customer. They must complete the first payment with card+CVV. Use get_subscription_status to confirm activation after they pay."
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
}),
|
|
1757
|
+
get_subscription_status: tool({
|
|
1758
|
+
description: desc("get_subscription_status"),
|
|
1759
|
+
inputSchema: z.object({
|
|
1760
|
+
subscription_id: z.string().describe("The Mercado Pago subscription/preapproval ID")
|
|
1761
|
+
}),
|
|
1762
|
+
execute: async ({ subscription_id }) => {
|
|
1763
|
+
const fresh = await client.getPreapproval(subscription_id);
|
|
1764
|
+
const cached = await options.state.get(subscription_id);
|
|
1765
|
+
return {
|
|
1766
|
+
subscription_id: fresh.id,
|
|
1767
|
+
status: fresh.status,
|
|
1768
|
+
payer_email: fresh.payer_email,
|
|
1769
|
+
amount: fresh.auto_recurring.transaction_amount,
|
|
1770
|
+
currency: fresh.auto_recurring.currency_id,
|
|
1771
|
+
next_payment_date: fresh.next_payment_date ?? null,
|
|
1772
|
+
last_webhook_status: cached?.lastWebhookStatus ?? null,
|
|
1773
|
+
last_webhook_at: cached?.lastWebhookAt ?? null
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
}),
|
|
1777
|
+
cancel_subscription: tool({
|
|
1778
|
+
description: desc("cancel_subscription"),
|
|
1779
|
+
inputSchema: z.object({
|
|
1780
|
+
subscription_id: z.string().describe("The Mercado Pago subscription/preapproval ID to cancel")
|
|
1781
|
+
}),
|
|
1782
|
+
execute: async ({ subscription_id }) => {
|
|
1783
|
+
const cancelled = await client.cancelPreapproval(subscription_id);
|
|
1784
|
+
await options.state.set(subscription_id, {
|
|
1785
|
+
status: cancelled.status,
|
|
1786
|
+
cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1787
|
+
});
|
|
1788
|
+
return {
|
|
1789
|
+
subscription_id: cancelled.id,
|
|
1790
|
+
status: cancelled.status,
|
|
1791
|
+
message: "Subscription cancelled. No further charges will occur."
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
}),
|
|
1795
|
+
pause_subscription: tool({
|
|
1796
|
+
description: desc("pause_subscription"),
|
|
1797
|
+
inputSchema: z.object({ subscription_id: z.string() }),
|
|
1798
|
+
execute: async ({ subscription_id }) => {
|
|
1799
|
+
const paused = await client.pausePreapproval(subscription_id);
|
|
1800
|
+
await options.state.set(subscription_id, { status: paused.status });
|
|
1801
|
+
return {
|
|
1802
|
+
subscription_id: paused.id,
|
|
1803
|
+
status: paused.status,
|
|
1804
|
+
message: "Subscription paused. Use resume_subscription to reactivate."
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
}),
|
|
1808
|
+
resume_subscription: tool({
|
|
1809
|
+
description: desc("resume_subscription"),
|
|
1810
|
+
inputSchema: z.object({ subscription_id: z.string() }),
|
|
1811
|
+
execute: async ({ subscription_id }) => {
|
|
1812
|
+
const resumed = await client.resumePreapproval(subscription_id);
|
|
1813
|
+
await options.state.set(subscription_id, { status: resumed.status });
|
|
1814
|
+
return {
|
|
1815
|
+
subscription_id: resumed.id,
|
|
1816
|
+
status: resumed.status,
|
|
1817
|
+
message: "Subscription resumed. Charges will continue on next scheduled date."
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
}),
|
|
1821
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1822
|
+
// Payments (v0.2)
|
|
1823
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1824
|
+
create_payment: tool({
|
|
1825
|
+
description: desc("create_payment"),
|
|
1826
|
+
inputSchema: z.object({
|
|
1827
|
+
amount_ars: z.number().positive().describe("Amount in ARS"),
|
|
1828
|
+
payment_method_id: z.string().describe("MP payment method id (e.g. 'account_money', 'rapipago', 'visa', 'master', 'naranja')"),
|
|
1829
|
+
payer_email: z.string().email().describe("Email of the payer. Cannot equal seller email."),
|
|
1830
|
+
token: z.string().optional().describe("Card token from MP frontend Cardform. Required for credit/debit; omit for cash/account_money."),
|
|
1831
|
+
installments: z.number().int().min(1).max(24).optional().describe("Number of installments (cuotas). Default 1. Use calculate_installments first to see options."),
|
|
1832
|
+
description: z.string().max(255).optional().describe("Short description"),
|
|
1833
|
+
external_reference: z.string().optional().describe("Your-system identifier"),
|
|
1834
|
+
identification: z.object({
|
|
1835
|
+
type: z.enum(["DNI", "CUIT", "CUIL"]),
|
|
1836
|
+
number: z.string()
|
|
1837
|
+
}).optional().describe("Payer identification \u2014 required for some payment types in AR"),
|
|
1838
|
+
statement_descriptor: z.string().max(13).optional().describe("Shows on buyer's card statement (max 13 chars)")
|
|
1839
|
+
}),
|
|
1840
|
+
execute: async (input) => {
|
|
1841
|
+
const payment = await client.createPayment({
|
|
1842
|
+
transactionAmount: input.amount_ars,
|
|
1843
|
+
paymentMethodId: input.payment_method_id,
|
|
1844
|
+
payerEmail: input.payer_email,
|
|
1845
|
+
...input.token !== void 0 ? { token: input.token } : {},
|
|
1846
|
+
...input.installments !== void 0 ? { installments: input.installments } : {},
|
|
1847
|
+
...input.description !== void 0 ? { description: input.description } : {},
|
|
1848
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1849
|
+
...input.identification !== void 0 ? { identification: input.identification } : {},
|
|
1850
|
+
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
1851
|
+
...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
|
|
1852
|
+
// Deterministic idempotency key — safe to retry, same inputs always
|
|
1853
|
+
// produce the same key (MP dedupes on its side).
|
|
1854
|
+
idempotencyKey: deterministicIdempotencyKey(
|
|
1855
|
+
"create_payment",
|
|
1856
|
+
input.external_reference ?? input.payer_email,
|
|
1857
|
+
input.amount_ars,
|
|
1858
|
+
input.payment_method_id,
|
|
1859
|
+
input.token
|
|
1860
|
+
)
|
|
1861
|
+
});
|
|
1862
|
+
return {
|
|
1863
|
+
payment_id: payment.id,
|
|
1864
|
+
status: payment.status,
|
|
1865
|
+
status_detail: payment.status_detail,
|
|
1866
|
+
amount: payment.transaction_amount,
|
|
1867
|
+
currency: payment.currency_id,
|
|
1868
|
+
installments: payment.installments,
|
|
1869
|
+
payment_method: payment.payment_method_id,
|
|
1870
|
+
payer_email: payment.payer?.email ?? null,
|
|
1871
|
+
external_reference: payment.external_reference,
|
|
1872
|
+
date_created: payment.date_created,
|
|
1873
|
+
date_approved: payment.date_approved
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
}),
|
|
1877
|
+
get_payment: tool({
|
|
1878
|
+
description: desc("get_payment"),
|
|
1879
|
+
inputSchema: z.object({
|
|
1880
|
+
payment_id: z.string().describe("The MP payment ID")
|
|
1881
|
+
}),
|
|
1882
|
+
execute: async ({ payment_id }) => {
|
|
1883
|
+
const p = await client.getPayment(payment_id);
|
|
1884
|
+
return {
|
|
1885
|
+
payment_id: p.id,
|
|
1886
|
+
status: p.status,
|
|
1887
|
+
status_detail: p.status_detail,
|
|
1888
|
+
amount: p.transaction_amount,
|
|
1889
|
+
currency: p.currency_id,
|
|
1890
|
+
payment_method: p.payment_method_id,
|
|
1891
|
+
installments: p.installments,
|
|
1892
|
+
payer_email: p.payer?.email ?? null,
|
|
1893
|
+
external_reference: p.external_reference,
|
|
1894
|
+
date_created: p.date_created,
|
|
1895
|
+
date_approved: p.date_approved,
|
|
1896
|
+
net_received: p.transaction_details?.net_received_amount ?? null
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
}),
|
|
1900
|
+
search_payments: tool({
|
|
1901
|
+
description: desc("search_payments"),
|
|
1902
|
+
inputSchema: z.object({
|
|
1903
|
+
external_reference: z.string().optional(),
|
|
1904
|
+
status: z.string().optional().describe("'approved' | 'pending' | 'rejected' | 'cancelled' | 'refunded' etc."),
|
|
1905
|
+
payer_email: z.string().optional(),
|
|
1906
|
+
begin_date: z.string().optional().describe("ISO 8601, e.g. 2026-01-01T00:00:00Z"),
|
|
1907
|
+
end_date: z.string().optional().describe("ISO 8601"),
|
|
1908
|
+
limit: z.number().int().min(1).max(100).optional().describe("Default 30, max 100"),
|
|
1909
|
+
offset: z.number().int().min(0).optional().describe("Pagination offset (default 0)")
|
|
1910
|
+
}),
|
|
1911
|
+
execute: async (input) => {
|
|
1912
|
+
const result = await client.searchPayments({
|
|
1913
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
1914
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
1915
|
+
...input.payer_email !== void 0 ? { payerEmail: input.payer_email } : {},
|
|
1916
|
+
...input.begin_date !== void 0 ? { beginDate: input.begin_date } : {},
|
|
1917
|
+
...input.end_date !== void 0 ? { endDate: input.end_date } : {},
|
|
1918
|
+
...input.limit !== void 0 ? { limit: input.limit } : {},
|
|
1919
|
+
...input.offset !== void 0 ? { offset: input.offset } : {}
|
|
1920
|
+
});
|
|
1921
|
+
return {
|
|
1922
|
+
total: result.paging.total,
|
|
1923
|
+
returned: result.results.length,
|
|
1924
|
+
offset: result.paging.offset,
|
|
1925
|
+
payments: result.results.map((p) => ({
|
|
1926
|
+
payment_id: p.id,
|
|
1927
|
+
status: p.status,
|
|
1928
|
+
amount: p.transaction_amount,
|
|
1929
|
+
currency: p.currency_id,
|
|
1930
|
+
payer_email: p.payer?.email ?? null,
|
|
1931
|
+
external_reference: p.external_reference,
|
|
1932
|
+
date_created: p.date_created
|
|
1933
|
+
}))
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
}),
|
|
1937
|
+
cancel_payment: tool({
|
|
1938
|
+
description: desc("cancel_payment"),
|
|
1939
|
+
inputSchema: z.object({ payment_id: z.string() }),
|
|
1940
|
+
execute: async ({ payment_id }) => {
|
|
1941
|
+
const cancelled = await client.cancelPayment(payment_id);
|
|
1942
|
+
return {
|
|
1943
|
+
payment_id: cancelled.id,
|
|
1944
|
+
status: cancelled.status,
|
|
1945
|
+
message: "Payment cancelled. If it was already approved, use refund_payment instead."
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
}),
|
|
1949
|
+
capture_payment: tool({
|
|
1950
|
+
description: desc("capture_payment"),
|
|
1951
|
+
inputSchema: z.object({
|
|
1952
|
+
payment_id: z.string(),
|
|
1953
|
+
amount_ars: z.number().positive().optional().describe("Optional partial-capture amount. Omit to capture full authorized amount.")
|
|
1954
|
+
}),
|
|
1955
|
+
execute: async ({ payment_id, amount_ars }) => {
|
|
1956
|
+
const captured = await client.capturePayment(payment_id, amount_ars);
|
|
1957
|
+
return {
|
|
1958
|
+
payment_id: captured.id,
|
|
1959
|
+
status: captured.status,
|
|
1960
|
+
amount: captured.transaction_amount
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
}),
|
|
1964
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1965
|
+
// Refunds
|
|
1966
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
1967
|
+
refund_payment: tool({
|
|
1968
|
+
description: desc("refund_payment"),
|
|
1969
|
+
inputSchema: z.object({
|
|
1970
|
+
payment_id: z.string(),
|
|
1971
|
+
amount_ars: z.number().positive().optional().describe("Partial-refund amount in ARS. Omit for full refund.")
|
|
1972
|
+
}),
|
|
1973
|
+
execute: async ({ payment_id, amount_ars }) => {
|
|
1974
|
+
const refund = await client.createRefund({
|
|
1975
|
+
paymentId: payment_id,
|
|
1976
|
+
...amount_ars !== void 0 ? { amount: amount_ars } : {},
|
|
1977
|
+
idempotencyKey: deterministicIdempotencyKey("refund", payment_id, amount_ars ?? "full")
|
|
1978
|
+
});
|
|
1979
|
+
return {
|
|
1980
|
+
refund_id: refund.id,
|
|
1981
|
+
payment_id: refund.payment_id,
|
|
1982
|
+
amount: refund.amount,
|
|
1983
|
+
status: refund.status,
|
|
1984
|
+
message: amount_ars === void 0 ? "Full refund issued. Funds return to the buyer in 3-10 business days." : `Partial refund of ${amount_ars} ARS issued.`
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
}),
|
|
1988
|
+
list_refunds: tool({
|
|
1989
|
+
description: desc("list_refunds"),
|
|
1990
|
+
inputSchema: z.object({ payment_id: z.string() }),
|
|
1991
|
+
execute: async ({ payment_id }) => {
|
|
1992
|
+
const refunds = await client.listRefunds(payment_id);
|
|
1993
|
+
return {
|
|
1994
|
+
payment_id,
|
|
1995
|
+
count: refunds.length,
|
|
1996
|
+
refunds: refunds.map((r) => ({
|
|
1997
|
+
refund_id: r.id,
|
|
1998
|
+
amount: r.amount,
|
|
1999
|
+
status: r.status,
|
|
2000
|
+
date_created: r.date_created
|
|
2001
|
+
}))
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
2004
|
+
}),
|
|
2005
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2006
|
+
// Checkout Pro
|
|
2007
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2008
|
+
create_payment_preference: tool({
|
|
2009
|
+
description: desc("create_payment_preference"),
|
|
2010
|
+
inputSchema: z.object({
|
|
2011
|
+
items: z.array(z.object({
|
|
2012
|
+
title: z.string().min(1).max(256),
|
|
2013
|
+
quantity: z.number().int().positive(),
|
|
2014
|
+
unit_price: z.number().positive(),
|
|
2015
|
+
description: z.string().optional(),
|
|
2016
|
+
picture_url: z.string().url().optional()
|
|
2017
|
+
})).min(1).describe("Items being charged. At least one required."),
|
|
2018
|
+
payer_email: z.string().email().optional().describe("Pre-fill the payer email on Checkout Pro form"),
|
|
2019
|
+
external_reference: z.string().optional(),
|
|
2020
|
+
max_installments: z.number().int().min(1).max(24).optional().describe("Limit max cuotas offered. Defaults to MP account config."),
|
|
2021
|
+
statement_descriptor: z.string().max(13).optional(),
|
|
2022
|
+
excluded_payment_types: z.array(z.enum(["credit_card", "debit_card", "ticket", "atm", "bank_transfer"])).optional().describe("Block payment types \u2014 e.g., ['ticket'] to disable Rapipago/Pago F\xE1cil")
|
|
2023
|
+
}),
|
|
2024
|
+
execute: async (input) => {
|
|
2025
|
+
const pref = await client.createPreference({
|
|
2026
|
+
items: input.items.map((it) => ({
|
|
2027
|
+
title: it.title,
|
|
2028
|
+
quantity: it.quantity,
|
|
2029
|
+
unit_price: it.unit_price,
|
|
2030
|
+
currency_id: "ARS",
|
|
2031
|
+
...it.description !== void 0 ? { description: it.description } : {},
|
|
2032
|
+
...it.picture_url !== void 0 ? { picture_url: it.picture_url } : {}
|
|
2033
|
+
})),
|
|
2034
|
+
...input.payer_email !== void 0 ? { payer: { email: input.payer_email } } : {},
|
|
2035
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
2036
|
+
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
2037
|
+
backUrls: { success: options.backUrl, failure: options.backUrl, pending: options.backUrl },
|
|
2038
|
+
autoReturn: "approved",
|
|
2039
|
+
...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
|
|
2040
|
+
...input.max_installments !== void 0 || input.excluded_payment_types !== void 0 ? {
|
|
2041
|
+
paymentMethods: {
|
|
2042
|
+
...input.max_installments !== void 0 ? { installments: input.max_installments } : {},
|
|
2043
|
+
...input.excluded_payment_types !== void 0 ? { excluded_payment_types: input.excluded_payment_types.map((id) => ({ id })) } : {}
|
|
2044
|
+
}
|
|
2045
|
+
} : {}
|
|
2046
|
+
});
|
|
2047
|
+
return {
|
|
2048
|
+
preference_id: pref.id,
|
|
2049
|
+
init_point_url: pref.init_point ?? null,
|
|
2050
|
+
sandbox_init_point_url: pref.sandbox_init_point ?? null,
|
|
2051
|
+
external_reference: pref.external_reference,
|
|
2052
|
+
date_created: pref.date_created,
|
|
2053
|
+
next_step: "Send init_point_url (or sandbox_init_point_url in sandbox) to the customer. After they pay, MP fires a webhook with the payment_id; use get_payment to confirm status."
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
}),
|
|
2057
|
+
get_payment_preference: tool({
|
|
2058
|
+
description: desc("get_payment_preference"),
|
|
2059
|
+
inputSchema: z.object({ preference_id: z.string() }),
|
|
2060
|
+
execute: async ({ preference_id }) => {
|
|
2061
|
+
const pref = await client.getPreference(preference_id);
|
|
2062
|
+
return {
|
|
2063
|
+
preference_id: pref.id,
|
|
2064
|
+
init_point_url: pref.init_point ?? null,
|
|
2065
|
+
sandbox_init_point_url: pref.sandbox_init_point ?? null,
|
|
2066
|
+
external_reference: pref.external_reference,
|
|
2067
|
+
items: pref.items,
|
|
2068
|
+
date_created: pref.date_created
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
}),
|
|
2072
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2073
|
+
// Customers + Saved Cards
|
|
2074
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2075
|
+
create_customer: tool({
|
|
2076
|
+
description: desc("create_customer"),
|
|
2077
|
+
inputSchema: z.object({
|
|
2078
|
+
email: z.string().email(),
|
|
2079
|
+
first_name: z.string().optional(),
|
|
2080
|
+
last_name: z.string().optional(),
|
|
2081
|
+
identification: z.object({
|
|
2082
|
+
type: z.enum(["DNI", "CUIT", "CUIL"]),
|
|
2083
|
+
number: z.string()
|
|
2084
|
+
}).optional(),
|
|
2085
|
+
description: z.string().optional()
|
|
2086
|
+
}),
|
|
2087
|
+
execute: async (input) => {
|
|
2088
|
+
const customer = await client.createCustomer({
|
|
2089
|
+
email: input.email,
|
|
2090
|
+
...input.first_name !== void 0 ? { firstName: input.first_name } : {},
|
|
2091
|
+
...input.last_name !== void 0 ? { lastName: input.last_name } : {},
|
|
2092
|
+
...input.identification !== void 0 ? { identification: input.identification } : {},
|
|
2093
|
+
...input.description !== void 0 ? { description: input.description } : {}
|
|
2094
|
+
});
|
|
2095
|
+
return {
|
|
2096
|
+
customer_id: customer.id,
|
|
2097
|
+
email: customer.email,
|
|
2098
|
+
first_name: customer.first_name,
|
|
2099
|
+
last_name: customer.last_name,
|
|
2100
|
+
date_created: customer.date_created
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
}),
|
|
2104
|
+
find_customer_by_email: tool({
|
|
2105
|
+
description: desc("find_customer_by_email"),
|
|
2106
|
+
inputSchema: z.object({ email: z.string().email() }),
|
|
2107
|
+
execute: async ({ email }) => {
|
|
2108
|
+
const result = await client.searchCustomers({ email, limit: 1 });
|
|
2109
|
+
const customer = result.results[0] ?? null;
|
|
2110
|
+
return customer ? {
|
|
2111
|
+
found: true,
|
|
2112
|
+
customer_id: customer.id,
|
|
2113
|
+
email: customer.email,
|
|
2114
|
+
first_name: customer.first_name,
|
|
2115
|
+
last_name: customer.last_name
|
|
2116
|
+
} : { found: false, customer_id: null };
|
|
2117
|
+
}
|
|
2118
|
+
}),
|
|
2119
|
+
list_customer_cards: tool({
|
|
2120
|
+
description: desc("list_customer_cards"),
|
|
2121
|
+
inputSchema: z.object({ customer_id: z.string() }),
|
|
2122
|
+
execute: async ({ customer_id }) => {
|
|
2123
|
+
const cards = await client.listCustomerCards(customer_id);
|
|
2124
|
+
return {
|
|
2125
|
+
customer_id,
|
|
2126
|
+
count: cards.length,
|
|
2127
|
+
cards: cards.map((c) => ({
|
|
2128
|
+
card_id: c.id,
|
|
2129
|
+
last_four_digits: c.last_four_digits,
|
|
2130
|
+
expiration_month: c.expiration_month,
|
|
2131
|
+
expiration_year: c.expiration_year,
|
|
2132
|
+
payment_method: c.payment_method?.id ?? null,
|
|
2133
|
+
payment_method_name: c.payment_method?.name ?? null
|
|
2134
|
+
}))
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
}),
|
|
2138
|
+
delete_customer_card: tool({
|
|
2139
|
+
description: desc("delete_customer_card"),
|
|
2140
|
+
inputSchema: z.object({
|
|
2141
|
+
customer_id: z.string(),
|
|
2142
|
+
card_id: z.string()
|
|
2143
|
+
}),
|
|
2144
|
+
execute: async ({ customer_id, card_id }) => {
|
|
2145
|
+
await client.deleteCustomerCard(customer_id, card_id);
|
|
2146
|
+
return { customer_id, card_id, deleted: true };
|
|
2147
|
+
}
|
|
2148
|
+
}),
|
|
2149
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2150
|
+
// Payment Methods + Installments
|
|
2151
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2152
|
+
list_payment_methods: tool({
|
|
2153
|
+
description: desc("list_payment_methods"),
|
|
2154
|
+
inputSchema: z.object({}),
|
|
2155
|
+
execute: async () => {
|
|
2156
|
+
const methods = await client.listPaymentMethods();
|
|
2157
|
+
return {
|
|
2158
|
+
count: methods.length,
|
|
2159
|
+
methods: methods.map((m) => ({
|
|
2160
|
+
id: m.id,
|
|
2161
|
+
name: m.name,
|
|
2162
|
+
payment_type: m.payment_type_id,
|
|
2163
|
+
status: m.status,
|
|
2164
|
+
min_amount: m.min_allowed_amount,
|
|
2165
|
+
max_amount: m.max_allowed_amount
|
|
2166
|
+
}))
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
}),
|
|
2170
|
+
calculate_installments: tool({
|
|
2171
|
+
description: desc("calculate_installments"),
|
|
2172
|
+
inputSchema: z.object({
|
|
2173
|
+
amount_ars: z.number().positive(),
|
|
2174
|
+
payment_method_id: z.string().optional().describe("E.g. 'visa', 'master', 'naranja'. Omit for all available methods."),
|
|
2175
|
+
bin: z.string().min(6).max(8).optional().describe("First 6-8 digits of card for issuer-specific offers (e.g., Naranja interest-free promotions)")
|
|
2176
|
+
}),
|
|
2177
|
+
execute: async (input) => {
|
|
2178
|
+
const offers = await client.getInstallments({
|
|
2179
|
+
amount: input.amount_ars,
|
|
2180
|
+
...input.payment_method_id !== void 0 ? { paymentMethodId: input.payment_method_id } : {},
|
|
2181
|
+
...input.bin !== void 0 ? { bin: input.bin } : {}
|
|
2182
|
+
});
|
|
2183
|
+
return {
|
|
2184
|
+
amount: input.amount_ars,
|
|
2185
|
+
offers: offers.map((o) => ({
|
|
2186
|
+
payment_method_id: o.payment_method_id,
|
|
2187
|
+
payment_type_id: o.payment_type_id,
|
|
2188
|
+
issuer_name: o.issuer?.name ?? null,
|
|
2189
|
+
options: o.payer_costs.map((pc) => ({
|
|
2190
|
+
installments: pc.installments,
|
|
2191
|
+
installment_amount: pc.installment_amount,
|
|
2192
|
+
total_amount: pc.total_amount,
|
|
2193
|
+
installment_rate: pc.installment_rate,
|
|
2194
|
+
recommended_message: pc.recommended_message
|
|
2195
|
+
}))
|
|
2196
|
+
}))
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
}),
|
|
2200
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2201
|
+
// Account
|
|
2202
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2203
|
+
get_account_info: tool({
|
|
2204
|
+
description: desc("get_account_info"),
|
|
2205
|
+
inputSchema: z.object({}),
|
|
2206
|
+
execute: async () => {
|
|
2207
|
+
const me = await client.getMe();
|
|
2208
|
+
return {
|
|
2209
|
+
account_id: me.id,
|
|
2210
|
+
email: me.email,
|
|
2211
|
+
nickname: me.nickname,
|
|
2212
|
+
country_id: me.country_id,
|
|
2213
|
+
site_id: me.site_id,
|
|
2214
|
+
user_type: me.user_type
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
}),
|
|
2218
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2219
|
+
// Saved-card charging (v0.3)
|
|
2220
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2221
|
+
charge_saved_card: tool({
|
|
2222
|
+
description: desc("charge_saved_card"),
|
|
2223
|
+
inputSchema: z.object({
|
|
2224
|
+
customer_id: z.string().describe("MP customer id (from create_customer / find_customer_by_email)"),
|
|
2225
|
+
card_id: z.string().describe("Saved card id (from list_customer_cards)"),
|
|
2226
|
+
security_code: z.string().regex(/^\d{3,4}$/).describe("CVV \u2014 3 digits (Visa/Master) or 4 (Amex). User must provide this each charge in AR."),
|
|
2227
|
+
amount_ars: z.number().positive(),
|
|
2228
|
+
description: z.string().min(1).max(255),
|
|
2229
|
+
installments: z.number().int().min(1).max(24).optional().describe("Default 1. Use calculate_installments first to pick a valid count."),
|
|
2230
|
+
external_reference: z.string().optional(),
|
|
2231
|
+
statement_descriptor: z.string().max(13).optional()
|
|
2232
|
+
}),
|
|
2233
|
+
execute: async (input) => {
|
|
2234
|
+
const payment = await client.chargeSavedCard({
|
|
2235
|
+
customerId: input.customer_id,
|
|
2236
|
+
cardId: input.card_id,
|
|
2237
|
+
securityCode: input.security_code,
|
|
2238
|
+
amount: input.amount_ars,
|
|
2239
|
+
description: input.description,
|
|
2240
|
+
...input.installments !== void 0 ? { installments: input.installments } : {},
|
|
2241
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
2242
|
+
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
2243
|
+
idempotencyKey: deterministicIdempotencyKey(
|
|
2244
|
+
"charge_saved_card",
|
|
2245
|
+
input.card_id,
|
|
2246
|
+
input.amount_ars,
|
|
2247
|
+
input.external_reference
|
|
2248
|
+
)
|
|
2249
|
+
});
|
|
2250
|
+
return {
|
|
2251
|
+
payment_id: payment.id,
|
|
2252
|
+
status: payment.status,
|
|
2253
|
+
status_detail: payment.status_detail,
|
|
2254
|
+
amount: payment.transaction_amount,
|
|
2255
|
+
installments: payment.installments,
|
|
2256
|
+
payment_method: payment.payment_method_id,
|
|
2257
|
+
customer_id: input.customer_id,
|
|
2258
|
+
card_id: input.card_id,
|
|
2259
|
+
external_reference: payment.external_reference,
|
|
2260
|
+
date_approved: payment.date_approved
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
}),
|
|
2264
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2265
|
+
// QR in-store (v0.3)
|
|
2266
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2267
|
+
create_qr_payment: tool({
|
|
2268
|
+
description: desc("create_qr_payment"),
|
|
2269
|
+
inputSchema: z.object({
|
|
2270
|
+
external_pos_id: z.string().describe("Pre-configured POS external_id from MP dashboard. Required."),
|
|
2271
|
+
amount_ars: z.number().positive(),
|
|
2272
|
+
title: z.string().min(1).max(80).describe("Display title shown when scanning"),
|
|
2273
|
+
description: z.string().max(255).optional(),
|
|
2274
|
+
external_reference: z.string().optional(),
|
|
2275
|
+
notification_url: z.string().url().optional().describe("Webhook URL \u2014 falls back to dashboard config if omitted"),
|
|
2276
|
+
expires_in_seconds: z.number().int().min(60).max(3600).optional().describe("Default 600 (10 min)")
|
|
2277
|
+
}),
|
|
2278
|
+
execute: async (input) => {
|
|
2279
|
+
const QRCode = (await import('qrcode')).default;
|
|
2280
|
+
const me = await client.getMe();
|
|
2281
|
+
const userId = String(me.id);
|
|
2282
|
+
const expiresAt = new Date(
|
|
2283
|
+
Date.now() + (input.expires_in_seconds ?? 600) * 1e3
|
|
2284
|
+
).toISOString();
|
|
2285
|
+
const qr = await client.createQrPayment(userId, {
|
|
2286
|
+
externalPosId: input.external_pos_id,
|
|
2287
|
+
totalAmount: input.amount_ars,
|
|
2288
|
+
title: input.title,
|
|
2289
|
+
...input.description !== void 0 ? { description: input.description } : {},
|
|
2290
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
2291
|
+
...input.notification_url !== void 0 ? { notificationUrl: input.notification_url } : {},
|
|
2292
|
+
expirationDate: expiresAt
|
|
2293
|
+
});
|
|
2294
|
+
const qrDataUrl = await QRCode.toDataURL(qr.qr_data, {
|
|
2295
|
+
errorCorrectionLevel: "M",
|
|
2296
|
+
margin: 1,
|
|
2297
|
+
width: 512
|
|
2298
|
+
});
|
|
2299
|
+
return {
|
|
2300
|
+
in_store_order_id: qr.in_store_order_id,
|
|
2301
|
+
qr_data: qr.qr_data,
|
|
2302
|
+
qr_data_url: qrDataUrl,
|
|
2303
|
+
expires_at: expiresAt,
|
|
2304
|
+
external_pos_id: input.external_pos_id,
|
|
2305
|
+
amount: input.amount_ars,
|
|
2306
|
+
next_step: "Display the qr_data_url image to the buyer. Wait for the payment webhook (point_integration_wh fires first, then payment topic). If buyer doesn't scan in time, call cancel_qr_payment to free the POS."
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
}),
|
|
2310
|
+
cancel_qr_payment: tool({
|
|
2311
|
+
description: desc("cancel_qr_payment"),
|
|
2312
|
+
inputSchema: z.object({
|
|
2313
|
+
external_pos_id: z.string()
|
|
2314
|
+
}),
|
|
2315
|
+
execute: async ({ external_pos_id }) => {
|
|
2316
|
+
const me = await client.getMe();
|
|
2317
|
+
await client.cancelQrPayment(String(me.id), external_pos_id);
|
|
2318
|
+
return { external_pos_id, cancelled: true };
|
|
2319
|
+
}
|
|
2320
|
+
}),
|
|
2321
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2322
|
+
// Subscription Plans (v0.4)
|
|
2323
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2324
|
+
create_subscription_plan: tool({
|
|
2325
|
+
description: desc("create_subscription_plan"),
|
|
2326
|
+
inputSchema: z.object({
|
|
2327
|
+
reason: z.string().min(3).max(120).describe("Plan name shown at checkout"),
|
|
2328
|
+
amount_ars: z.number().positive(),
|
|
2329
|
+
frequency_months: z.number().int().min(1).max(12),
|
|
2330
|
+
back_url: z.string().url().describe("HTTPS URL where MP redirects after first payment"),
|
|
2331
|
+
external_reference: z.string().optional(),
|
|
2332
|
+
free_trial_days: z.number().int().min(1).max(60).optional().describe("Free trial period in days before first charge")
|
|
2333
|
+
}),
|
|
2334
|
+
execute: async (input) => {
|
|
2335
|
+
const plan = await client.createSubscriptionPlan({
|
|
2336
|
+
reason: input.reason,
|
|
2337
|
+
amount: input.amount_ars,
|
|
2338
|
+
currency: "ARS",
|
|
2339
|
+
frequency: input.frequency_months,
|
|
2340
|
+
frequencyType: "months",
|
|
2341
|
+
backUrl: input.back_url,
|
|
2342
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
2343
|
+
...input.free_trial_days !== void 0 ? { freeTrialFrequency: input.free_trial_days, freeTrialFrequencyType: "days" } : {}
|
|
2344
|
+
});
|
|
2345
|
+
return {
|
|
2346
|
+
plan_id: plan.id,
|
|
2347
|
+
status: plan.status,
|
|
2348
|
+
reason: plan.reason,
|
|
2349
|
+
amount: plan.auto_recurring.transaction_amount,
|
|
2350
|
+
currency: plan.auto_recurring.currency_id,
|
|
2351
|
+
frequency: `${plan.auto_recurring.frequency} ${plan.auto_recurring.frequency_type}`,
|
|
2352
|
+
external_reference: plan.external_reference,
|
|
2353
|
+
next_step: "Use subscribe_to_plan to enroll customers in this plan, or share its ID for them to subscribe via your frontend."
|
|
2354
|
+
};
|
|
2355
|
+
}
|
|
2356
|
+
}),
|
|
2357
|
+
list_subscription_plans: tool({
|
|
2358
|
+
description: desc("list_subscription_plans"),
|
|
2359
|
+
inputSchema: z.object({
|
|
2360
|
+
status: z.string().optional(),
|
|
2361
|
+
limit: z.number().int().min(1).max(100).optional()
|
|
2362
|
+
}),
|
|
2363
|
+
execute: async (input) => {
|
|
2364
|
+
const result = await client.listSubscriptionPlans({
|
|
2365
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
2366
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
2367
|
+
});
|
|
2368
|
+
return {
|
|
2369
|
+
total: result.paging.total,
|
|
2370
|
+
plans: result.results.map((p) => ({
|
|
2371
|
+
plan_id: p.id,
|
|
2372
|
+
reason: p.reason,
|
|
2373
|
+
status: p.status,
|
|
2374
|
+
amount: p.auto_recurring.transaction_amount,
|
|
2375
|
+
currency: p.auto_recurring.currency_id,
|
|
2376
|
+
frequency: `${p.auto_recurring.frequency} ${p.auto_recurring.frequency_type}`
|
|
2377
|
+
}))
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
}),
|
|
2381
|
+
update_subscription_plan: tool({
|
|
2382
|
+
description: desc("update_subscription_plan"),
|
|
2383
|
+
inputSchema: z.object({
|
|
2384
|
+
plan_id: z.string(),
|
|
2385
|
+
reason: z.string().optional(),
|
|
2386
|
+
amount_ars: z.number().positive().optional(),
|
|
2387
|
+
status: z.enum(["active", "cancelled"]).optional(),
|
|
2388
|
+
back_url: z.string().url().optional()
|
|
2389
|
+
}),
|
|
2390
|
+
execute: async (input) => {
|
|
2391
|
+
const updated = await client.updateSubscriptionPlan(input.plan_id, {
|
|
2392
|
+
...input.reason !== void 0 ? { reason: input.reason } : {},
|
|
2393
|
+
...input.amount_ars !== void 0 ? { amount: input.amount_ars } : {},
|
|
2394
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
2395
|
+
...input.back_url !== void 0 ? { backUrl: input.back_url } : {}
|
|
2396
|
+
});
|
|
2397
|
+
return {
|
|
2398
|
+
plan_id: updated.id,
|
|
2399
|
+
status: updated.status,
|
|
2400
|
+
reason: updated.reason,
|
|
2401
|
+
amount: updated.auto_recurring.transaction_amount,
|
|
2402
|
+
message: input.amount_ars !== void 0 ? "Updated. Existing subscribers keep their old amount; only NEW subscribers get the new pricing." : "Plan updated."
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
}),
|
|
2406
|
+
subscribe_to_plan: tool({
|
|
2407
|
+
description: desc("subscribe_to_plan"),
|
|
2408
|
+
inputSchema: z.object({
|
|
2409
|
+
plan_id: z.string(),
|
|
2410
|
+
customer_email: z.string().email(),
|
|
2411
|
+
external_reference: z.string().optional()
|
|
2412
|
+
}),
|
|
2413
|
+
execute: async (input) => {
|
|
2414
|
+
const sub = await client.subscribeToPlan({
|
|
2415
|
+
planId: input.plan_id,
|
|
2416
|
+
payerEmail: input.customer_email,
|
|
2417
|
+
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {}
|
|
2418
|
+
});
|
|
2419
|
+
return {
|
|
2420
|
+
subscription_id: sub.id,
|
|
2421
|
+
status: sub.status,
|
|
2422
|
+
payer_email: sub.payer_email,
|
|
2423
|
+
init_point_url: sub.init_point,
|
|
2424
|
+
next_step: "Send init_point_url to the customer for first payment with card+CVV."
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
}),
|
|
2428
|
+
list_subscription_payments: tool({
|
|
2429
|
+
description: desc("list_subscription_payments"),
|
|
2430
|
+
inputSchema: z.object({
|
|
2431
|
+
subscription_id: z.string(),
|
|
2432
|
+
limit: z.number().int().min(1).max(100).optional()
|
|
2433
|
+
}),
|
|
2434
|
+
execute: async (input) => {
|
|
2435
|
+
const result = await client.listSubscriptionPayments(input.subscription_id, {
|
|
2436
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
2437
|
+
});
|
|
2438
|
+
return {
|
|
2439
|
+
subscription_id: input.subscription_id,
|
|
2440
|
+
total: result.paging.total,
|
|
2441
|
+
payments: result.results.map((p) => ({
|
|
2442
|
+
authorized_payment_id: p.id,
|
|
2443
|
+
payment_id: p.payment_id ?? null,
|
|
2444
|
+
status: p.status,
|
|
2445
|
+
amount: p.transaction_amount ?? null,
|
|
2446
|
+
currency: p.currency_id ?? null,
|
|
2447
|
+
debit_date: p.debit_date ?? null,
|
|
2448
|
+
next_retry_date: p.next_retry_date ?? null,
|
|
2449
|
+
retry_attempt: p.retry_attempt ?? 0,
|
|
2450
|
+
reason: p.reason ?? null
|
|
2451
|
+
}))
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
}),
|
|
2455
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2456
|
+
// Stores + POS (v0.4)
|
|
2457
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2458
|
+
create_store: tool({
|
|
2459
|
+
description: desc("create_store"),
|
|
2460
|
+
inputSchema: z.object({
|
|
2461
|
+
name: z.string().min(1).max(80),
|
|
2462
|
+
external_id: z.string().min(1).max(64).describe("Unique within the seller's stores"),
|
|
2463
|
+
address_line: z.string().optional(),
|
|
2464
|
+
city_name: z.string().optional(),
|
|
2465
|
+
state_name: z.string().optional()
|
|
2466
|
+
}),
|
|
2467
|
+
execute: async (input) => {
|
|
2468
|
+
const me = await client.getMe();
|
|
2469
|
+
const store = await client.createStore(String(me.id), {
|
|
2470
|
+
name: input.name,
|
|
2471
|
+
externalId: input.external_id,
|
|
2472
|
+
...input.address_line || input.city_name || input.state_name ? {
|
|
2473
|
+
location: {
|
|
2474
|
+
...input.address_line ? { addressLine: input.address_line } : {},
|
|
2475
|
+
...input.city_name ? { cityName: input.city_name } : {},
|
|
2476
|
+
...input.state_name ? { stateName: input.state_name } : {},
|
|
2477
|
+
countryId: "AR"
|
|
2478
|
+
}
|
|
2479
|
+
} : {}
|
|
2480
|
+
});
|
|
2481
|
+
return {
|
|
2482
|
+
store_id: store.id,
|
|
2483
|
+
name: store.name,
|
|
2484
|
+
external_id: store.external_id,
|
|
2485
|
+
next_step: "Use create_pos with this store_id to add a Point of Sale where create_qr_payment can issue QRs."
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
}),
|
|
2489
|
+
list_stores: tool({
|
|
2490
|
+
description: desc("list_stores"),
|
|
2491
|
+
inputSchema: z.object({
|
|
2492
|
+
limit: z.number().int().min(1).max(100).optional()
|
|
2493
|
+
}),
|
|
2494
|
+
execute: async (input) => {
|
|
2495
|
+
const me = await client.getMe();
|
|
2496
|
+
const result = await client.listStores(String(me.id), {
|
|
2497
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
2498
|
+
});
|
|
2499
|
+
return {
|
|
2500
|
+
total: result.paging.total,
|
|
2501
|
+
stores: result.results.map((s) => ({
|
|
2502
|
+
store_id: s.id,
|
|
2503
|
+
name: s.name ?? null,
|
|
2504
|
+
external_id: s.external_id ?? null
|
|
2505
|
+
}))
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
}),
|
|
2509
|
+
create_pos: tool({
|
|
2510
|
+
description: desc("create_pos"),
|
|
2511
|
+
inputSchema: z.object({
|
|
2512
|
+
name: z.string().min(1).max(80),
|
|
2513
|
+
external_id: z.string().min(1).max(64).describe("Unique within the store. This is what create_qr_payment uses."),
|
|
2514
|
+
store_id: z.string().describe("From create_store / list_stores"),
|
|
2515
|
+
category: z.number().int().optional().describe("MP category code, default 621102 (other food/beverage)"),
|
|
2516
|
+
fixed_amount: z.boolean().optional().describe("True for static QR with fixed amount; false (default) for dynamic per-order QR")
|
|
2517
|
+
}),
|
|
2518
|
+
execute: async (input) => {
|
|
2519
|
+
const pos = await client.createPos({
|
|
2520
|
+
name: input.name,
|
|
2521
|
+
externalId: input.external_id,
|
|
2522
|
+
storeId: input.store_id,
|
|
2523
|
+
...input.category !== void 0 ? { category: input.category } : {},
|
|
2524
|
+
...input.fixed_amount !== void 0 ? { fixedAmount: input.fixed_amount } : {}
|
|
2525
|
+
});
|
|
2526
|
+
return {
|
|
2527
|
+
pos_id: pos.id,
|
|
2528
|
+
external_id: pos.external_id,
|
|
2529
|
+
store_id: pos.store_id,
|
|
2530
|
+
name: pos.name,
|
|
2531
|
+
next_step: "Use create_qr_payment with this external_id to start issuing dynamic QRs from this POS."
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
}),
|
|
2535
|
+
list_pos: tool({
|
|
2536
|
+
description: desc("list_pos"),
|
|
2537
|
+
inputSchema: z.object({
|
|
2538
|
+
store_id: z.string().optional(),
|
|
2539
|
+
limit: z.number().int().min(1).max(100).optional()
|
|
2540
|
+
}),
|
|
2541
|
+
execute: async (input) => {
|
|
2542
|
+
const result = await client.listPos({
|
|
2543
|
+
...input.store_id !== void 0 ? { storeId: input.store_id } : {},
|
|
2544
|
+
...input.limit !== void 0 ? { limit: input.limit } : {}
|
|
2545
|
+
});
|
|
2546
|
+
return {
|
|
2547
|
+
total: result.paging.total,
|
|
2548
|
+
pos: result.results.map((p) => ({
|
|
2549
|
+
pos_id: p.id,
|
|
2550
|
+
external_id: p.external_id ?? null,
|
|
2551
|
+
store_id: p.store_id ?? null,
|
|
2552
|
+
name: p.name ?? null
|
|
2553
|
+
}))
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
}),
|
|
2557
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2558
|
+
// Disputes (v0.4 — read-only)
|
|
2559
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2560
|
+
list_payment_disputes: tool({
|
|
2561
|
+
description: desc("list_payment_disputes"),
|
|
2562
|
+
inputSchema: z.object({ payment_id: z.string() }),
|
|
2563
|
+
execute: async ({ payment_id }) => {
|
|
2564
|
+
const disputes = await client.listPaymentDisputes(payment_id);
|
|
2565
|
+
return {
|
|
2566
|
+
payment_id,
|
|
2567
|
+
count: disputes.length,
|
|
2568
|
+
disputes: disputes.map((d) => ({
|
|
2569
|
+
dispute_id: d.id,
|
|
2570
|
+
status: d.status,
|
|
2571
|
+
amount: d.amount ?? null,
|
|
2572
|
+
reason: d.reason ?? null,
|
|
2573
|
+
date_created: d.date_created ?? null,
|
|
2574
|
+
dashboard_url: `https://www.mercadopago.com.ar/disputes/${d.id}`
|
|
2575
|
+
}))
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
}),
|
|
2579
|
+
get_dispute: tool({
|
|
2580
|
+
description: desc("get_dispute"),
|
|
2581
|
+
inputSchema: z.object({
|
|
2582
|
+
payment_id: z.string(),
|
|
2583
|
+
dispute_id: z.string()
|
|
2584
|
+
}),
|
|
2585
|
+
execute: async ({ payment_id, dispute_id }) => {
|
|
2586
|
+
const d = await client.getDispute(payment_id, dispute_id);
|
|
2587
|
+
return {
|
|
2588
|
+
dispute_id: d.id,
|
|
2589
|
+
status: d.status,
|
|
2590
|
+
amount: d.amount ?? null,
|
|
2591
|
+
reason: d.reason ?? null,
|
|
2592
|
+
reason_description: d.reason_description ?? null,
|
|
2593
|
+
resolution: d.resolution ?? null,
|
|
2594
|
+
date_created: d.date_created ?? null,
|
|
2595
|
+
dashboard_url: `https://www.mercadopago.com.ar/disputes/${d.id}`
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
}),
|
|
2599
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2600
|
+
// Lookup helpers (v0.4)
|
|
2601
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2602
|
+
list_identification_types: tool({
|
|
2603
|
+
description: desc("list_identification_types"),
|
|
2604
|
+
inputSchema: z.object({}),
|
|
2605
|
+
execute: async () => {
|
|
2606
|
+
const types = await client.listIdentificationTypes();
|
|
2607
|
+
return {
|
|
2608
|
+
count: types.length,
|
|
2609
|
+
types: types.map((t) => ({
|
|
2610
|
+
id: t.id,
|
|
2611
|
+
name: t.name,
|
|
2612
|
+
type: t.type,
|
|
2613
|
+
min_length: t.min_length ?? null,
|
|
2614
|
+
max_length: t.max_length ?? null
|
|
2615
|
+
}))
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
}),
|
|
2619
|
+
list_issuers: tool({
|
|
2620
|
+
description: desc("list_issuers"),
|
|
2621
|
+
inputSchema: z.object({
|
|
2622
|
+
payment_method_id: z.string().describe("E.g. 'visa', 'master', 'naranja'"),
|
|
2623
|
+
bin: z.string().min(6).max(8).optional().describe("First 6-8 digits of card for precise issuer detection")
|
|
2624
|
+
}),
|
|
2625
|
+
execute: async (input) => {
|
|
2626
|
+
const issuers = await client.listIssuers({
|
|
2627
|
+
paymentMethodId: input.payment_method_id,
|
|
2628
|
+
...input.bin !== void 0 ? { bin: input.bin } : {}
|
|
2629
|
+
});
|
|
2630
|
+
return {
|
|
2631
|
+
payment_method_id: input.payment_method_id,
|
|
2632
|
+
count: issuers.length,
|
|
2633
|
+
issuers: issuers.map((i) => ({
|
|
2634
|
+
issuer_id: i.id,
|
|
2635
|
+
name: i.name,
|
|
2636
|
+
status: i.status ?? null
|
|
2637
|
+
}))
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
}),
|
|
2641
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2642
|
+
// Webhooks management (v0.4)
|
|
2643
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2644
|
+
list_webhooks: tool({
|
|
2645
|
+
description: desc("list_webhooks"),
|
|
2646
|
+
inputSchema: z.object({}),
|
|
2647
|
+
execute: async () => {
|
|
2648
|
+
const hooks = await client.listWebhooks();
|
|
2649
|
+
return {
|
|
2650
|
+
count: hooks.length,
|
|
2651
|
+
webhooks: hooks.map((h) => ({
|
|
2652
|
+
webhook_id: h.id,
|
|
2653
|
+
url: h.url ?? null,
|
|
2654
|
+
topic: h.topic ?? null,
|
|
2655
|
+
status: h.status ?? null,
|
|
2656
|
+
date_created: h.date_created ?? null
|
|
2657
|
+
}))
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2660
|
+
}),
|
|
2661
|
+
create_webhook: tool({
|
|
2662
|
+
description: desc("create_webhook"),
|
|
2663
|
+
inputSchema: z.object({
|
|
2664
|
+
url: z.string().url(),
|
|
2665
|
+
topic: z.string().describe("E.g. 'payment', 'subscription_authorized_payment', 'subscription_preapproval', 'merchant_order', 'point_integration_wh'")
|
|
2666
|
+
}),
|
|
2667
|
+
execute: async ({ url, topic }) => {
|
|
2668
|
+
const hook = await client.createWebhook({ url, topic });
|
|
2669
|
+
return {
|
|
2670
|
+
webhook_id: hook.id,
|
|
2671
|
+
url: hook.url ?? url,
|
|
2672
|
+
topic: hook.topic ?? topic,
|
|
2673
|
+
status: hook.status ?? null
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
}),
|
|
2677
|
+
update_webhook: tool({
|
|
2678
|
+
description: desc("update_webhook"),
|
|
2679
|
+
inputSchema: z.object({
|
|
2680
|
+
webhook_id: z.string(),
|
|
2681
|
+
url: z.string().url().optional(),
|
|
2682
|
+
topic: z.string().optional()
|
|
2683
|
+
}),
|
|
2684
|
+
execute: async (input) => {
|
|
2685
|
+
const hook = await client.updateWebhook(input.webhook_id, {
|
|
2686
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
2687
|
+
...input.topic !== void 0 ? { topic: input.topic } : {}
|
|
2688
|
+
});
|
|
2689
|
+
return {
|
|
2690
|
+
webhook_id: hook.id,
|
|
2691
|
+
url: hook.url ?? null,
|
|
2692
|
+
topic: hook.topic ?? null,
|
|
2693
|
+
status: hook.status ?? null
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
}),
|
|
2697
|
+
delete_webhook: tool({
|
|
2698
|
+
description: desc("delete_webhook"),
|
|
2699
|
+
inputSchema: z.object({ webhook_id: z.string() }),
|
|
2700
|
+
execute: async ({ webhook_id }) => {
|
|
2701
|
+
await client.deleteWebhook(webhook_id);
|
|
2702
|
+
return { webhook_id, deleted: true };
|
|
2703
|
+
}
|
|
2704
|
+
}),
|
|
2705
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2706
|
+
// v0.5 — Webhook handler combo
|
|
2707
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2708
|
+
handle_webhook: tool({
|
|
2709
|
+
description: desc("handle_webhook"),
|
|
2710
|
+
inputSchema: z.object({
|
|
2711
|
+
raw_body: z.string().describe(
|
|
2712
|
+
"The raw JSON body of the webhook request, exactly as received (do NOT re-stringify). Pass `await req.text()` from your handler."
|
|
2713
|
+
),
|
|
2714
|
+
signature_header: z.string().nullable().describe("Value of the `x-signature` request header."),
|
|
2715
|
+
request_id_header: z.string().nullable().describe("Value of the `x-request-id` request header."),
|
|
2716
|
+
auto_fetch: z.boolean().optional().default(true).describe(
|
|
2717
|
+
"If true (default), fetch the underlying resource (Payment, Subscription, etc.) AS the MP user the client is configured for. Set to false to skip the fetch (faster, useful when you only need the topic+id)."
|
|
2718
|
+
)
|
|
2719
|
+
}),
|
|
2720
|
+
execute: async ({
|
|
2721
|
+
raw_body,
|
|
2722
|
+
signature_header,
|
|
2723
|
+
request_id_header,
|
|
2724
|
+
auto_fetch
|
|
2725
|
+
}) => {
|
|
2726
|
+
if (!options.webhookSecret) {
|
|
2727
|
+
return {
|
|
2728
|
+
verified: false,
|
|
2729
|
+
error: "webhookSecret not configured in mercadoPagoTools options. Pass it from MP dev panel \u2192 Notificaciones \u2192 Webhooks.",
|
|
2730
|
+
event: null,
|
|
2731
|
+
resource: null
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
let parsedBody;
|
|
2735
|
+
try {
|
|
2736
|
+
parsedBody = JSON.parse(raw_body);
|
|
2737
|
+
} catch {
|
|
2738
|
+
return {
|
|
2739
|
+
verified: false,
|
|
2740
|
+
error: "raw_body is not valid JSON.",
|
|
2741
|
+
event: null,
|
|
2742
|
+
resource: null
|
|
2743
|
+
};
|
|
2744
|
+
}
|
|
2745
|
+
const event = parseWebhookEvent(parsedBody);
|
|
2746
|
+
if (!event) {
|
|
2747
|
+
return {
|
|
2748
|
+
verified: false,
|
|
2749
|
+
error: "Could not extract topic + dataId from webhook body.",
|
|
2750
|
+
event: null,
|
|
2751
|
+
resource: null
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
const verified = verifyWebhookSignature({
|
|
2755
|
+
requestId: request_id_header,
|
|
2756
|
+
dataId: event.dataId,
|
|
2757
|
+
signatureHeader: signature_header,
|
|
2758
|
+
secret: options.webhookSecret
|
|
2759
|
+
});
|
|
2760
|
+
if (!verified) {
|
|
2761
|
+
return {
|
|
2762
|
+
verified: false,
|
|
2763
|
+
error: "HMAC-SHA256 signature mismatch. Reject the webhook (HTTP 401).",
|
|
2764
|
+
event,
|
|
2765
|
+
resource: null
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
let resource = null;
|
|
2769
|
+
let resourceError = null;
|
|
2770
|
+
if (auto_fetch) {
|
|
2771
|
+
try {
|
|
2772
|
+
switch (event.topic) {
|
|
2773
|
+
case "payment":
|
|
2774
|
+
case "payment.created":
|
|
2775
|
+
case "payment.updated":
|
|
2776
|
+
resource = await client.getPayment(event.dataId);
|
|
2777
|
+
break;
|
|
2778
|
+
case "preapproval":
|
|
2779
|
+
case "subscription_preapproval":
|
|
2780
|
+
resource = await client.getPreapproval(event.dataId);
|
|
2781
|
+
break;
|
|
2782
|
+
case "subscription_authorized_payment":
|
|
2783
|
+
resource = { id: event.dataId, hint: "Use list_subscription_payments to enumerate parent." };
|
|
2784
|
+
break;
|
|
2785
|
+
default:
|
|
2786
|
+
resource = null;
|
|
2787
|
+
resourceError = `No auto-fetch handler for topic '${event.topic}' yet.`;
|
|
2788
|
+
}
|
|
2789
|
+
} catch (err) {
|
|
2790
|
+
resourceError = err instanceof Error ? err.message : String(err);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
return {
|
|
2794
|
+
verified: true,
|
|
2795
|
+
event,
|
|
2796
|
+
resource,
|
|
2797
|
+
resource_error: resourceError
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
}),
|
|
2801
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2802
|
+
// v0.5 — OAuth Marketplace flow
|
|
2803
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2804
|
+
oauth_authorize_url: tool({
|
|
2805
|
+
description: desc("oauth_authorize_url"),
|
|
2806
|
+
inputSchema: z.object({
|
|
2807
|
+
redirect_uri: z.string().url().describe(
|
|
2808
|
+
"Where MP redirects the seller after approval. MUST be whitelisted in MP dev panel \u2192 Aplicaciones \u2192 tu app \u2192 Redirect URIs."
|
|
2809
|
+
),
|
|
2810
|
+
state: z.string().min(8).describe(
|
|
2811
|
+
"Opaque CSRF/session token echoed back. Bind to the user's session and verify on redirect."
|
|
2812
|
+
)
|
|
2813
|
+
}),
|
|
2814
|
+
execute: async ({ redirect_uri, state }) => {
|
|
2815
|
+
if (!options.oauth?.clientId) {
|
|
2816
|
+
return {
|
|
2817
|
+
available: false,
|
|
2818
|
+
error: "OAuth not configured. Pass `oauth: { clientId, clientSecret }` to mercadoPagoTools options.",
|
|
2819
|
+
url: null
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
const url = buildAuthorizeUrl({
|
|
2823
|
+
clientId: options.oauth.clientId,
|
|
2824
|
+
redirectUri: redirect_uri,
|
|
2825
|
+
state
|
|
2826
|
+
});
|
|
2827
|
+
return {
|
|
2828
|
+
available: true,
|
|
2829
|
+
url,
|
|
2830
|
+
next_step: "Redirect the seller to `url`. After approval MP sends them to redirect_uri?code=...&state=... \u2014 verify state matches, then call oauth_exchange_code with the code."
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
}),
|
|
2834
|
+
oauth_exchange_code: tool({
|
|
2835
|
+
description: desc("oauth_exchange_code"),
|
|
2836
|
+
inputSchema: z.object({
|
|
2837
|
+
code: z.string().describe("The `code` query param from the OAuth redirect URL."),
|
|
2838
|
+
redirect_uri: z.string().url().describe(
|
|
2839
|
+
"Must EXACTLY match the redirect_uri used in oauth_authorize_url."
|
|
2840
|
+
)
|
|
2841
|
+
}),
|
|
2842
|
+
execute: async ({ code, redirect_uri }) => {
|
|
2843
|
+
if (!options.oauth?.clientId || !options.oauth?.clientSecret) {
|
|
2844
|
+
return {
|
|
2845
|
+
available: false,
|
|
2846
|
+
error: "OAuth not configured. Pass `oauth: { clientId, clientSecret }` to mercadoPagoTools options.",
|
|
2847
|
+
token: null
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
try {
|
|
2851
|
+
const token = await exchangeCodeForToken({
|
|
2852
|
+
clientId: options.oauth.clientId,
|
|
2853
|
+
clientSecret: options.oauth.clientSecret,
|
|
2854
|
+
code,
|
|
2855
|
+
redirectUri: redirect_uri
|
|
2856
|
+
});
|
|
2857
|
+
return {
|
|
2858
|
+
available: true,
|
|
2859
|
+
token,
|
|
2860
|
+
next_step: "PERSIST { user_id, access_token, refresh_token, expires_in } against this seller. Use access_token to instantiate `new MercadoPagoClient({ accessToken })` AS the seller for marketplace API calls."
|
|
2861
|
+
};
|
|
2862
|
+
} catch (err) {
|
|
2863
|
+
return {
|
|
2864
|
+
available: true,
|
|
2865
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2866
|
+
token: null
|
|
2867
|
+
};
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
}),
|
|
2871
|
+
oauth_refresh_token: tool({
|
|
2872
|
+
description: desc("oauth_refresh_token"),
|
|
2873
|
+
inputSchema: z.object({
|
|
2874
|
+
refresh_token: z.string().describe("The saved refresh_token for this seller.")
|
|
2875
|
+
}),
|
|
2876
|
+
execute: async ({ refresh_token }) => {
|
|
2877
|
+
if (!options.oauth?.clientId || !options.oauth?.clientSecret) {
|
|
2878
|
+
return {
|
|
2879
|
+
available: false,
|
|
2880
|
+
error: "OAuth not configured. Pass `oauth: { clientId, clientSecret }` to mercadoPagoTools options.",
|
|
2881
|
+
token: null
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
try {
|
|
2885
|
+
const token = await refreshAccessToken({
|
|
2886
|
+
clientId: options.oauth.clientId,
|
|
2887
|
+
clientSecret: options.oauth.clientSecret,
|
|
2888
|
+
refreshToken: refresh_token
|
|
2889
|
+
});
|
|
2890
|
+
return {
|
|
2891
|
+
available: true,
|
|
2892
|
+
token,
|
|
2893
|
+
next_step: "Replace the persisted access_token + refresh_token with these new values (refresh_token may have rotated)."
|
|
2894
|
+
};
|
|
2895
|
+
} catch (err) {
|
|
2896
|
+
return {
|
|
2897
|
+
available: true,
|
|
2898
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2899
|
+
token: null
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
}),
|
|
2904
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2905
|
+
// v0.5 — Order Management API
|
|
2906
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2907
|
+
create_order: tool({
|
|
2908
|
+
description: desc("create_order"),
|
|
2909
|
+
inputSchema: z.object({
|
|
2910
|
+
type: z.enum(["online", "in_store"]).describe("'online' for hosted/checkout flow, 'in_store' for QR/POS"),
|
|
2911
|
+
currency_id: z.string().optional().default("ARS"),
|
|
2912
|
+
external_reference: z.string().optional(),
|
|
2913
|
+
total_amount: z.number().positive().optional(),
|
|
2914
|
+
items: z.array(
|
|
2915
|
+
z.object({
|
|
2916
|
+
title: z.string(),
|
|
2917
|
+
unit_price: z.number(),
|
|
2918
|
+
quantity: z.number(),
|
|
2919
|
+
description: z.string().optional()
|
|
2920
|
+
})
|
|
2921
|
+
).optional(),
|
|
2922
|
+
payer_email: z.string().email().optional(),
|
|
2923
|
+
capture_mode: z.enum(["automatic", "manual"]).optional().describe(
|
|
2924
|
+
"'automatic' charges immediately; 'manual' authorizes only \u2014 capture later via capture_order."
|
|
2925
|
+
),
|
|
2926
|
+
notification_url: z.string().url().optional(),
|
|
2927
|
+
marketplace: z.string().optional().describe(
|
|
2928
|
+
"Marketplace identifier (your app's name). Required for split payments."
|
|
2929
|
+
),
|
|
2930
|
+
marketplace_fee: z.number().optional().describe(
|
|
2931
|
+
"Fee in ARS (NOT %) credited to the marketplace's MP account."
|
|
2932
|
+
),
|
|
2933
|
+
collector_id: z.union([z.string(), z.number()]).optional().describe(
|
|
2934
|
+
"Seller's MP user_id (from oauth_exchange_code.user_id). Funds route here; marketplace_fee is split off to your account."
|
|
2935
|
+
)
|
|
2936
|
+
}),
|
|
2937
|
+
execute: async (input) => {
|
|
2938
|
+
const params = {
|
|
2939
|
+
type: input.type
|
|
2940
|
+
};
|
|
2941
|
+
if (input.currency_id) params.currency_id = input.currency_id;
|
|
2942
|
+
if (input.external_reference) params.external_reference = input.external_reference;
|
|
2943
|
+
if (input.total_amount !== void 0) params.total_amount = input.total_amount;
|
|
2944
|
+
if (input.items) params.items = input.items;
|
|
2945
|
+
if (input.payer_email) params.payer = { email: input.payer_email };
|
|
2946
|
+
if (input.capture_mode) params.capture_mode = input.capture_mode;
|
|
2947
|
+
if (input.notification_url) params.notification_url = input.notification_url;
|
|
2948
|
+
if (input.marketplace) params.marketplace = input.marketplace;
|
|
2949
|
+
if (input.marketplace_fee !== void 0) params.marketplace_fee = input.marketplace_fee;
|
|
2950
|
+
if (input.collector_id !== void 0) params.collector_id = input.collector_id;
|
|
2951
|
+
const order = await client.createOrder(params, {
|
|
2952
|
+
idempotencyKey: deterministicIdempotencyKey(
|
|
2953
|
+
"create_order",
|
|
2954
|
+
input.external_reference,
|
|
2955
|
+
input.total_amount,
|
|
2956
|
+
input.collector_id
|
|
2957
|
+
)
|
|
2958
|
+
});
|
|
2959
|
+
return {
|
|
2960
|
+
order_id: order.id,
|
|
2961
|
+
status: order.status ?? null,
|
|
2962
|
+
capture_mode: order.capture_mode ?? params.capture_mode ?? "automatic",
|
|
2963
|
+
total_amount: order.total_amount ?? null
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
}),
|
|
2967
|
+
get_order: tool({
|
|
2968
|
+
description: desc("get_order"),
|
|
2969
|
+
inputSchema: z.object({ order_id: z.string() }),
|
|
2970
|
+
execute: async ({ order_id }) => {
|
|
2971
|
+
const order = await client.getOrder(order_id);
|
|
2972
|
+
return order;
|
|
2973
|
+
}
|
|
2974
|
+
}),
|
|
2975
|
+
update_order: tool({
|
|
2976
|
+
description: desc("update_order"),
|
|
2977
|
+
inputSchema: z.object({
|
|
2978
|
+
order_id: z.string(),
|
|
2979
|
+
external_reference: z.string().optional(),
|
|
2980
|
+
total_amount: z.number().optional()
|
|
2981
|
+
}),
|
|
2982
|
+
execute: async ({ order_id, external_reference, total_amount }) => {
|
|
2983
|
+
const patch = {};
|
|
2984
|
+
if (external_reference !== void 0) patch.external_reference = external_reference;
|
|
2985
|
+
if (total_amount !== void 0) patch.total_amount = total_amount;
|
|
2986
|
+
const order = await client.updateOrder(order_id, patch);
|
|
2987
|
+
return order;
|
|
2988
|
+
}
|
|
2989
|
+
}),
|
|
2990
|
+
capture_order: tool({
|
|
2991
|
+
description: desc("capture_order"),
|
|
2992
|
+
inputSchema: z.object({
|
|
2993
|
+
order_id: z.string(),
|
|
2994
|
+
amount: z.number().positive().optional().describe(
|
|
2995
|
+
"Optional partial-capture amount. Omit to capture the full authorized amount."
|
|
2996
|
+
)
|
|
2997
|
+
}),
|
|
2998
|
+
execute: async ({ order_id, amount }) => {
|
|
2999
|
+
const order = await client.captureOrder(order_id, amount);
|
|
3000
|
+
return {
|
|
3001
|
+
order_id: order.id,
|
|
3002
|
+
status: order.status ?? null,
|
|
3003
|
+
captured_amount: amount ?? order.total_amount ?? null
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
}),
|
|
3007
|
+
cancel_order: tool({
|
|
3008
|
+
description: desc("cancel_order"),
|
|
3009
|
+
inputSchema: z.object({ order_id: z.string() }),
|
|
3010
|
+
execute: async ({ order_id }) => {
|
|
3011
|
+
const order = await client.cancelOrder(order_id);
|
|
3012
|
+
return {
|
|
3013
|
+
order_id: order.id,
|
|
3014
|
+
status: order.status ?? "canceled"
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
}),
|
|
3018
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3019
|
+
// v0.6 — Account / Balance / Movements / Settlements
|
|
3020
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3021
|
+
get_account_balance: tool({
|
|
3022
|
+
description: desc("get_account_balance"),
|
|
3023
|
+
inputSchema: z.object({}),
|
|
3024
|
+
execute: async () => {
|
|
3025
|
+
const balance = await client.getAccountBalance();
|
|
3026
|
+
return balance;
|
|
3027
|
+
}
|
|
3028
|
+
}),
|
|
3029
|
+
list_account_movements: tool({
|
|
3030
|
+
description: desc("list_account_movements"),
|
|
3031
|
+
inputSchema: z.object({
|
|
3032
|
+
from: z.string().optional().describe("ISO 8601 start date (e.g. 2026-05-01)"),
|
|
3033
|
+
to: z.string().optional().describe("ISO 8601 end date"),
|
|
3034
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
3035
|
+
offset: z.number().int().min(0).optional()
|
|
3036
|
+
}),
|
|
3037
|
+
execute: async ({ from, to, limit, offset }) => {
|
|
3038
|
+
const params = {};
|
|
3039
|
+
if (from !== void 0) params.from = from;
|
|
3040
|
+
if (to !== void 0) params.to = to;
|
|
3041
|
+
if (limit !== void 0) params.limit = limit;
|
|
3042
|
+
if (offset !== void 0) params.offset = offset;
|
|
3043
|
+
return client.listAccountMovements(params);
|
|
3044
|
+
}
|
|
3045
|
+
}),
|
|
3046
|
+
list_settlements: tool({
|
|
3047
|
+
description: desc("list_settlements"),
|
|
3048
|
+
inputSchema: z.object({
|
|
3049
|
+
from: z.string().optional(),
|
|
3050
|
+
to: z.string().optional(),
|
|
3051
|
+
status: z.string().optional(),
|
|
3052
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
3053
|
+
offset: z.number().int().min(0).optional()
|
|
3054
|
+
}),
|
|
3055
|
+
execute: async ({ from, to, status, limit, offset }) => {
|
|
3056
|
+
const params = {};
|
|
3057
|
+
if (from !== void 0) params.from = from;
|
|
3058
|
+
if (to !== void 0) params.to = to;
|
|
3059
|
+
if (status !== void 0) params.status = status;
|
|
3060
|
+
if (limit !== void 0) params.limit = limit;
|
|
3061
|
+
if (offset !== void 0) params.offset = offset;
|
|
3062
|
+
return client.listSettlements(params);
|
|
3063
|
+
}
|
|
3064
|
+
}),
|
|
3065
|
+
get_settlement: tool({
|
|
3066
|
+
description: desc("get_settlement"),
|
|
3067
|
+
inputSchema: z.object({ settlement_id: z.string() }),
|
|
3068
|
+
execute: async ({ settlement_id }) => {
|
|
3069
|
+
return client.getSettlement(settlement_id);
|
|
3070
|
+
}
|
|
3071
|
+
}),
|
|
3072
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3073
|
+
// v0.6 — 3DS analyzer (combined: fetch payment + analyze)
|
|
3074
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3075
|
+
analyze_payment_3ds: tool({
|
|
3076
|
+
description: desc("analyze_payment_3ds"),
|
|
3077
|
+
inputSchema: z.object({
|
|
3078
|
+
payment_id: z.string().describe("MP payment id")
|
|
3079
|
+
}),
|
|
3080
|
+
execute: async ({ payment_id }) => {
|
|
3081
|
+
const payment = await client.getPayment(payment_id);
|
|
3082
|
+
const info = analyze3DS(payment);
|
|
3083
|
+
return {
|
|
3084
|
+
payment_id,
|
|
3085
|
+
payment_status: payment.status,
|
|
3086
|
+
payment_status_detail: payment.status_detail ?? null,
|
|
3087
|
+
...info
|
|
3088
|
+
};
|
|
3089
|
+
}
|
|
3090
|
+
}),
|
|
3091
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3092
|
+
// v0.6 — Test cards (pure)
|
|
3093
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3094
|
+
get_test_cards: tool({
|
|
3095
|
+
description: desc("get_test_cards"),
|
|
3096
|
+
inputSchema: z.object({}),
|
|
3097
|
+
execute: async () => {
|
|
3098
|
+
return {
|
|
3099
|
+
site: "MLA",
|
|
3100
|
+
cards: TEST_CARDS_AR,
|
|
3101
|
+
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."
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
})
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
// src/state.ts
|
|
3109
|
+
var InMemoryStateAdapter = class {
|
|
3110
|
+
store = /* @__PURE__ */ new Map();
|
|
3111
|
+
async set(id, state) {
|
|
3112
|
+
const existing = this.store.get(id) ?? {};
|
|
3113
|
+
this.store.set(id, { ...existing, ...state });
|
|
3114
|
+
}
|
|
3115
|
+
async get(id) {
|
|
3116
|
+
return this.store.get(id) ?? null;
|
|
3117
|
+
}
|
|
3118
|
+
async list() {
|
|
3119
|
+
return Array.from(this.store.keys());
|
|
3120
|
+
}
|
|
3121
|
+
/** Test helper: drop everything. Not part of the adapter interface. */
|
|
3122
|
+
reset() {
|
|
3123
|
+
this.store.clear();
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
2300
3126
|
|
|
2301
|
-
export { InMemoryStateAdapter, MercadoPagoAccountTypeMismatchError, MercadoPagoAuthError, MercadoPagoAuthorizeForbiddenError, MercadoPagoBackUrlInvalidError, MercadoPagoClient, MercadoPagoError, MercadoPagoOverloadedError, MercadoPagoPaymentRejectedError, MercadoPagoRateLimitError, MercadoPagoSelfPaymentError, MercadoPagoTimeoutError, classifyError, mercadoPagoTools, parseWebhookEvent, verifyWebhookSignature };
|
|
3127
|
+
export { InMemoryStateAdapter, MercadoPagoAccountTypeMismatchError, MercadoPagoAuthError, MercadoPagoAuthorizeForbiddenError, MercadoPagoBackUrlInvalidError, MercadoPagoClient, MercadoPagoError, MercadoPagoOverloadedError, MercadoPagoPaymentRejectedError, MercadoPagoRateLimitError, MercadoPagoSelfPaymentError, MercadoPagoTimeoutError, TEST_CARDS_AR, TEST_PAYERS_AR, analyze3DS, buildAuthorizeUrl, buildTestCardScenario, classifyError, exchangeCodeForToken, expirationTimeMs, isExpiringSoon, mercadoPagoTools, parseWebhookEvent, refreshAccessToken, verifyWebhookSignature };
|
|
2302
3128
|
//# sourceMappingURL=index.js.map
|
|
2303
3129
|
//# sourceMappingURL=index.js.map
|