@ar-agents/mercadopago 0.7.0 → 0.8.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/CHANGELOG.md +47 -0
- package/README.md +63 -1
- package/cookbook/01-checkout-pro-basic.ts +99 -0
- package/cookbook/02-saas-subscription.ts +137 -0
- package/cookbook/03-webhook-handler.ts +162 -0
- package/cookbook/04-marketplace-split.ts +194 -0
- package/cookbook/05-qr-in-store.ts +142 -0
- package/cookbook/06-3ds-challenge.ts +139 -0
- package/cookbook/07-auth-only-order.ts +127 -0
- package/cookbook/08-recovery-patterns.ts +191 -0
- package/cookbook/README.md +36 -0
- package/dist/index.cjs +109 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -49
- package/dist/index.d.ts +28 -49
- package/dist/index.js +108 -13
- package/dist/index.js.map +1 -1
- package/dist/state-C6Wzb_XX.d.cts +106 -0
- package/dist/state-C6Wzb_XX.d.ts +106 -0
- package/dist/vercel-kv.cjs +92 -0
- package/dist/vercel-kv.cjs.map +1 -0
- package/dist/vercel-kv.d.cts +107 -0
- package/dist/vercel-kv.d.ts +107 -0
- package/dist/vercel-kv.js +88 -0
- package/dist/vercel-kv.js.map +1 -0
- package/package.json +25 -2
- package/tools.manifest.json +1 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recipe 08 — Recovery patterns: retry, recover stuck payments, handle expirations.
|
|
3
|
+
*
|
|
4
|
+
* # Common stuck states and how to recover
|
|
5
|
+
*
|
|
6
|
+
* 1. **Subscription card expired → recurring charge rejected**
|
|
7
|
+
* Recover by: capture fresh card token from buyer + `update_subscription({ card_token_id })`
|
|
8
|
+
*
|
|
9
|
+
* 2. **Payment stuck in `pending_challenge` (3DS not completed)**
|
|
10
|
+
* Recover by: redirect buyer back to the challenge URL via
|
|
11
|
+
* `analyze_payment_3ds(payment_id).challengeUrl`
|
|
12
|
+
*
|
|
13
|
+
* 3. **Payment in `pending_review_manual` (MP fraud team review)**
|
|
14
|
+
* Recover by: WAIT — MP processes within 24-72h. Don't retry.
|
|
15
|
+
*
|
|
16
|
+
* 4. **Subscription auto-cancelled because first payment failed**
|
|
17
|
+
* Recover by: create a fresh subscription (the original is dead, MP doesn't
|
|
18
|
+
* let you "reactivate" — that's documented in `MercadoPagoPaymentRejectedError`).
|
|
19
|
+
*
|
|
20
|
+
* 5. **`pending_waiting_payment` for cash methods (Rapipago, Pago Fácil)**
|
|
21
|
+
* Recover by: NOTHING — the buyer must complete payment within the
|
|
22
|
+
* timeout (typically 3-5 days). Polling or push-webhooks notify when done.
|
|
23
|
+
*
|
|
24
|
+
* 6. **Webhook arrived but payment not in your DB**
|
|
25
|
+
* Recover by: idempotent upsert via `searchPayments({ external_reference })`
|
|
26
|
+
* instead of trusting the webhook payload alone.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import {
|
|
30
|
+
classifyError,
|
|
31
|
+
explainPaymentStatus,
|
|
32
|
+
MercadoPagoClient,
|
|
33
|
+
MercadoPagoPaymentRejectedError,
|
|
34
|
+
type PaymentStatusExplanation,
|
|
35
|
+
} from "@ar-agents/mercadopago";
|
|
36
|
+
|
|
37
|
+
const mp = new MercadoPagoClient({
|
|
38
|
+
accessToken: process.env.MP_ACCESS_TOKEN!,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
// Pattern 1 — Subscription card swap on rejection
|
|
43
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
export async function recoverFromCardRejection(input: {
|
|
46
|
+
subscriptionId: string;
|
|
47
|
+
buyerWhatsAppNumber: string;
|
|
48
|
+
}) {
|
|
49
|
+
const sub = await mp.getPreapproval(input.subscriptionId);
|
|
50
|
+
if (sub.status !== "paused" && sub.status !== "cancelled") {
|
|
51
|
+
return { ok: true, action: "none" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Send buyer a link to update their card via MP frontend SDK
|
|
55
|
+
const updateUrl = `https://yourapp.com/billing/update-card?sub=${input.subscriptionId}`;
|
|
56
|
+
// ... send via WhatsApp with the toolkit's whatsappTools ...
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
action: "card_swap_required",
|
|
61
|
+
sentTo: input.buyerWhatsAppNumber,
|
|
62
|
+
updateUrl,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
// Pattern 2 — Recover stuck-pending payment with status explanation
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export async function inspectStuckPayment(paymentId: string): Promise<{
|
|
71
|
+
paymentId: string;
|
|
72
|
+
status: string;
|
|
73
|
+
explanation: PaymentStatusExplanation;
|
|
74
|
+
nextAction: string;
|
|
75
|
+
}> {
|
|
76
|
+
const payment = await mp.getPayment(paymentId);
|
|
77
|
+
const explanation = explainPaymentStatus(payment);
|
|
78
|
+
|
|
79
|
+
let nextAction = explanation.recommendedAction;
|
|
80
|
+
if (explanation.retryable) {
|
|
81
|
+
nextAction = `Reintentar con otra tarjeta. Razón: ${explanation.summary}`;
|
|
82
|
+
} else if (!explanation.final) {
|
|
83
|
+
nextAction = `Esperar webhook (${explanation.summary}). Sin acción de tu parte.`;
|
|
84
|
+
} else if (explanation.paid) {
|
|
85
|
+
nextAction = `Acreditado. Continuar con flujo posterior.`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
paymentId,
|
|
90
|
+
status: payment.status as string,
|
|
91
|
+
explanation,
|
|
92
|
+
nextAction,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
97
|
+
// Pattern 3 — Idempotent upsert via search (don't trust webhook payload alone)
|
|
98
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
export async function reconcilePaymentByExternalRef(externalReference: string) {
|
|
101
|
+
// Search MP for ALL payments under this external_reference. There may be
|
|
102
|
+
// multiple if the buyer retried.
|
|
103
|
+
const result = await mp.searchPayments({ external_reference: externalReference });
|
|
104
|
+
|
|
105
|
+
// Find the latest approved one (winning attempt)
|
|
106
|
+
const approved = result.results
|
|
107
|
+
?.filter((p) => p.status === "approved")
|
|
108
|
+
.sort((a, b) => (b.date_created ?? "").localeCompare(a.date_created ?? ""))[0];
|
|
109
|
+
|
|
110
|
+
if (approved) {
|
|
111
|
+
return { found: true, paymentId: approved.id, amount: approved.transaction_amount };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// No approved payment — find the latest attempt (could be pending or rejected)
|
|
115
|
+
const latest = result.results
|
|
116
|
+
?.sort((a, b) => (b.date_created ?? "").localeCompare(a.date_created ?? ""))[0];
|
|
117
|
+
|
|
118
|
+
return { found: false, lastAttempt: latest };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
122
|
+
// Pattern 4 — Handle MercadoPagoPaymentRejectedError explicitly
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
export async function chargeWithRetry(input: {
|
|
126
|
+
cardId: string;
|
|
127
|
+
customerId: string;
|
|
128
|
+
amountArs: number;
|
|
129
|
+
cvv: string;
|
|
130
|
+
externalReference: string;
|
|
131
|
+
}): Promise<
|
|
132
|
+
{ ok: true; paymentId: string } | { ok: false; reason: string; recoverable: boolean }
|
|
133
|
+
> {
|
|
134
|
+
try {
|
|
135
|
+
const payment = await mp.chargeSavedCard({
|
|
136
|
+
cardId: input.cardId,
|
|
137
|
+
customerId: input.customerId,
|
|
138
|
+
transactionAmount: input.amountArs,
|
|
139
|
+
securityCode: input.cvv,
|
|
140
|
+
payerEmail: "—", // populated server-side from customer
|
|
141
|
+
description: "Recurring charge",
|
|
142
|
+
externalReference: input.externalReference,
|
|
143
|
+
});
|
|
144
|
+
return { ok: true, paymentId: payment.id };
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (err instanceof MercadoPagoPaymentRejectedError) {
|
|
147
|
+
// The lib's MercadoPagoPaymentRejectedError carries status_detail —
|
|
148
|
+
// use it to drive recovery.
|
|
149
|
+
const detail = (err as MercadoPagoPaymentRejectedError & { statusDetail?: string }).statusDetail;
|
|
150
|
+
const recoverable =
|
|
151
|
+
detail === "cc_rejected_call_for_authorize" ||
|
|
152
|
+
detail === "cc_rejected_insufficient_amount" ||
|
|
153
|
+
detail === "cc_rejected_bad_filled_security_code";
|
|
154
|
+
return { ok: false, reason: detail ?? "rejected", recoverable };
|
|
155
|
+
}
|
|
156
|
+
const classified = classifyError(err);
|
|
157
|
+
throw classified; // Re-throw for ops/observability
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
+
// Pattern 5 — Cron-driven monitoring (Vercel Cron Job)
|
|
163
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Hypothetical Vercel Cron Job (`vercel.json`):
|
|
167
|
+
* ```json
|
|
168
|
+
* { "crons": [{ "path": "/api/cron/mp-monitor", "schedule": "0 *\/4 * * *" }] }
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* Runs every 4 hours; surfaces:
|
|
172
|
+
* - Subscriptions that haven't auto-charged in >35 days (probably broken)
|
|
173
|
+
* - Stuck-pending payments older than 24h (need investigation)
|
|
174
|
+
* - Disputes opened in the last 24h (need response)
|
|
175
|
+
*/
|
|
176
|
+
export async function cronMonitorMpHealth() {
|
|
177
|
+
const since = new Date(Date.now() - 24 * 3600 * 1000).toISOString();
|
|
178
|
+
const stuck = await mp.searchPayments({
|
|
179
|
+
status: "pending",
|
|
180
|
+
range: "date_created",
|
|
181
|
+
begin_date: since.slice(0, 10),
|
|
182
|
+
} as never);
|
|
183
|
+
|
|
184
|
+
// Surface to ops via Slack/email/Sentry
|
|
185
|
+
const stuckCount = stuck.results?.length ?? 0;
|
|
186
|
+
if (stuckCount > 5) {
|
|
187
|
+
// alertOps(...)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return { stuckCount };
|
|
191
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Cookbook — `@ar-agents/mercadopago`
|
|
2
|
+
|
|
3
|
+
Real, copy-pasteable recipes for the most common MP integration flows. Every
|
|
4
|
+
recipe is a self-contained Next.js route handler or agent loop you can
|
|
5
|
+
deploy on Vercel as-is.
|
|
6
|
+
|
|
7
|
+
## Recipes
|
|
8
|
+
|
|
9
|
+
| # | File | Pattern |
|
|
10
|
+
| --- | --------------------------------- | ----------------------------------------------------------------------- |
|
|
11
|
+
| 01 | `01-checkout-pro-basic.ts` | First-time hosted-checkout sale (Checkout Pro preference + back URLs) |
|
|
12
|
+
| 02 | `02-saas-subscription.ts` | Reusable plan + subscription with first-payment + card swap on failure |
|
|
13
|
+
| 03 | `03-webhook-handler.ts` | Vercel route handler with HMAC verify + auto-fetch + dispatch by topic |
|
|
14
|
+
| 04 | `04-marketplace-split.ts` | OAuth seller link → preference with `marketplace_fee` → reconciliation |
|
|
15
|
+
| 05 | `05-qr-in-store.ts` | Create POS → generate QR → poll status → notify buyer via WhatsApp |
|
|
16
|
+
| 06 | `06-3ds-challenge.ts` | Detect challenge → redirect buyer → recover via webhook |
|
|
17
|
+
| 07 | `07-auth-only-order.ts` | `Order` with manual capture → capture later when service completes |
|
|
18
|
+
| 08 | `08-recovery-patterns.ts` | Retry expired subscriptions, recover stuck-pending payments, etc. |
|
|
19
|
+
|
|
20
|
+
## Conventions
|
|
21
|
+
|
|
22
|
+
- All recipes assume `MP_ACCESS_TOKEN` is set (TEST- prefix in sandbox).
|
|
23
|
+
- All recipes show the agent path AND the manual-client path side by side
|
|
24
|
+
where relevant.
|
|
25
|
+
- Recipes that need state use `VercelKVSubscriptionStateAdapter` —
|
|
26
|
+
swap for `InMemoryStateAdapter` in tests.
|
|
27
|
+
- Recipes that need OAuth credentials assume `MP_CLIENT_ID` + `MP_CLIENT_SECRET`.
|
|
28
|
+
- Recipes that need webhook secrets assume `MP_WEBHOOK_SECRET`.
|
|
29
|
+
- All `Edge Runtime` compatible (Web Crypto only — no `node:crypto`).
|
|
30
|
+
|
|
31
|
+
## Deploying as Vercel functions
|
|
32
|
+
|
|
33
|
+
Each recipe is a standalone TypeScript file you can drop into
|
|
34
|
+
`apps/your-app/src/app/api/mp/{route}.ts` (App Router) or
|
|
35
|
+
`apps/your-app/pages/api/mp/{route}.ts` (Pages Router). Add `export const runtime = "edge"` if
|
|
36
|
+
you want Edge Runtime; the toolkit is fully Edge-compatible.
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var crypto = require('crypto');
|
|
4
3
|
var ai = require('ai');
|
|
5
4
|
var zod = require('zod');
|
|
6
5
|
|
|
@@ -1235,6 +1234,54 @@ var MercadoPagoClient = class {
|
|
|
1235
1234
|
return { id: intentId, canceled: true };
|
|
1236
1235
|
}
|
|
1237
1236
|
};
|
|
1237
|
+
|
|
1238
|
+
// src/crypto.ts
|
|
1239
|
+
var subtle = (() => {
|
|
1240
|
+
const c = globalThis.crypto;
|
|
1241
|
+
if (!c?.subtle) {
|
|
1242
|
+
throw new Error(
|
|
1243
|
+
"@ar-agents/mercadopago: Web Crypto API is not available in this runtime. Use Node 18+, Vercel Edge Runtime, Cloudflare Workers, or any modern browser."
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
return c.subtle;
|
|
1247
|
+
})();
|
|
1248
|
+
var encoder = new TextEncoder();
|
|
1249
|
+
async function hmacSha256Hex(secret, message) {
|
|
1250
|
+
const keyMaterial = await subtle.importKey(
|
|
1251
|
+
"raw",
|
|
1252
|
+
encoder.encode(secret),
|
|
1253
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1254
|
+
false,
|
|
1255
|
+
["sign"]
|
|
1256
|
+
);
|
|
1257
|
+
const sigBuf = await subtle.sign(
|
|
1258
|
+
"HMAC",
|
|
1259
|
+
keyMaterial,
|
|
1260
|
+
encoder.encode(message)
|
|
1261
|
+
);
|
|
1262
|
+
return bufferToHex(sigBuf);
|
|
1263
|
+
}
|
|
1264
|
+
async function sha256Hex(input) {
|
|
1265
|
+
const digest = await subtle.digest("SHA-256", encoder.encode(input));
|
|
1266
|
+
return bufferToHex(digest);
|
|
1267
|
+
}
|
|
1268
|
+
function timingSafeEqualHex(a, b) {
|
|
1269
|
+
if (a.length !== b.length) return false;
|
|
1270
|
+
let diff = 0;
|
|
1271
|
+
for (let i = 0; i < a.length; i++) {
|
|
1272
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1273
|
+
}
|
|
1274
|
+
return diff === 0;
|
|
1275
|
+
}
|
|
1276
|
+
function bufferToHex(buf) {
|
|
1277
|
+
const bytes = new Uint8Array(buf);
|
|
1278
|
+
let hex = "";
|
|
1279
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1280
|
+
const b = bytes[i];
|
|
1281
|
+
hex += (b < 16 ? "0" : "") + b.toString(16);
|
|
1282
|
+
}
|
|
1283
|
+
return hex;
|
|
1284
|
+
}
|
|
1238
1285
|
zod.z.enum(["MLA", "MLB", "MLM", "MCO", "MLC", "MLU"]);
|
|
1239
1286
|
var CurrencyIdSchema = zod.z.enum(["ARS", "USD", "BRL", "MXN"]);
|
|
1240
1287
|
var FrequencyTypeSchema = zod.z.enum(["months", "days"]);
|
|
@@ -2102,6 +2149,8 @@ function analyze3DS(payment) {
|
|
|
2102
2149
|
description: "No se pudo determinar el estado 3DS \u2014 revisar payment.three_d_secure_mode + payment.status_detail manualmente."
|
|
2103
2150
|
};
|
|
2104
2151
|
}
|
|
2152
|
+
|
|
2153
|
+
// src/webhook.ts
|
|
2105
2154
|
function parseWebhookEvent(body, searchParams) {
|
|
2106
2155
|
const parseResult = WebhookBodySchema.safeParse(body ?? {});
|
|
2107
2156
|
const parsedBody = parseResult.success ? parseResult.data : {};
|
|
@@ -2117,7 +2166,8 @@ function parseWebhookEvent(body, searchParams) {
|
|
|
2117
2166
|
raw: parsedBody
|
|
2118
2167
|
};
|
|
2119
2168
|
}
|
|
2120
|
-
|
|
2169
|
+
var DEFAULT_REPLAY_TOLERANCE_SECONDS = 300;
|
|
2170
|
+
async function verifyWebhookSignature(params) {
|
|
2121
2171
|
if (!params.signatureHeader || !params.requestId) return false;
|
|
2122
2172
|
const parts = Object.fromEntries(
|
|
2123
2173
|
params.signatureHeader.split(",").map((segment) => segment.trim().split("="))
|
|
@@ -2125,16 +2175,20 @@ function verifyWebhookSignature(params) {
|
|
|
2125
2175
|
const ts = parts.ts;
|
|
2126
2176
|
const v1 = parts.v1;
|
|
2127
2177
|
if (!ts || !v1) return false;
|
|
2178
|
+
const tolerance = params.replayToleranceSeconds ?? DEFAULT_REPLAY_TOLERANCE_SECONDS;
|
|
2179
|
+
const tsNumber = Number(ts);
|
|
2180
|
+
if (!Number.isFinite(tsNumber)) return false;
|
|
2181
|
+
const ageSeconds = Math.abs(Math.floor(Date.now() / 1e3) - tsNumber);
|
|
2182
|
+
if (ageSeconds > tolerance) return false;
|
|
2128
2183
|
const manifest = `id:${params.dataId};request-id:${params.requestId};ts:${ts};`;
|
|
2129
|
-
const expected =
|
|
2130
|
-
|
|
2131
|
-
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
|
|
2184
|
+
const expected = await hmacSha256Hex(params.secret, manifest);
|
|
2185
|
+
return timingSafeEqualHex(expected, v1);
|
|
2132
2186
|
}
|
|
2133
2187
|
|
|
2134
2188
|
// src/tools.ts
|
|
2135
|
-
function deterministicIdempotencyKey(...parts) {
|
|
2189
|
+
async function deterministicIdempotencyKey(...parts) {
|
|
2136
2190
|
const payload = parts.filter((p) => p !== void 0 && p !== null).map(String).join("|");
|
|
2137
|
-
return
|
|
2191
|
+
return (await sha256Hex(payload)).slice(0, 32);
|
|
2138
2192
|
}
|
|
2139
2193
|
var DEFAULT_DESCRIPTIONS = {
|
|
2140
2194
|
// ── Subscriptions ────────────────────────────────────────────────────────
|
|
@@ -2390,7 +2444,7 @@ function mercadoPagoTools(client, options) {
|
|
|
2390
2444
|
...options.notificationUrl !== void 0 ? { notificationUrl: options.notificationUrl } : {},
|
|
2391
2445
|
// Deterministic idempotency key — safe to retry, same inputs always
|
|
2392
2446
|
// produce the same key (MP dedupes on its side).
|
|
2393
|
-
idempotencyKey: deterministicIdempotencyKey(
|
|
2447
|
+
idempotencyKey: await deterministicIdempotencyKey(
|
|
2394
2448
|
"create_payment",
|
|
2395
2449
|
input.external_reference ?? input.payer_email,
|
|
2396
2450
|
input.amount_ars,
|
|
@@ -2513,7 +2567,7 @@ function mercadoPagoTools(client, options) {
|
|
|
2513
2567
|
const refund = await client.createRefund({
|
|
2514
2568
|
paymentId: payment_id,
|
|
2515
2569
|
...amount_ars !== void 0 ? { amount: amount_ars } : {},
|
|
2516
|
-
idempotencyKey: deterministicIdempotencyKey("refund", payment_id, amount_ars ?? "full")
|
|
2570
|
+
idempotencyKey: await deterministicIdempotencyKey("refund", payment_id, amount_ars ?? "full")
|
|
2517
2571
|
});
|
|
2518
2572
|
return {
|
|
2519
2573
|
refund_id: refund.id,
|
|
@@ -2779,7 +2833,7 @@ function mercadoPagoTools(client, options) {
|
|
|
2779
2833
|
...input.installments !== void 0 ? { installments: input.installments } : {},
|
|
2780
2834
|
...input.external_reference !== void 0 ? { externalReference: input.external_reference } : {},
|
|
2781
2835
|
...input.statement_descriptor !== void 0 ? { statementDescriptor: input.statement_descriptor } : {},
|
|
2782
|
-
idempotencyKey: deterministicIdempotencyKey(
|
|
2836
|
+
idempotencyKey: await deterministicIdempotencyKey(
|
|
2783
2837
|
"charge_saved_card",
|
|
2784
2838
|
input.card_id,
|
|
2785
2839
|
input.amount_ars,
|
|
@@ -3290,7 +3344,7 @@ function mercadoPagoTools(client, options) {
|
|
|
3290
3344
|
resource: null
|
|
3291
3345
|
};
|
|
3292
3346
|
}
|
|
3293
|
-
const verified = verifyWebhookSignature({
|
|
3347
|
+
const verified = await verifyWebhookSignature({
|
|
3294
3348
|
requestId: request_id_header,
|
|
3295
3349
|
dataId: event.dataId,
|
|
3296
3350
|
signatureHeader: signature_header,
|
|
@@ -3488,7 +3542,7 @@ function mercadoPagoTools(client, options) {
|
|
|
3488
3542
|
if (input.marketplace_fee !== void 0) params.marketplace_fee = input.marketplace_fee;
|
|
3489
3543
|
if (input.collector_id !== void 0) params.collector_id = input.collector_id;
|
|
3490
3544
|
const order = await client.createOrder(params, {
|
|
3491
|
-
idempotencyKey: deterministicIdempotencyKey(
|
|
3545
|
+
idempotencyKey: await deterministicIdempotencyKey(
|
|
3492
3546
|
"create_order",
|
|
3493
3547
|
input.external_reference,
|
|
3494
3548
|
input.total_amount,
|
|
@@ -4052,7 +4106,50 @@ var InMemoryStateAdapter = class {
|
|
|
4052
4106
|
this.store.clear();
|
|
4053
4107
|
}
|
|
4054
4108
|
};
|
|
4109
|
+
var InMemoryOAuthTokenStore = class {
|
|
4110
|
+
store = /* @__PURE__ */ new Map();
|
|
4111
|
+
async set(userId, token) {
|
|
4112
|
+
this.store.set(userId, token);
|
|
4113
|
+
}
|
|
4114
|
+
async get(userId) {
|
|
4115
|
+
return this.store.get(userId) ?? null;
|
|
4116
|
+
}
|
|
4117
|
+
async delete(userId) {
|
|
4118
|
+
this.store.delete(userId);
|
|
4119
|
+
}
|
|
4120
|
+
async list() {
|
|
4121
|
+
return Array.from(this.store.keys());
|
|
4122
|
+
}
|
|
4123
|
+
/** Test helper. */
|
|
4124
|
+
reset() {
|
|
4125
|
+
this.store.clear();
|
|
4126
|
+
}
|
|
4127
|
+
};
|
|
4128
|
+
var InMemoryIdempotencyCache = class {
|
|
4129
|
+
store = /* @__PURE__ */ new Map();
|
|
4130
|
+
async get(key) {
|
|
4131
|
+
const entry = this.store.get(key);
|
|
4132
|
+
if (!entry) return null;
|
|
4133
|
+
if (Date.now() > entry.expiresAt) {
|
|
4134
|
+
this.store.delete(key);
|
|
4135
|
+
return null;
|
|
4136
|
+
}
|
|
4137
|
+
return entry.value;
|
|
4138
|
+
}
|
|
4139
|
+
async set(key, value, ttlSeconds = 86400) {
|
|
4140
|
+
this.store.set(key, { value, expiresAt: Date.now() + ttlSeconds * 1e3 });
|
|
4141
|
+
}
|
|
4142
|
+
async delete(key) {
|
|
4143
|
+
this.store.delete(key);
|
|
4144
|
+
}
|
|
4145
|
+
/** Test helper. */
|
|
4146
|
+
reset() {
|
|
4147
|
+
this.store.clear();
|
|
4148
|
+
}
|
|
4149
|
+
};
|
|
4055
4150
|
|
|
4151
|
+
exports.InMemoryIdempotencyCache = InMemoryIdempotencyCache;
|
|
4152
|
+
exports.InMemoryOAuthTokenStore = InMemoryOAuthTokenStore;
|
|
4056
4153
|
exports.InMemoryStateAdapter = InMemoryStateAdapter;
|
|
4057
4154
|
exports.MercadoPagoAccountTypeMismatchError = MercadoPagoAccountTypeMismatchError;
|
|
4058
4155
|
exports.MercadoPagoAuthError = MercadoPagoAuthError;
|