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

Integración con webhooks de pago en la tienda de tu servidor de MU Online

Configura webhooks de pago (Mercado Pago, PagSeguro, Stripe) en la tienda online de tu servidor de MU Online, con validación de firma, idempotencia y acreditación automática de VIP/Zen/Cash Points.

GA Gabriel · Actualizado el 21 abr 2024 · ⏱ 16 min de lectura
Respuesta rápida

La tienda de Cash Points es una de las principales fuentes de ingresos de un servidor de MU Online privado, y el eslabón más frágil de ella suele ser justamente la confirmación de pago. Depender solo de la pantalla de "pago aprobado" en el navegador del jugador es receta para chargebacks, fraude y c

La tienda de Cash Points es una de las principales fuentes de ingresos de un servidor de MU Online privado, y el eslabón más frágil de ella suele ser justamente la confirmación de pago. Depender solo de la pantalla de "pago aprobado" en el navegador del jugador es receta para chargebacks, fraude y créditos duplicados. La solución correcta es integrar webhooks: notificaciones asíncronas que la pasarela de pago envía directamente a tu servidor, confirmando que el dinero realmente ingresó. Este tutorial cubre la configuración de webhooks con Mercado Pago, PagSeguro y Stripe, la validación de firma, el tratamiento de idempotencia y la acreditación automática de VIP, Zen o Cash Points en la cuenta del jugador, con los errores más comunes de quien implementa esto por primera vez.

Por qué la confirmación vía navegador no es suficiente

Cuando el jugador finaliza el pago, la pasarela redirige su navegador a una URL de "éxito" en tu sitio. Esa redirección no es confiable como fuente de verdad: el jugador puede cerrar la pestaña antes de completar, perder la conexión, o en casos raros manipular la URL de retorno intentando forjar un ?status=approved. El webhook, en contraste, es una llamada HTTP servidor a servidor, firmada criptográficamente, disparada por la propia pasarela cuando el estado del pago cambia de verdad en su lado.

Visión general del flujo completo

  1. El jugador elige un paquete de Cash Points en la tienda web y es redirigido al checkout de la pasarela.
  2. La pasarela procesa el pago (Pix, tarjeta, boleto).
  3. La pasarela envía un webhook POST a una URL tuya, informando el estado de la transacción.
  4. Tu backend valida la firma, verifica idempotencia y acredita el valor en la tabla de créditos del jugador (leída por el servidor de MU al login o vía comando in-game).
  5. La pasarela también redirige el navegador del jugador a la página de éxito, solo como UX, no como fuente de verdad.

Comparando las principales pasarelas usadas por servidores de MU en Latinoamérica

PasarelaHeader de firmaFormato del payloadObservación
Mercado Pagox-signature (HMAC SHA256)JSON con data.id del pagoNecesita buscar el pago completo vía API después del webhook
PagSeguro/PagBankToken en la URL de notificaciónXML/JSON según la API (legada vs. nueva)La API legada envía solo el código; la nueva envía payload completo
StripeStripe-Signature (HMAC SHA256)JSON completo del eventoLa librería oficial ya valida la firma (stripe.webhooks.constructEvent)
Pagar.mex-hub-signature (HMAC SHA1)JSON completoFormato similar a Mercado Pago

Paso 1 — Crear el endpoint que recibe el webhook

Usa una ruta dedicada, fuera de cualquier autenticación de sesión de usuario (la pasarela no tiene cookie de sesión):

<?php
// webhook_pagamento.php
$payload = file_get_contents('php://input');
$headers = getallheaders();

// nunca confíes en el payload antes de validar la firma
if (!validarAssinatura($payload, $headers['x-signature'] ?? '')) {
    http_response_code(401);
    exit('firma inválida');
}

$evento = json_decode($payload, true);
processarEvento($evento);
http_response_code(200);

Paso 2 — Validar la firma (HMAC)

Nunca proceses un webhook sin confirmar que realmente vino de la pasarela. El principio es el mismo en todas: recalcular el HMAC con tu clave secreta y comparar con el header recibido, usando comparación de tiempo constante para evitar un timing attack:

function validarAssinatura(string $payload, string $assinaturaRecebida): bool {
    $chaveSecreta = getenv('WEBHOOK_SECRET');
    $assinaturaCalculada = hash_hmac('sha256', $payload, $chaveSecreta);
    return hash_equals($assinaturaCalculada, $assinaturaRecebida);
}

Guarda el WEBHOOK_SECRET en una variable de entorno, nunca en el código fuente versionado.

Paso 3 — Buscar el estado real de la transacción (cuando sea necesario)

Algunas pasarelas (Mercado Pago es el caso clásico) envían solo un ID en el webhook, y necesitas consultar su API para confirmar el estado real; esto evita que un webhook falsificado con estado "approved" sea aceptado sin verificación cruzada:

$paymentId = $evento['data']['id'];
$resposta = httpGet("https://api.mercadopago.com/v1/payments/{$paymentId}", [
    'Authorization: Bearer ' . getenv('MP_ACCESS_TOKEN')
]);
$pagamento = json_decode($resposta, true);

if ($pagamento['status'] !== 'approved') {
    http_response_code(200); // reconoce el evento, pero no acredita
    exit;
}

Paso 4 — Garantizar idempotencia

Las pasarelas reenvían el mismo webhook varias veces si no reciben 200 a tiempo, o por política de reintentos. Registra el ID de la transacción antes de acreditar, y rechaza duplicados:

CREATE TABLE webhook_eventos (
    id_transacao VARCHAR(100) PRIMARY KEY,
    processado_em DATETIME,
    valor_creditado DECIMAL(10,2)
);
if (transacaoJaProcessada($paymentId)) {
    http_response_code(200);
    exit; // ya acreditado, evita duplicar
}

Paso 5 — Acreditar VIP, Zen o Cash Points

Después de validado y confirmado como no duplicado, acredita en la tabla que lee el GameServer (directamente en la base del MU, o en una tabla intermedia que un servicio acredita periódicamente):

UPDATE AccountCash SET CashPoints = CashPoints + @valorComprado
WHERE AccountID = @conta;

INSERT INTO webhook_eventos (id_transacao, processado_em, valor_creditado)
VALUES (@paymentId, GETDATE(), @valorComprado);

Si tu emulador usa Web Cash Shop con tabla propia (ej.: IGCN_CashShop), acredita en la estructura correspondiente y asegúrate de que el GameServer relea el saldo en el próximo login o vía evento en tiempo real, si lo soporta.

Paso 6 — Notificar al jugador

Envía un correo de confirmación y, si es posible, una notificación in-game (correo del juego, mensaje de sistema) confirmando el crédito. Esto reduce drásticamente los tickets de soporte del tipo "pagué y no recibí", ya que muchos casos son solo un retraso de algunos segundos entre el pago y el webhook.

Probando webhooks en ambiente local

Las pasarelas no pueden alcanzar localhost. Usa un túnel público durante el desarrollo:

ngrok http 8443
# copia la URL https generada y regístrala como endpoint de webhook en el panel de la pasarela

Todas las pasarelas mencionadas ofrecen modo sandbox/prueba; usa tarjetas y cuentas de prueba antes de apuntar a producción.

Seguridad adicional: whitelist de IP y rate limit

Complementariamente a la validación de firma, restringe el endpoint de webhook por IP de origen cuando la pasarela publica una lista fija (Stripe y PagSeguro publican rangos), y aplica rate limiting en el endpoint para mitigar intentos de fuerza bruta contra la validación de firma.

Logs y auditoría

Guarda todo payload recibido (incluso los rechazados por firma inválida) en un log separado, con timestamp e IP de origen. Esto es esencial para investigar disputas de chargeback y para auditar si algún crédito se aplicó indebidamente.

Errores comunes y soluciones

SíntomaCausa probableSolución
Cash Points acreditados por duplicadoWebhook reenviado sin verificación de idempotenciaRegistrar el ID de transacción e ignorar duplicados
El webhook nunca llegaEndpoint sin HTTPS válido o URL incorrecta en el panelConfigurar certificado válido y revisar la URL registrada en la pasarela
La firma siempre es inválidaClave secreta incorrecta o payload alterado antes de la validación (ej. el framework haciendo parse)Validar sobre el raw body, antes de cualquier decodificación
El jugador pagó pero no recibióLa falla en el crédito devolvió 200 a la pasarela por errorDevolver 500 en falla de crédito para forzar el reenvío
Webhook de prueba (sandbox) procesado en producciónAmbiente sandbox y producción compartiendo el mismo endpoint sin distinciónUsar endpoints/claves separadas para sandbox y producción
El chargeback no se refleja en el juegoFalta de tratamiento del evento de reembolso/chargebackImplementar un handler para eventos de reembolso y remover crédito/aplicar baneo según la política

Lista de verificación de integración de pagos

  • Endpoint HTTPS dedicado para webhooks, fuera de la autenticación de sesión normal.
  • Validación de firma HMAC implementada y probada con payload real.
  • Consulta del estado real vía API de la pasarela cuando el webhook solo trae un ID.
  • Tabla de idempotencia registrando IDs de transacción ya procesados.
  • Acreditación de VIP/Zen/Cash Points automatizada y validada en sandbox.
  • Notificación al jugador (correo y/o in-game) después del crédito.
  • Logs de todos los payloads recibidos, incluyendo los rechazados.
  • Tratamiento de eventos de reembolso/chargeback implementado.

Con los webhooks confiables y la tienda acreditando automáticamente, vale la pena revisar la arquitectura general del sitio y del servidor para garantizar que el resto de la infraestructura soporte el volumen de transacciones con seguridad; comienza por la guía de creación de servidor de MU Online si aún no revisaste la base.

Preguntas frecuentes

¿Por qué no puedo confiar solo en la redirección de éxito del pago?

Porque la redirección ocurre en el navegador del jugador, que puede ser manipulado, interrumpido o nunca completado (cerrar la pestaña, caída de conexión). El webhook viene directo del servidor de la pasarela de pago hacia tu backend, de forma asíncrona y confiable, y es la única fuente de verdad para liberar el crédito.

¿Cómo sé que el webhook realmente vino de la pasarela de pago y no de un estafador?

Toda pasarela seria firma el payload con una clave secreta (HMAC) o envía un header de firma. Vuelves a calcular esa firma en tu backend con tu clave secreta y la comparas; si no coincide, descartas la solicitud. Nunca proceses un webhook sin esa validación.

¿Qué es la idempotencia y por qué es obligatoria acá?

Es garantizar que procesar el mismo evento dos veces no acredite Cash Points por duplicado. Las pasarelas reenvían webhooks cuando no reciben confirmación a tiempo, así que necesitas registrar el ID del evento/transacción e ignorar los duplicados antes de acreditar.

¿Necesito HTTPS para recibir webhooks?

Sí, es un requisito de prácticamente todas las pasarelas (Mercado Pago, Stripe, PagSeguro); rechazan o advierten sobre endpoints HTTP. Usa un certificado válido (Let's Encrypt es suficiente) en el dominio de tu tienda.

¿Qué hacer si el crédito falla después de que el pago ya fue aprobado?

Registra la falla en una cola de reprocesamiento y devuelve HTTP 500 a la pasarela para que reenvíe el webhook automáticamente. Nunca devuelvas 200 si el crédito no se aplicó; eso hace que la pasarela deje de intentar y el jugador se quede sin recibir nada.

GA
Editor de guías y builds

Gabriel cubre gameplay, builds de clases, PvP y progresión. Prueba cada estrategia en un servidor antes de publicar.

Sigue leyendo

Artículos relacionados