El mayor portal de MU Online de Brasil — desde 2003
Tutorial Avanzado Website

Cómo integrar donaciones con Mercado Pago en el sitio del servidor de MU

Integra Mercado Pago al sitio de tu servidor de MU Online para recibir donaciones vía Pix y tarjeta, con preferencias de pago, webhook de confirmación y entrega automática de créditos de forma segura e idempotente.

BR Bruno · Actualizado el 10 jul 2025 · ⏱ 27 min de lectura
Respuesta rápida

Las donaciones son el motor financiero de la mayoría de los servidores privados de MU Online, y Mercado Pago es la pasarela más usada en Brasil porque acepta Pix, tarjeta y boleto con una integración relativamente directa. El desafío no es técnico en el sentido de la complejidad del código, sino de

Las donaciones son el motor financiero de la mayoría de los servidores privados de MU Online, y Mercado Pago es la pasarela más usada en Brasil porque acepta Pix, tarjeta y boleto con una integración relativamente directa. El desafío no es técnico en el sentido de la complejidad del código, sino de confiabilidad: necesitas garantizar que todo pago aprobado se convierta en crédito en el juego exactamente una vez, que ningún crédito se libere sin pago real y que fallas de red o intentos de fraude no abran brechas. En este tutorial vas a construir ese puente desde cero, creando la preferencia de pago, tratando el retorno del jugador y, lo más importante, procesando el webhook que confirma el dinero y dispara la entrega automática de los créditos. Los detalles de la API y los nombres de campo pueden cambiar; Mercado Pago actualiza su documentación con frecuencia, así que trata el código como un ejemplo funcional que debes contrastar con la referencia oficial vigente.

La regla mental que atraviesa toda la integración es simple: el sitio nunca decide por sí solo que un pago fue realizado. Quien decide es Mercado Pago, y la única forma confiable de saberlo es consultar su API por el ID del pago. Que el jugador vuelva al sitio con un mensaje de éxito no prueba nada, porque esa URL puede falsificarse o abrirse sin pagar. Toda la arquitectura gira en torno a confiar únicamente en la respuesta autenticada de la API.

Requisitos previos

Con el servidor ya en línea (si no lo está, revisa cómo crear un servidor de MU Online), reúne:

  • Cuenta en Mercado Pago con las credenciales de producción: Public Key y, sobre todo, el Access Token.
  • Sitio con PHP 7.4+ accesible públicamente por HTTPS, ya que el webhook necesita una URL pública con TLS.
  • Extensión cURL habilitada en PHP para llamar a la API de Mercado Pago.
  • Sistema de cuentas y login funcionando, para saber a qué cuenta acreditar.
  • Tabla de saldo/créditos ya definida en la base de datos (WCoin, puntos de donación o lo que use tu servidor).
  • Tabla de control de transacciones con clave de idempotencia, como la modelada en el tutorial de Cash Shop.
  • Un certificado SSL válido en el dominio; Mercado Pago no entrega webhook a endpoints sin HTTPS confiable.

Guarda el Access Token con el mismo cuidado que una contraseña de banco. Permite mover tu cuenta a través de la API. Nunca lo pongas en código versionado público, nunca lo expongas en el front-end y mantenlo en un archivo de configuración fuera del webroot.

Cómo funciona el flujo de donación

Entender el ciclo completo antes de programar evita errores de arquitectura. El flujo tiene cinco etapas bien definidas, y cada una tiene un dueño claro.

EtapaDónde ocurreResponsable¿Confiable para acreditar?
Jugador elige paqueteSitioTu PHPNo
Creación de la preferenciaSitio llama a la API de MPTu PHP + APINo
PagoPantalla de Mercado PagoMercado PagoNo
Retorno del jugadorback_url en el sitioNavegadorNo
Notificación webhookLa API de MP llama a tu endpointMercado PagoSí, tras consultar la API

Fíjate en que solo la última fila es confiable para liberar crédito. Todas las demás son partes del flujo de experiencia, pero ninguna prueba el pago. Esta tabla es el mapa mental que impide el error clásico de acreditar en la back_url.

Guardando las credenciales con seguridad

Aísla las credenciales en un archivo de configuración que queda fuera de la carpeta pública y nunca va al control de versiones.

<?php
// config/mercadopago.php  (fuera del webroot o protegido)
return [
    'access_token' => getenv('MP_ACCESS_TOKEN') ?: 'APP_USR-xxxxxxxx',
    'public_key'   => getenv('MP_PUBLIC_KEY')   ?: 'APP_USR-yyyyyyyy',
    'base_url'     => 'https://api.mercadopago.com',
    'site_url'     => 'https://tuservidor.com',
];

Preferir getenv() permite inyectar las credenciales por variable de entorno en producción, que es la práctica recomendada. El valor fijo sirve solo de respaldo para desarrollo. En producción, define MP_ACCESS_TOKEN en el entorno del servidor web y deja el código sin el secreto escrito.

Creando la preferencia de pago

La preferencia es el objeto que describe la donación: monto, descripción, URLs de retorno y la URL de notificación. La creas llamando a la API de Mercado Pago, que devuelve un enlace al que se envía al jugador.

<?php
// criar-doacao.php
session_start();
$cfg = require __DIR__ . '/config/mercadopago.php';

if (empty($_SESSION['conta_mu'])) {
    header('Location: /login');
    exit;
}

$pacotes = [
    'p1' => ['titulo' => 'Donación 1000 WCoin', 'valor' => 10.00, 'wcoin' => 1000],
    'p2' => ['titulo' => 'Donación 2500 WCoin', 'valor' => 20.00, 'wcoin' => 2500],
    'p3' => ['titulo' => 'Donación 6000 WCoin', 'valor' => 40.00, 'wcoin' => 6000],
];

$id = $_GET['pacote'] ?? '';
if (!isset($pacotes[$id])) { http_response_code(400); exit('Paquete inválido'); }
$p = $pacotes[$id];

// external_reference amarra el pago a la cuenta y al paquete en nuestra base de datos
$refExterna = json_encode([
    'conta'  => $_SESSION['conta_mu'],
    'pacote' => $id,
    'wcoin'  => $p['wcoin'],
    'nonce'  => bin2hex(random_bytes(8)),
]);

$preferencia = [
    'items' => [[
        'title'       => $p['titulo'],
        'quantity'    => 1,
        'unit_price'  => $p['valor'],
        'currency_id' => 'BRL',
    ]],
    'external_reference' => $refExterna,
    'back_urls' => [
        'success' => $cfg['site_url'] . '/doacao-retorno.php?st=ok',
        'pending' => $cfg['site_url'] . '/doacao-retorno.php?st=pend',
        'failure' => $cfg['site_url'] . '/doacao-retorno.php?st=fail',
    ],
    'auto_return'  => 'approved',
    'notification_url' => $cfg['site_url'] . '/webhook-mercadopago.php',
];

$ch = curl_init($cfg['base_url'] . '/checkout/preferences');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $cfg['access_token'],
    ],
    CURLOPT_POSTFIELDS     => json_encode($preferencia),
]);
$resposta = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http !== 201) {
    error_log('Fallo al crear la preferencia de MP: ' . $resposta);
    exit('Error al iniciar el pago. Inténtalo de nuevo.');
}

$dados = json_decode($resposta, true);
// Redirige al jugador al checkout de Mercado Pago
header('Location: ' . $dados['init_point']);
exit;

El campo external_reference es el pegamento entre Mercado Pago y tu base de datos. En él guardas la cuenta, el paquete y la cantidad de WCoin, además de un nonce aleatorio para hacer única cada preferencia. Cuando llegue el webhook, lees esa referencia de vuelta y sabes exactamente a quién acreditar y cuánto, sin depender de sesión ni cookie, que no existen en el contexto del webhook.

El notification_url es la dirección que Mercado Pago va a llamar cuando cambie el estado del pago. Es ese endpoint el que hace el trabajo de verdad.

La página de retorno del jugador

Cuando el pago termina, Mercado Pago redirige al jugador a una de las back_urls. Esa página solo sirve para dar retroalimentación visual. No acredita nada.

<?php
// doacao-retorno.php
$status = $_GET['st'] ?? '';
$mensagens = [
    'ok'   => '¡Pago recibido! Tus créditos se liberarán en instantes.',
    'pend' => 'Pago pendiente. En cuanto sea aprobado, entran los créditos.',
    'fail' => 'El pago no se completó. No se cobró ningún monto.',
];
$msg = $mensagens[$status] ?? 'Estado desconocido.';
?>
<h1>Donación</h1>
<p><?= htmlspecialchars($msg) ?></p>
<p><a href="/painel">Volver al panel</a></p>

Fíjate en el texto "se liberarán en instantes" incluso en el éxito. Es intencional: la liberación depende del webhook, que normalmente llega en segundos, pero es asíncrono. Prometer liberación inmediata en esta página sería mentir, porque el crédito aún no ocurrió. Gestionar esa expectativa reduce los reclamos de "pagué y no recibí al instante".

El webhook: donde el crédito realmente ocurre

Este es el endpoint más importante de toda la integración. Recibe la notificación, consulta la API para confirmar el estado verdadero y, solo entonces, acredita de forma idempotente.

<?php
// webhook-mercadopago.php
$cfg = require __DIR__ . '/config/mercadopago.php';
require_once __DIR__ . '/lib/cashshop.php';   // creditarWCoin() con idempotencia

// 1) Responder rápido es importante; capturamos el ID del pago
$entrada = json_decode(file_get_contents('php://input'), true);
$tipo = $_GET['type'] ?? $entrada['type'] ?? '';
$pagamentoId = $_GET['data.id'] ?? $entrada['data']['id'] ?? null;

if ($tipo !== 'payment' || !$pagamentoId) {
    http_response_code(200); // reconoce e ignora lo que no interesa
    exit;
}

// 2) NUNCA confíes en el cuerpo. Consulta la API por el ID.
$ch = curl_init($cfg['base_url'] . "/v1/payments/{$pagamentoId}");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $cfg['access_token']],
]);
$resp = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http !== 200) {
    http_response_code(500); // MP lo intentará de nuevo después
    exit;
}

$pag = json_decode($resp, true);

// 3) Solo el estado aprobado acredita
if (($pag['status'] ?? '') !== 'approved') {
    http_response_code(200);
    exit;
}

// 4) Leer el external_reference que amarramos en la creación
$ref = json_decode($pag['external_reference'] ?? '{}', true);
$conta = $ref['conta'] ?? null;
$wcoin = (int)($ref['wcoin'] ?? 0);

if (!$conta || $wcoin <= 0) {
    http_response_code(200); // nada que hacer
    exit;
}

// 5) Acreditar usando el ID del pago como clave de idempotencia
$ok = creditarWCoin($conta, $wcoin, 'mercadopago', (string)$pagamentoId);

http_response_code($ok ? 200 : 500);

Tres decisiones definen la seguridad de este webhook. Primero, ignora por completo cualquier valor de estado que venga en el cuerpo de la notificación y va a buscar la verdad en la API con el Access Token, lo que hace imposible falsificar un pago aprobado. Segundo, usa el external_reference grabado en la creación para saber a quién acreditar, sin depender de sesión. Tercero, pasa el $pagamentoId como clave de idempotencia a la función creditarWCoin, que, con el índice único en la base de datos, garantiza que webhooks repetidos, algo normal en Mercado Pago, nunca acrediten dos veces.

El código de respuesta HTTP también es estratégico. Devolver 200 le dice a Mercado Pago "recibí, puedes dejar de reenviar". Devolver 500 dice "hubo un problema, inténtalo de nuevo más tarde". Por eso, cuando la consulta a la API falla por inestabilidad momentánea, respondemos 500 a propósito: queremos que Mercado Pago reenvíe la notificación hasta que logremos procesarla.

La función de crédito idempotente

El webhook depende de una función de crédito que sea segura contra la duplicidad. Reutiliza exactamente la misma lógica transaccional del sistema de Cash Shop.

<?php
// lib/cashshop.php (fragmento esencial)
function creditarWCoin(string $conta, int $qtd, string $origem, string $ref): bool {
    if ($qtd <= 0) return false;
    $conn = getDB();

    // El procedimiento hace: valida cuenta, chequea GatewayRef (idempotencia),
    // suma WCoin en transacción y graba el log. Ver tutorial de Cash Shop.
    $stmt = sqlsrv_query($conn, "{CALL dbo.CreditarWCoin(?, ?, ?, ?)}", [
        [$conta,  SQLSRV_PARAM_IN],
        [$qtd,    SQLSRV_PARAM_IN],
        [$origem, SQLSRV_PARAM_IN],
        [$ref,    SQLSRV_PARAM_IN],
    ]);

    if ($stmt === false) {
        error_log('Error de crédito de donación: ' . print_r(sqlsrv_errors(), true));
        return false;
    }
    return true;
}

La $ref aquí es el ID del pago de Mercado Pago. Como el procedimiento graba ese valor en una columna con índice único, un segundo intento de acreditar el mismo pago es bloqueado por la propia base de datos. Esa es la diferencia entre un sistema que aguanta reenvíos de la pasarela y uno que acredita créditos fantasma.

Probando antes de ir a producción

Nunca pongas un sistema de pago en línea sin probar el flujo completo. Mercado Pago proporciona credenciales de prueba y usuarios de prueba que simulan al pagador y al vendedor. Con ellos recorres todo el camino, del clic al crédito, sin mover dinero real.

  1. Genera las credenciales de prueba en el panel de desarrollador de Mercado Pago.
  2. Crea usuarios de prueba comprador y vendedor.
  3. Usa el Access Token de prueba en la configuración.
  4. Haz una donación con una tarjeta de prueba aprobada y confirma que el webhook acredita.
  5. Haz una donación con tarjeta rechazada y confirma que nada se acredita.
  6. Simula un reenvío del webhook y confirma que el crédito no se duplica.
  7. Solo después cambia a las credenciales de producción.

Probar el caso del webhook duplicado es el paso que más gente omite y el que más perjuicio causa después. Fuérzalo a propósito antes de confiar en el sistema.

Seguridad y cumplimiento

El pago involucra dinero y datos sensibles, así que algunas reglas no son negociables. Nunca guardes datos de tarjeta en tu base de datos; deja eso enteramente en manos de Mercado Pago usando Checkout Pro. Mantén el Access Token fuera del código público y del front-end. Sirve todo el flujo bajo HTTPS, incluido el endpoint del webhook. Registra en log cada notificación recibida para auditoría, incluso las ignoradas, porque eso ayuda a investigar disputas. Y aplica límites de cordura: si llega un pago con un valor que no corresponde a ningún paquete conocido, regístralo y no acredites automáticamente, tratándolo como caso a revisar manualmente.

Vale la pena reforzar que este tutorial cubre la integración técnica, no orientación financiera, contable o fiscal. Las reglas de tributación sobre donaciones y movimiento de cuenta son responsabilidad tuya y de tu contador; consulta a un profesional para esa parte.

Errores comunes y soluciones

SíntomaCausa probableSolución
Crédito liberado sin pagoCrédito hecho en la back_urlMueve el crédito exclusivamente al webhook
El webhook nunca lleganotification_url sin HTTPS válidoConfigura SSL confiable y la URL pública correcta
El jugador pagó y no recibióEl webhook falló y devolvió 200Devuelve 500 en caso de fallo para forzar el reenvío; revisa los logs
Crédito dobleReenvío del webhook sin idempotenciaUsa el ID del pago como clave única en la base de datos
No sé a qué cuenta acreditarexternal_reference sin completarGraba cuenta y paquete en external_reference al crear la preferencia
Una notificación falsa acreditóConfió en el cuerpo sin consultar la APIConsulta siempre /v1/payments/{id} con el Access Token
Error 401 en la APIAccess Token incorrecto o de prueba en producciónConfirma el token correcto del entorno vigente
Valor distinto al del paqueteManipulación o paquete alteradoValida el monto pagado contra la tabla de paquetes

Lista de verificación de lanzamiento

  • Access Token de producción guardado fuera del webroot y del versionado
  • Sitio entero servido bajo HTTPS con certificado válido
  • Preferencia de pago creada con external_reference que contiene cuenta y wcoin
  • notification_url apuntando al webhook público y correcto
  • Webhook consultando /v1/payments/{id} en vez de confiar en el cuerpo
  • Crédito ocurriendo solo cuando el estado es approved
  • ID del pago usado como clave de idempotencia en el crédito
  • Índice único en la columna de referencia de transacción en la base de datos
  • Página de retorno solo informativa, sin acreditar
  • Códigos HTTP 200/500 usados correctamente para controlar los reenvíos
  • Flujo completo probado con credenciales y usuarios de prueba
  • Prueba de webhook duplicado confirmando crédito único
  • Prueba de pago rechazado confirmando ausencia de crédito
  • Log de todas las notificaciones recibidas activo
  • Límites de cordura de valor por transacción configurados

Con este flujo, tu servidor recibe donaciones vía Pix y tarjeta de forma automática, segura y a prueba de los reenvíos que forman parte de la vida real de cualquier pasarela. El principio que sostiene todo se repite de punta a punta: el crédito solo nace de un pago que la propia API de Mercado Pago confirmó como aprobado, entregado exactamente una vez.

Preguntas frecuentes

¿Necesito CNPJ para usar Mercado Pago en el servidor?

No necesariamente; la cuenta puede ser de persona física. Sin embargo, para volúmenes mayores y para reducir riesgos de bloqueo por movimiento atípico, muchos dueños de servidor abren un MEI o una cuenta empresarial. Las reglas de cuenta y los límites varían y los define Mercado Pago, no el código.

¿Cuál es la diferencia entre Checkout Pro y Checkout Transparente?

En Checkout Pro el jugador es redirigido a la pantalla de Mercado Pago, que se encarga de todo, lo que es más simple y seguro para empezar. En Checkout Transparente el pago ocurre dentro de tu sitio, lo que exige más responsabilidad con los datos. Para servidores de MU, Checkout Pro suele ser el camino recomendado.

¿El crédito debe entregarse en la página de retorno o en el webhook?

Siempre en el webhook. La página de retorno (back_url) sirve solo para mostrar un mensaje al jugador y puede cerrarse, recargarse o ni siquiera abrirse. La entrega del crédito tiene que ocurrir en la notificación webhook, que es la fuente confiable del estado real del pago.

¿Cómo confirmo que la notificación vino realmente de Mercado Pago?

Nunca confíes directamente en el cuerpo de la notificación. Al recibir el webhook, consulta la API de Mercado Pago por el ID del pago usando tu Access Token y verifica el estado devuelto por la propia API. Opcionalmente, valida la firma del encabezado cuando esté disponible.

¿El Pix se acredita al instante en la cuenta del jugador?

El Pix se confirma en segundos, pero el crédito en el juego solo entra cuando llega el webhook y consultas la API confirmando el estado approved. En la práctica son pocos segundos, pero nunca acredites antes de esa confirmación, aunque el jugador jure que pagó.

BR
Editor de eventos, mapas e ítems

Bruno es especialista en eventos, mapas, bosses y economía de ítems de MU Online. Documenta cada detalle basándose en el juego real.

Sigue leyendo

Artículos relacionados