This commit is contained in:
Lucas Tettamanti
2026-01-18 20:07:40 -03:00
parent 23c3d44490
commit 9754347a36
11 changed files with 1516 additions and 6 deletions

View File

@@ -0,0 +1,92 @@
import {
handleListRecentOrders,
handleGetProductsWithStock,
handleCreateTestOrder,
handleCreatePaymentLink,
handleSimulateMpWebhook,
} from "../handlers/testing.js";
export const makeListRecentOrders = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const limit = parseInt(req.query.limit) || 20;
const result = await handleListRecentOrders({ tenantId, limit });
res.json(result);
} catch (err) {
console.error("[testing] listRecentOrders error:", err);
res.status(500).json({ ok: false, error: err.message || "internal_error" });
}
};
export const makeGetProductsWithStock = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const result = await handleGetProductsWithStock({ tenantId });
res.json(result);
} catch (err) {
console.error("[testing] getProductsWithStock error:", err);
res.status(500).json({ ok: false, error: err.message || "internal_error" });
}
};
export const makeCreateTestOrder = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const { basket, address, wa_chat_id } = req.body || {};
if (!basket?.items?.length) {
return res.status(400).json({ ok: false, error: "basket_required" });
}
const result = await handleCreateTestOrder({ tenantId, basket, address, wa_chat_id });
res.json(result);
} catch (err) {
console.error("[testing] createTestOrder error:", err);
res.status(500).json({ ok: false, error: err.message || "internal_error" });
}
};
export const makeCreatePaymentLink = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const { woo_order_id, amount } = req.body || {};
if (!woo_order_id) {
return res.status(400).json({ ok: false, error: "woo_order_id_required" });
}
if (!amount || Number(amount) <= 0) {
return res.status(400).json({ ok: false, error: "amount_required" });
}
const result = await handleCreatePaymentLink({
tenantId,
wooOrderId: woo_order_id,
amount: Number(amount)
});
res.json(result);
} catch (err) {
console.error("[testing] createPaymentLink error:", err);
res.status(500).json({ ok: false, error: err.message || "internal_error" });
}
};
export const makeSimulateMpWebhook = (tenantIdOrFn) => async (req, res) => {
try {
const tenantId = typeof tenantIdOrFn === "function" ? tenantIdOrFn() : tenantIdOrFn;
const { woo_order_id, amount } = req.body || {};
if (!woo_order_id) {
return res.status(400).json({ ok: false, error: "woo_order_id_required" });
}
const result = await handleSimulateMpWebhook({
tenantId,
wooOrderId: woo_order_id,
amount: Number(amount) || 0
});
res.json(result);
} catch (err) {
console.error("[testing] simulateMpWebhook error:", err);
res.status(500).json({ ok: false, error: err.message || "internal_error" });
}
};

View File

@@ -0,0 +1,131 @@
import { createOrder, listRecentOrders } from "../../4-woo-orders/wooOrders.js";
import { createPreference, reconcilePayment } from "../../6-mercadopago/mercadoPago.js";
import { listProducts } from "../db/repo.js";
/**
* Lista pedidos recientes de WooCommerce
*/
export async function handleListRecentOrders({ tenantId, limit = 20 }) {
const orders = await listRecentOrders({ tenantId, limit });
return { items: orders };
}
/**
* Obtiene productos con stock para testing
*/
export async function handleGetProductsWithStock({ tenantId }) {
const allProducts = await listProducts({ tenantId, limit: 500 });
const withStock = allProducts.filter(p =>
p.stock_status === "instock" &&
p.price &&
Number(p.price) > 0
);
return { items: withStock };
}
/**
* Crea una orden de prueba en WooCommerce
*/
export async function handleCreateTestOrder({ tenantId, basket, address, wa_chat_id }) {
if (!basket?.items?.length) {
throw new Error("basket_empty");
}
const order = await createOrder({
tenantId,
wooCustomerId: null, // Sin customer de Woo para testing
basket,
address,
run_id: `test-${Date.now()}`,
});
// Calcular total desde line_items
let total = 0;
if (order?.raw?.line_items) {
for (const item of order.raw.line_items) {
total += Number(item.total) || 0;
}
} else if (order?.raw?.total) {
total = Number(order.raw.total) || 0;
}
return {
ok: true,
woo_order_id: order?.id || null,
total,
line_items: order?.line_items || [],
raw: order?.raw || null,
};
}
/**
* Crea un link de pago de MercadoPago
*/
export async function handleCreatePaymentLink({ tenantId, wooOrderId, amount }) {
if (!wooOrderId) {
throw new Error("missing_woo_order_id");
}
if (!amount || Number(amount) <= 0) {
throw new Error("invalid_amount");
}
const pref = await createPreference({
tenantId,
wooOrderId,
amount: Number(amount),
});
return {
ok: true,
preference_id: pref?.preference_id || null,
init_point: pref?.init_point || null,
sandbox_init_point: pref?.sandbox_init_point || null,
};
}
/**
* Simula un webhook de MercadoPago con pago exitoso
* No pasa por el endpoint real (requiere firma HMAC)
* Crea un payment mock y llama a reconcilePayment directamente
*/
export async function handleSimulateMpWebhook({ tenantId, wooOrderId, amount }) {
if (!wooOrderId) {
throw new Error("missing_woo_order_id");
}
// Crear payment mock con status approved
const mockPaymentId = `test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const mockPayment = {
id: mockPaymentId,
status: "approved",
status_detail: "accredited",
external_reference: `${tenantId}|${wooOrderId}`,
transaction_amount: Number(amount) || 0,
currency_id: "ARS",
date_approved: new Date().toISOString(),
date_created: new Date().toISOString(),
payment_method_id: "test",
payment_type_id: "credit_card",
payer: {
email: "test@test.com",
},
order: {
id: `pref-test-${wooOrderId}`,
},
};
// Reconciliar el pago (actualiza mp_payments y cambia status de orden a processing)
const result = await reconcilePayment({
tenantId,
payment: mockPayment,
});
return {
ok: true,
payment_id: mockPaymentId,
woo_order_id: result?.woo_order_id || wooOrderId,
status: "approved",
order_status: "processing",
reconciled: result?.payment || null,
};
}