Cómo integrar la API de Mercado Libre con Contabilium, Tango o tu ERP en PHP 8.4 y Cloud VPS: Webhooks HTTPS, OAuth2 y sincronización de stock
Cuando una empresa vende al mismo tiempo en Mercado Libre, su tienda online (WooCommerce / Tiendanube), mostrador y WhatsApp, sincronizar el stock y la facturación contra su ERP (Contabilium, Tango Gestión, Colppy o un sistema propio) se vuelve crítico. En esta guía técnica te mostramos cómo diseñar una integración en PHP 8.4 y MySQL que responda los Webhooks HTTPS de Mercado Libre en menos de 50 ms, proteja y rote los tokens OAuth2 sin condiciones de carrera, y cuándo te conviene alojarla en Web Hosting NVMe, en un Servidor Cloud VPS o pedirla llave en mano con MIA.
¿Dónde conviene alojar tu conector entre Mercado Libre y Contabilium / Tango / WooCommerce?
En MESI tenés baja latencia nacional contra las APIs de Mercado Libre, Contabilium y ARCA (ex AFIP), facturación local en Pesos (Factura A o B) y tres modalidades según el volumen de tu operación:
1. Servidor Cloud VPS (SSH Root)
Ideal para correr workers persistentes con systemd o Supervisor, crontab cada 1 minuto (* * * * *), colas en Redis/MySQL y stacks en PHP 8.4, Node.js o Python sobre Ubuntu 24.04 LTS.
2. Web Hosting NVMe (PHP 8.4)
Perfecto para conectores en PHP 8.4 + MySQL con Webhooks HTTPS, subcarpeta protegida con .htaccess (o MySQL) para guardar tus tokens OAuth2 y tareas programadas (cron) cada 5 a 15 minutos.
3. MIA (Desarrollo IA + VPS)
¿No querés programarlo? Nuestros especialistas junto con MIA desarrollan tu integrador a medida (Mercado Libre + ERP + ARCA + WhatsApp) y lo dejan corriendo en tu propia Cloud VPS.
1 Los 4 cuellos de botella al conectar la API de Mercado Libre con Contabilium, Tango o WooCommerce
Muchos desarrollos propios que conectan Mercado Libre con un ERP como Contabilium o Tango funcionan bien en pruebas con 2 pedidos, pero fallan en plena jornada comercial (o durante un Hot Sale o CyberMonday) por cuatro motivos técnicos concretos:
- El límite estricto de 500 ms en los Webhooks de Mercado Libre: Cuando entra una venta o cambia un envío, Mercado Libre hace un
POSTHTTPS a tu URL de Callback (tópicosorders_v2,items,shipments,questions). Si tu endpoint tarda más de 500 milisegundos en devolverHTTP 200 OKporque se queda consultando la orden y llamando a la API del ERP en el mismo request, Mercado Libre da la notificación por caída, reintenta en ráfaga y termina deshabilitando el webhook de tu aplicación. - Corrupción del
refresh_tokenpor condición de carrera (Race Condition): Elaccess_tokende Mercado Libre dura 6 horas (21600segundos) y cuando pedís uno nuevo congrant_type=refresh_token, elrefresh_tokenanterior queda invalidado. Si un webhook y un cron intentan refrescar el token en el mismo segundo sin un bloqueo exclusivo (flock), uno pisa al otro y la integración pierde autorización hasta volver a loguearse manualmente. - Tokens expuestos sin protección dentro de
/wwwo de un subdominio: En las cuentas de hosting por FTP el nivel superior a/wwwno tiene permisos de escritura directa (ya quewwwes un subdominio y cualquier otra carpeta en ese nivel debe crearse como subdominio desde el panel, siendo también accesible vía web). Si guardásmeli_tokens.jsonsuelto sin bloquear su carpeta con.htaccess, cualquiera podría descargarlo por navegador. - Límites de frecuencia de Cron y Rate-Limiting del ERP: Las APIs de ERPs como Contabilium tienen cupos de peticiones por minuto y por segundo. Si disparás llamadas sin cola, el ERP devuelve
HTTP 429 Too Many Requestsy quedan ventas sin facturar o artículos con stock desfasado.
Comparativa técnica: ¿Web Hosting NVMe, Servidor Cloud VPS o MIA?
| Requerimiento de tu Integración | Web Hosting Profesional NVMe | Servidor Cloud VPS (SSH Root) | MIA (Desarrollo IA + VPS) |
|---|---|---|---|
| Recepción de Webhooks HTTPS (SSL + SNI + TLS 1.3) | Incluido (alta automática en 60 seg) | Incluido (IP dedicada propia y puertos a elección) | Incluido y configurado por MESI |
| Resguardo de Tokens OAuth2 y credenciales | Subcarpeta /www/private/ bloqueada con .htaccess o tabla en MySQL |
Control total del filesystem Linux (fuera del DocumentRoot) |
Sí (bóveda segura gestionada en VPS) |
Frecuencia de Cron Jobs (crontab) |
Cada 5, 10 o 15 minutos desde el panel | Sin restricciones (cada 1 minuto * * * * *) |
Tiempo real + tareas a medida |
Workers persistentes 24/7 (systemd / Supervisor / Colas) |
No (solo ejecución por HTTP o Cron programado) | Sí (acceso SSH root en Ubuntu 24.04 LTS) |
Sí (monitoreado 24/7 por MESI) |
| Lenguajes y motores disponibles | PHP 8.4 / 8.2 + MySQL | PHP 8.4, Node.js, Python, Go, Redis, Docker, PostgreSQL, MySQL | Desarrollado llave en mano por especialistas + IA |
2 Cómo proteger la carpeta de Tokens OAuth2 y rotarlos de forma atómica en PHP 8.4
Es fundamental conocer cómo funciona la estructura de directorios en el servidor antes de elegir dónde guardar tus credenciales:
- En Web Hosting (y entornos con panel FTP): La carpeta
/www/es en realidad el subdominiowww. En el nivel superior a/wwwno tenés permisos de escritura directa por FTP; para tener otra carpeta en ese nivel hay que crear un subdominio desde panel.com.ar (por ejemplomeli.tudominio.com.ar→ carpeta/meli/), y ese subdominio también es accesible vía web. Por lo tanto, para aislar tus tokens OAuth2, configuración y workers CLI dentro de/www/(o dentro de/meli/), debés crear una subcarpetaprivate/bloqueada con un archivo.htaccess(Require all denied). - En un Cloud VPS autoadministrado (
SSH rootcon Ubuntu 24.04 LTS): Tenés control absoluto del sistema de archivos Linux y podés ubicar/var/www/integrador/private/directamente por fuera delDocumentRootde Nginx o Apache.
/www/ (o /meli/ si creaste el subdominio meli.tudominio.com.ar desde panel.com.ar)
├── private/ <-- BLOQUEADO AL NAVEGADOR VÍA .htaccess (HTTP 403)
│ ├── .htaccess <-- Require all denied (impide todo acceso HTTP externo)
│ ├── config/
│ │ ├── env.php <-- Client ID, Client Secret de MeLi y API Key de Contabilium
│ │ └── db.php <-- Conexión PDO a MySQL (localhost)
│ ├── tokens/
│ │ ├── .ht_meli_oauth.json <-- access_token + refresh_token rotados automáticamente
│ │ ├── .ht_meli_oauth.lock <-- Archivo de semáforo para flock(LOCK_EX)
│ │ └── .ht_contabilium.json <-- Bearer token de Contabilium
│ ├── src/
│ │ └── MeliErpTokenManager.php <-- Clase de rotación atómica OAuth2
│ └── cli/
│ └── worker-meli-erp.php <-- Script ejecutado por Cron o demonio systemd
└── webhooks/ <-- PÚBLICO HTTPS
└── meli-callback.php <-- Único endpoint público para recibir POST de Mercado Libre
# Bloquear absolutamente cualquier petición HTTP/HTTPS a /private/ y sus subcarpetas
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
.htaccess en /private/, nombrar los archivos de tokens comenzando con .ht_ (por ejemplo .ht_meli_oauth.json) aprovecha la regla global nativa de Apache (FilesMatch "^\.ht"), que deniega de fábrica cualquier archivo con prefijo .ht incluso si alguien borrara por accidente el .htaccess.
Clase PHP 8.4 para rotar access_token y refresh_token sin condiciones de carrera
El siguiente gestor utiliza flock($lockFp, LOCK_EX) y el patrón double-check locking: si 10 notificaciones de Mercado Libre entran al mismo tiempo cuando el token acaba de expirar, el primer proceso adquiere el lock y renueva el token contra https://api.mercadolibre.com/oauth/token, mientras que los otros 9 esperan unos milisegundos y leen directamente el nuevo token recién guardado en disco sin volver a llamar a la API:
<?php
declare(strict_types=1);
final class MeliErpTokenManager
{
public function __construct(
private readonly string $storageDir,
private readonly string $meliAppId,
private readonly string $meliClientSecret,
private readonly string $contabiliumClientId,
private readonly string $contabiliumClientSecret,
) {}
/**
* Devuelve un access_token válido de Mercado Libre.
* Si faltan menos de 5 minutos para vencer, lo renueva con bloqueo exclusivo (flock).
*/
public function getMeliAccessToken(): string
{
$tokenFile = $this->storageDir . '/.ht_meli_oauth.json';
$lockFile = $this->storageDir . '/.ht_meli_oauth.lock';
$current = $this->readJson($tokenFile);
if ($current && ($current['expires_at'] ?? 0) > (time() + 300)) {
return (string)$current['access_token'];
}
$lockFp = fopen($lockFile, 'c+');
if ($lockFp === false || !flock($lockFp, LOCK_EX)) {
throw new RuntimeException('No se pudo adquirir el lock para rotar el token de Mercado Libre.');
}
try {
// Double-check: verificar si otro proceso ya lo renovó mientras esperábamos el lock
clearstatcache(true, $tokenFile);
$current = $this->readJson($tokenFile);
if ($current && ($current['expires_at'] ?? 0) > (time() + 300)) {
return (string)$current['access_token'];
}
$refreshToken = (string)($current['refresh_token'] ?? '');
if ($refreshToken === '') {
throw new RuntimeException('Falta el refresh_token inicial en .ht_meli_oauth.json');
}
$ch = curl_init('https://api.mercadolibre.com/oauth/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded',
],
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'refresh_token',
'client_id' => $this->meliAppId,
'client_secret' => $this->meliClientSecret,
'refresh_token' => $refreshToken,
]),
]);
$raw = (string)curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($raw, true);
if ($code !== 200 || empty($data['access_token']) || empty($data['refresh_token'])) {
throw new RuntimeException("Error OAuth2 Mercado Libre (HTTP {$code}): {$raw}");
}
$newPayload = [
'access_token' => (string)$data['access_token'],
'refresh_token' => (string)$data['refresh_token'],
'user_id' => (int)($data['user_id'] ?? ($current['user_id'] ?? 0)),
'expires_at' => time() + (int)($data['expires_in'] ?? 21600),
'updated_at' => date('c'),
];
$this->writeJsonAtomic($tokenFile, $newPayload);
return $newPayload['access_token'];
} finally {
flock($lockFp, LOCK_UN);
fclose($lockFp);
}
}
/**
* Obtiene y cachea el Bearer Token de la API REST de Contabilium.
*/
public function getContabiliumToken(): string
{
$tokenFile = $this->storageDir . '/.ht_contabilium.json';
$current = $this->readJson($tokenFile);
if ($current && ($current['expires_at'] ?? 0) > (time() + 300)) {
return (string)$current['access_token'];
}
$ch = curl_init('https://rest.contabilium.com/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'client_id' => $this->contabiliumClientId,
'client_secret' => $this->contabiliumClientSecret,
]),
]);
$raw = (string)curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($raw, true);
if ($code !== 200 || empty($data['access_token'])) {
throw new RuntimeException("Error Token Contabilium (HTTP {$code}): {$raw}");
}
$payload = [
'access_token' => (string)$data['access_token'],
'expires_at' => time() + (int)($data['expires_in'] ?? 86400),
'updated_at' => date('c'),
];
$this->writeJsonAtomic($tokenFile, $payload);
return $payload['access_token'];
}
private function readJson(string $path): ?array
{
if (!is_file($path)) {
return null;
}
$decoded = json_decode((string)file_get_contents($path), true);
return is_array($decoded) ? $decoded : null;
}
private function writeJsonAtomic(string $path, array $data): void
{
$tmp = $path . '.tmp.' . getmypid();
file_put_contents($tmp, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
chmod($tmp, 0600);
rename($tmp, $path);
}
}
3 Receptor de Webhooks HTTPS de Mercado Libre en <50 ms con Cola Idempotente en MySQL
Para cumplir siempre el requisito de responder en menos de 500 ms a Mercado Libre sin perder ni duplicar órdenes, el archivo público /www/webhooks/meli-callback.php solo debe validar el JSON entrante, insertarlo con INSERT IGNORE (o ON DUPLICATE KEY UPDATE) en una tabla de cola en MySQL y responder inmediatamente HTTP 200 OK:
CREATE TABLE IF NOT EXISTS meli_webhook_queue (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
notification_id VARCHAR(64) NOT NULL,
topic VARCHAR(40) NOT NULL,
resource VARCHAR(160) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
payload_json TEXT NOT NULL,
status ENUM('pending', 'processing', 'done', 'error') NOT NULL DEFAULT 'pending',
attempts TINYINT UNSIGNED NOT NULL DEFAULT 0,
last_error VARCHAR(255) DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed_at DATETIME DEFAULT NULL,
UNIQUE KEY uk_topic_resource_pending (topic, resource, status),
KEY idx_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS erp_orders_synced (
meli_order_id BIGINT UNSIGNED PRIMARY KEY,
sku VARCHAR(80) NOT NULL,
quantity INT NOT NULL,
total_amount DECIMAL(14,2) NOT NULL,
contabilium_synced TINYINT(1) NOT NULL DEFAULT 0,
synced_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
http_response_code(200);
exit(json_encode(['ok' => true, 'status' => 'ready']));
}
$rawBody = (string)file_get_contents('php://input');
$event = json_decode($rawBody, true);
if (!is_array($event) || empty($event['resource']) || empty($event['topic'])) {
http_response_code(400);
exit(json_encode(['ok' => false, 'error' => 'invalid_payload']));
}
require_once __DIR__ . '/../private/config/db.php'; // Define $pdo (PDO MySQL dentro de /www/private/)
$notifId = (string)($event['_id'] ?? bin2hex(random_bytes(8)));
$topic = (string)$event['topic']; // Ej: orders_v2, items, shipments, questions
$resource = (string)$event['resource']; // Ej: /orders/2000009876543210
$userId = (int)($event['user_id'] ?? 0);
// Encolamos en ~5 ms evitando duplicados si Mercado Libre reenvía el mismo recurso pendiente
$stmt = $pdo->prepare("
INSERT IGNORE INTO meli_webhook_queue
(notification_id, topic, resource, user_id, payload_json, status, created_at)
VALUES
(?, ?, ?, ?, ?, 'pending', NOW())
");
$stmt->execute([$notifId, $topic, $resource, $userId, $rawBody]);
// Respondemos HTTP 200 OK de inmediato (<20 ms totales)
http_response_code(200);
echo json_encode(['ok' => true, 'queued' => true]);
4 Worker CLI en PHP 8.4: consultar la orden en Mercado Libre y descontar stock en Contabilium / ERP
Una vez que la notificación está en MySQL, el script privado private/cli/worker-meli-erp.php procesa la cola fuera de línea: consulta el detalle real de la orden en Mercado Libre enviando el header x-format-new: true, extrae el SELLER_SKU de cada ítem y actualiza el inventario o notifica el comprobante en Contabilium (o en tu ERP):
<?php
declare(strict_types=1);
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit('Acceso permitido únicamente por CLI.');
}
require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../src/MeliErpTokenManager.php';
$tokenManager = new MeliErpTokenManager(
storageDir: __DIR__ . '/../tokens',
meliAppId: getenv('MELI_APP_ID') ?: '',
meliClientSecret: getenv('MELI_CLIENT_SECRET') ?: '',
contabiliumClientId: getenv('CONTABILIUM_EMAIL') ?: '',
contabiliumClientSecret: getenv('CONTABILIUM_API_KEY') ?: '',
);
$meliToken = $tokenManager->getMeliAccessToken();
$cbToken = $tokenManager->getContabiliumToken();
// Tomamos hasta 25 notificaciones pendientes de órdenes
$stmt = $pdo->query("
SELECT * FROM meli_webhook_queue
WHERE status = 'pending' AND topic IN ('orders_v2', 'orders')
ORDER BY id ASC
LIMIT 25
");
$jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($jobs as $job) {
$jobId = (int)$job['id'];
$resource = (string)$job['resource']; // Ej: /orders/2000009876543210
$pdo->prepare("UPDATE meli_webhook_queue SET status = 'processing', attempts = attempts + 1 WHERE id = ?")
->execute([$jobId]);
try {
// 1. Consultar la orden completa en la API de Mercado Libre
$ch = curl_init("https://api.mercadolibre.com{$resource}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$meliToken}",
'x-format-new: true',
],
]);
$orderRaw = (string)curl_exec($ch);
$orderCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$order = json_decode($orderRaw, true);
if ($orderCode !== 200 || !is_array($order)) {
throw new RuntimeException("MeLi GET {$resource} devolvió HTTP {$orderCode}");
}
// Solo sincronizamos órdenes pagadas / confirmadas
if (($order['status'] ?? '') !== 'paid') {
$pdo->prepare("UPDATE meli_webhook_queue SET status = 'done', processed_at = NOW() WHERE id = ?")
->execute([$jobId]);
continue;
}
$orderId = (int)$order['id'];
// 2. Recorrer ítems vendidos y sincronizar por SKU con Contabilium / ERP
foreach (($order['order_items'] ?? []) as $itemRow) {
$sku = (string)($itemRow['item']['seller_sku'] ?? ($itemRow['item']['seller_custom_field'] ?? ''));
$qty = (int)($itemRow['quantity'] ?? 1);
if ($sku === '') {
continue;
}
// Verificar idempotencia para no descontar dos veces la misma orden
$chk = $pdo->prepare("SELECT meli_order_id FROM erp_orders_synced WHERE meli_order_id = ?");
$chk->execute([$orderId]);
if ($chk->fetch()) {
continue;
}
// Ejemplo: consultar/actualizar stock o enviar comprobante en la API de Contabilium
$cbUrl = 'https://rest.contabilium.com/api/inventarios/getStockBySku?codigo=' . rawurlencode($sku);
$chCb = curl_init($cbUrl);
curl_setopt_array($chCb, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer {$cbToken}",
'Accept: application/json',
],
]);
$cbRes = (string)curl_exec($chCb);
$cbCode = (int)curl_getinfo($chCb, CURLINFO_HTTP_CODE);
curl_close($chCb);
if ($cbCode >= 400) {
throw new RuntimeException("Error Contabilium SKU {$sku} (HTTP {$cbCode}): {$cbRes}");
}
$pdo->prepare("
INSERT INTO erp_orders_synced (meli_order_id, sku, quantity, total_amount, contabilium_synced, synced_at)
VALUES (?, ?, ?, ?, 1, NOW())
")->execute([$orderId, $sku, $qty, (float)($order['total_amount'] ?? 0)]);
}
$pdo->prepare("UPDATE meli_webhook_queue SET status = 'done', processed_at = NOW() WHERE id = ?")
->execute([$jobId]);
// Pausa de 250 ms para respetar el rate-limit de la API del ERP
usleep(250000);
} catch (Throwable $e) {
$nextStatus = ((int)$job['attempts'] >= 5) ? 'error' : 'pending';
$pdo->prepare("UPDATE meli_webhook_queue SET status = ?, last_error = ? WHERE id = ?")
->execute([$nextStatus, substr($e->getMessage(), 0, 250), $jobId]);
}
}
5 Cómo ejecutar el sincronizador: Cron en Web Hosting vs. Demonio systemd en Cloud VPS
Opción A: En un plan de Web Hosting NVMe (ejecución cada 5 o 10 minutos)
Si tu volumen de ventas es moderado y te alcanza con sincronizar cada 5 o 10 minutos, podés programar la tarea desde panel.com.ar invocando el binario CLI de PHP 8.4 sobre el worker ubicado en /www/private/cli/ (o en el subdominio que hayas creado en el panel, por ejemplo /meli/private/cli/):
/usr/local/php84/bin/php /webcp/docs/webs/TU_ID/www/private/cli/worker-meli-erp.php >/dev/null 2>&1
Opción B: En un Servidor Cloud VPS con Ubuntu 24.04 LTS (Tiempo Real + Crontab cada 1 minuto)
Si vendés en varios canales simultáneos y necesitás que el descuento de stock y la facturación impacten en segundos para evitar sobreventas (o si tenés que sincronizar miles de SKUs entre depósitos), en un Servidor Cloud VPS con acceso SSH root podés configurar un servicio persistente con systemd y un crontab cada 1 minuto:
[Unit] Description=Worker de Sincronizacion Mercado Libre y Contabilium/ERP After=network.target mysql.service [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/var/www/integrador/private/cli ExecStart=/usr/bin/php8.4 /var/www/integrador/private/cli/worker-meli-erp-daemon.php Restart=always RestartSec=3 StandardOutput=append:/var/log/meli-erp-worker.log StandardError=append:/var/log/meli-erp-worker.err [Install] WantedBy=multi-user.target
systemctl enable --now meli-erp-worker.service), Linux mantiene tu integrador vivo las 24 horas y, si algún proceso se detiene, systemd lo levanta automáticamente en 3 segundos. Además, podés usar crontab -e para correr conciliaciones cada 1 minuto (* * * * *).
6 Prompt Maestro listo para copiar: creá tu integrador Mercado Libre + ERP con MIA o tu IA
Si contratás MIA (Desarrollo con IA de MESI) o desarrollás con Antigravity, Claude Code o Cursor sobre tu Cloud VPS en MESI, copiá este prompt para generar la arquitectura completa adaptada a tu ERP:
Elegí el plan ideal para tu integración con Mercado Libre y tu ERP
Todos nuestros planes se abonan en Pesos Argentinos con Factura A o B electrónica de ARCA y cuentan con soporte de especialistas técnicos y guardia IA 24/7:
Cloud VPS 2 Dedicado
2 vCPU, 2 GB RAM, 30 GB SSD Enterprise RAID10, IP dedicada y acceso SSH root en Ubuntu 24.04 LTS para correr crons cada 1 minuto y workers systemd.
Cloud VPS 4 Dedicado
4 vCPU, 4 GB RAM y 60 GB SSD Enterprise RAID10. Ideal para sincronizar miles de SKUs, bases MySQL/Redis intensivas, Node.js, Python o Docker.
Hosting Profesional NVMe
Para conectores en PHP 8.4 + MySQL con webhooks HTTPS, subdominios dedicados y crons cada 5 a 15 min. Incluye 20 GB NVMe y 50 casillas.
MIA (Desarrollo IA + VPS)
Programamos y mantenemos tu integración entre Mercado Libre, tu ERP, ARCA y WhatsApp alojada en su propia Cloud VPS dedicada.
Preguntas frecuentes sobre la API de Mercado Libre, Contabilium y Cloud VPS
¿Cuándo me alcanza un plan de Web Hosting NVMe y cuándo necesito un Servidor Cloud VPS?
Si tu conector está desarrollado en PHP 8.2 / 8.4 + MySQL, recibe webhooks HTTPS y ejecuta tareas programadas (cron) cada 5 o 15 minutos, un plan de Web Hosting Profesional NVMe funciona muy bien (resguardando tus tokens OAuth2 en una subcarpeta /www/private/ bloqueada con .htaccess o en MySQL). En cambio, si necesitás correr crons cada 1 minuto (* * * * *), mantener workers o demonios persistentes con systemd o Supervisor, tener control completo del sistema de archivos Linux fuera del DocumentRoot, usar colas en Redis o desplegar servicios en Node.js / Python / Docker, necesitás un Servidor Cloud VPS con acceso SSH root.
¿Por qué en el FTP no puedo crear una carpeta al mismo nivel de /www sin crear un subdominio?
Porque en la estructura del panel cada carpeta del nivel raíz de la cuenta corresponde a un subdominio (www es un subdominio en sí mismo y el nivel superior no tiene permisos de escritura directa por FTP). Si querés utilizar otra carpeta en ese nivel (por ejemplo meli/ o api/), tenés que dar de alta el subdominio desde panel.com.ar, teniendo en cuenta que ese subdominio también es accesible vía web. Por eso, cualquier carpeta interna con tokens o scripts CLI (como /www/private/ o /meli/private/) debe protegerse siempre con .htaccess (Require all denied).
¿Los servidores de MESI bloquean las notificaciones entrantes de los Webhooks de Mercado Libre?
No. Todos los planes de Web Hosting y Cloud VPS de MESI permiten recibir peticiones POST HTTPS entrantes desde los rangos de IP de Mercado Libre y Mercado Pago, con soporte completo de SNI, certificados SSL válidos y TLS 1.2 / 1.3 actualizado para que tus callbacks operen sin rechazos de firewall.
¿Por qué es importante el bloqueo con flock(LOCK_EX) al renovar el token de Mercado Libre?
Porque Mercado Libre utiliza rotación de refresh_token de un solo uso. Si un webhook y un cron intentan refrescar el token vencido en el mismo instante sin un bloqueo exclusivo, la segunda petición enviará un refresh_token recién revocado, provocando un error invalid_grant. Con flock(LOCK_EX) en tu archivo de lock protegido en /private/tokens/, el segundo proceso espera unos milisegundos y reutiliza el nuevo token ya renovado por el primero.
¿Puedo conectar también la facturación electrónica de ARCA (ex AFIP) y avisos por WhatsApp en el mismo servidor?
Sí. En el mismo Web Hosting o Cloud VPS podés integrar los Web Services WSAA y WSFEv1 de ARCA para emitir Factura A o B con CAE instantáneo y enviar notificaciones por la API Oficial de WhatsApp. Y si preferís no programarlo, con MIA te entregamos toda la solución funcionando llave en mano en tu propia Cloud VPS.
Guías Técnicas Relacionadas
Automatizar ventas por WhatsApp API y Mercado Pago con Comercio Agéntico
Perfiles de compra recurrente, avisos de reposición de stock, conciliación automática por webhook y orden de despacho.
Integrar Factura Electrónica ARCA (ex AFIP) WSFEv1 con MIA o PHP 8.4
Emití Factura A, B o C con CAE y QR oficial automáticamente en cada venta desde PHP 8.4 o con MIA.
Hosting compartido vs. Servidor Cloud VPS: cuándo cambiar
Comparativa de recursos dedicados, acceso SSH root, demonios en segundo plano y escalabilidad.
MIA