Como integrar doações com PayPal e PIX no servidor de MU Online
Aprenda a construir um sistema de doações completo para o seu servidor de MU Online, integrando PayPal via IPN/Webhook e PIX através de gateways brasileiros, com creditação automática de VIP e WCoin sem intervenção manual.
Montar um sistema de doações confiável é um dos passos que mais diferencia um servidor de MU Online amador de um projeto sustentável. A maioria dos administradores começa pedindo comprovante por Discord e creditando VIP na mão, o que não escala, gera atrasos e abre espaço para erro humano e golpes.
Montar um sistema de doações confiável é um dos passos que mais diferencia um servidor de MU Online amador de um projeto sustentável. A maioria dos administradores começa pedindo comprovante por Discord e creditando VIP na mão, o que não escala, gera atrasos e abre espaço para erro humano e golpes. Neste tutorial avançado você vai construir um fluxo de doação de ponta a ponta: uma página de checkout no site, integração com o PayPal via IPN/Webhook, integração com PIX através de um gateway brasileiro (usando o padrão de cobrança dinâmica com QR Code e confirmação por webhook), e a creditação automática de recompensas no banco de dados do jogo. Todos os exemplos de nomes de tabelas, colunas e valores são apenas ilustrativos e variam por versão (Season 6, Season 16, arquivos IGCN, MuEMU, etc.) — sempre confira o schema real da sua distribuição antes de rodar qualquer SQL em produção.
O princípio central que você deve levar deste guia é simples: nunca credite recompensa com base no que o navegador do usuário te diz. A confirmação de pagamento válida vem sempre de uma notificação servidor-a-servidor, assinada e verificável, enviada pelo provedor de pagamento diretamente ao seu backend. Tudo o mais é apenas interface.
Pré-requisitos
Antes de começar, garanta que você tem o ambiente e os acessos abaixo. Sem eles, o restante do tutorial não roda.
- Um site funcional em PHP 8.0 ou superior (idealmente 8.2+) com acesso ao
composerpara instalar SDKs. - Servidor web com HTTPS válido (Let's Encrypt já resolve). Webhooks de PayPal e de gateways PIX exigem endpoint HTTPS público — nada de
localhostou certificado autoassinado. - Acesso ao banco de dados do jogo (SQL Server, na maioria das distribuições de MU) e ao banco do site (MySQL/MariaDB, comum em CMS).
- Uma conta PayPal Business (a pessoal não emite as credenciais de API REST completas).
- Uma conta em um gateway de pagamento brasileiro que ofereça PIX via API com webhook (Mercado Pago, Efí/antigo Gerencianet, Asaas, PagSeguro, entre outros). Os exemplos usam o padrão genérico "criar cobrança → receber webhook".
- Noções de fila/cron ou de transações SQL para garantir creditação atômica.
> Segurança: trate as credenciais de API (Client ID, Secret, tokens) como senhas. Nunca as coloque no repositório Git nem em JavaScript do front-end. Use variáveis de ambiente (.env) fora da raiz pública sempre que possível.
Arquitetura geral do fluxo
Antes de escrever código, visualize o caminho de uma doação. O jogador nunca toca no banco do jogo diretamente; ele passa por camadas que validam cada etapa.
- O jogador faz login no site e escolhe um pacote (ex.: R$ 20 = 5.000 WCoin + 30 dias de VIP).
- O backend cria um pedido pendente no banco do site, com um identificador único (
order_id). - O jogador é redirecionado ao PayPal ou recebe um QR Code PIX.
- Após pagar, o provedor envia um webhook ao seu servidor confirmando o pagamento.
- O backend valida a assinatura, confere valor/moeda, marca o pedido como pago e credita a recompensa no banco do jogo — tudo de forma idempotente.
- O jogador vê o saldo atualizado no painel.
| Camada | Responsabilidade | Nunca deve |
|---|---|---|
| Front-end | Exibir pacotes e iniciar o pagamento | Decidir se algo foi pago |
| Backend (pedidos) | Criar/atualizar pedidos, gerar cobrança | Creditar sem webhook confirmado |
| Webhook handler | Validar assinatura e confirmar | Confiar em parâmetros de retorno (?status=success) |
| Creditação (game DB) | Adicionar VIP/WCoin de forma atômica | Rodar duas vezes para a mesma transação |
Modelagem da tabela de pedidos
Crie no banco do site uma tabela para rastrear cada doação. A idempotência depende dela: a coluna transaction_id do provedor deve ser única para impedir crédito duplicado.
CREATE TABLE donations (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
account VARCHAR(20) NOT NULL, -- conta do jogo
method ENUM('paypal','pix') NOT NULL,
package_id INT NOT NULL,
amount_brl DECIMAL(10,2) NOT NULL,
wcoin INT NOT NULL,
vip_days INT NOT NULL DEFAULT 0,
status ENUM('pending','paid','credited','failed','refunded') DEFAULT 'pending',
provider_txn VARCHAR(120) NULL,
ip VARCHAR(45) NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
paid_at DATETIME NULL,
UNIQUE KEY uq_provider_txn (provider_txn)
);
Repare em dois detalhes: o status passa por paid (webhook chegou) e só depois credited (recompensa aplicada no jogo). Separar esses dois estados é o que te salva quando a creditação falha por o banco do jogo estar offline — você reprocessa sem cobrar de novo.
Integração com PayPal (API REST + Webhook)
O PayPal moderno usa a API REST v2 com autenticação OAuth2. Você obtém um access_token com Client ID e Secret, cria uma "order", redireciona o usuário e recebe o resultado por webhook. Instale o SDK oficial via Composer:
composer require paypal/paypal-server-sdk
Para criar a ordem no backend (exemplo simplificado com chamada direta):
<?php
// obter token OAuth2
$ch = curl_init('https://api-m.paypal.com/v1/oauth2/token');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => $clientId . ':' . $secret,
CURLOPT_POSTFIELDS => 'grant_type=client_credentials',
]);
$token = json_decode(curl_exec($ch))->access_token;
// criar a ordem de pagamento
$payload = [
'intent' => 'CAPTURE',
'purchase_units' => [[
'reference_id' => $orderId, // seu order_id interno
'amount' => ['currency_code' => 'BRL', 'value' => '20.00'],
]],
];
O ponto crítico é o webhook. No painel do PayPal Developer você registra a URL https://seusite.com/webhook/paypal.php e assina o evento PAYMENT.CAPTURE.COMPLETED. Ao receber a notificação, você deve verificar a assinatura chamando o endpoint /v1/notifications/verify-webhook-signature, comparando os headers PAYPAL-TRANSMISSION-ID, PAYPAL-TRANSMISSION-SIG e o webhook_id. Só depois disso confie no conteúdo.
<?php
$body = file_get_contents('php://input');
$event = json_decode($body, true);
if ($event['event_type'] === 'PAYMENT.CAPTURE.COMPLETED') {
$txn = $event['resource']['id'];
$value = $event['resource']['amount']['value'];
$curr = $event['resource']['amount']['currency_code'];
$ref = $event['resource']['custom_id'] ?? null;
// 1. verificar assinatura via API (obrigatório!)
// 2. conferir valor e moeda contra o pedido salvo
// 3. creditar de forma idempotente
if ($curr === 'BRL' && verificarAssinaturaPayPal($body)) {
creditarDoacao($ref, $txn, (float)$value);
}
http_response_code(200);
}
Nunca credite com base no retorno do navegador (return_url). Aquele redirecionamento serve só para exibir "Obrigado!" ao jogador. A verdade financeira mora no webhook verificado.
Integração com PIX via gateway
O PIX puro (chave estática) não notifica seu servidor automaticamente — por isso usamos a cobrança dinâmica de um gateway (padrão PIX Cob), que gera um QR Code por pedido e dispara webhook quando o pagamento cai. O fluxo genérico é: autentica na API, cria uma cobrança com txid e valor, recebe o qrcode (payload copia-e-cola) e o imagemQrcode (base64), exibe ao usuário, e aguarda o webhook.
<?php
// exemplo genérico de criação de cobrança PIX (varia por gateway)
$cobranca = [
'calendario' => ['expiracao' => 3600],
'valor' => ['original' => '20.00'],
'chave' => '[email protected]',
'solicitacaoPagador' => "Doacao pedido #{$orderId}",
];
// POST autenticado -> retorna { txid, pixCopiaECola, location }
Quando o pagador conclui, o gateway chama seu webhook (ex.: https://seusite.com/webhook/pix.php) com o txid e o status CONCLUIDA. A validação aqui costuma ser feita por mTLS (certificado do gateway) ou por um token secreto acordado — confirme na documentação do seu provedor. Assim como no PayPal, confira valor, marque como pago e credite:
<?php
$dados = json_decode(file_get_contents('php://input'), true);
foreach ($dados['pix'] ?? [] as $pix) {
if (($pix['status'] ?? '') === 'CONCLUIDA') {
creditarPorTxid($pix['txid'], (float)$pix['valor']);
}
}
http_response_code(200);
Creditação atômica no banco do jogo
Esta é a etapa mais sensível. A recompensa (WCoin, VIP) vai para o banco do jogo (SQL Server), não para o do site. A função de creditação precisa ser idempotente e transacional: se rodar duas vezes para a mesma transação, credita uma só vez.
<?php
function creditarDoacao(string $orderId, string $txn, float $valor): void
{
global $siteDb, $gameDb;
// trava idempotente: só prossegue se ainda não foi creditado
$ok = $siteDb->prepare(
"UPDATE donations SET status='paid', provider_txn=?, paid_at=NOW()
WHERE id=? AND status='pending'"
);
$ok->execute([$txn, $orderId]);
if ($ok->rowCount() === 0) return; // já processado
$row = /* buscar wcoin, vip_days, account do pedido */;
// aplicar no game DB (nomes de tabela/coluna VARIAM POR VERSÃO)
$gameDb->beginTransaction();
$gameDb->prepare(
"UPDATE MEMB_INFO SET WCoin = WCoin + ? WHERE memb___id = ?"
)->execute([$row['wcoin'], $row['account']]);
// adicionar dias de VIP conforme o sistema da sua distribuição
$gameDb->commit();
$siteDb->prepare("UPDATE donations SET status='credited' WHERE id=?")
->execute([$orderId]);
}
O WHERE ... status='pending' combinado com rowCount() é o coração da idempotência: se dois webhooks chegarem (o PayPal reenvia em caso de timeout), apenas o primeiro muda a linha; o segundo encontra zero linhas afetadas e sai sem creditar de novo. A coluna WCoin e a tabela MEMB_INFO são exemplos típicos de Season 6 — na sua versão pode ser Cash, zen, uma tabela Cash_Shop própria, ou um sistema de VIP em tabela separada. Confirme sempre.
Se você quiser desacoplar ainda mais, o webhook apenas marca paid e um cron roda a cada minuto processando pedidos paid que ainda não foram credited. Isso torna o sistema resiliente a quedas do banco do jogo.
Página de doação e experiência do jogador
No front-end, apresente os pacotes de forma clara e ofereça os dois métodos. Um exemplo de estrutura de dados dos pacotes:
const pacotes = [
{ id: 1, label: "Iniciante", brl: 10, wcoin: 2000, vip: 7 },
{ id: 2, label: "Guerreiro", brl: 20, wcoin: 5000, vip: 30 },
{ id: 3, label: "Lorde", brl: 50, wcoin: 15000, vip: 90 },
];
Ao clicar, o front chama seu backend, que cria o pedido pending e devolve a URL do PayPal ou o QR Code PIX. Mostre um contador de expiração do PIX e um botão "Já paguei / Verificar" que consulta o status do pedido (sem creditar — só lê o status). Se você monta seu servidor do zero, vale revisar todo o restante da estrutura no guia como montar um servidor de MU Online antes de plugar o módulo de doações.
Erros comuns e soluções
| Sintoma | Causa provável | Solução |
|---|---|---|
| Doação paga mas não creditou | Webhook não configurado ou URL sem HTTPS válido | Registre a URL no painel do provedor e teste com certificado válido |
| WCoin creditado em dobro | Falta de idempotência; webhook reenviado | Use UNIQUE(provider_txn) e UPDATE ... WHERE status='pending' |
| Creditou valor errado | Confiou no valor enviado pelo navegador | Sempre reconcilie contra o pedido salvo no banco |
| Chargeback após crédito | Conta nova + PayPal | Logue IP/conta, entregue como bem virtual, monitore contas recentes |
| Webhook retorna 500 e provedor reenvia infinito | Exceção não tratada antes do http_response_code(200) | Envolva em try/catch e responda 200 mesmo em erro de negócio, logando à parte |
| PIX não confirma | Validação de assinatura/mTLS falhando | Confirme certificado do gateway e o token secreto do webhook |
Segurança e conformidade
- Valide sempre a origem do webhook. No PayPal, use o endpoint de verificação de assinatura; no PIX, mTLS ou token secreto.
- Registre logs de cada webhook recebido (payload cru + IP) por pelo menos alguns meses, para auditoria e disputa de chargeback.
- Limite tentativas e aplique rate limiting no endpoint de criação de pedidos, evitando abuso.
- Não exponha Client Secret em JavaScript; toda comunicação sensível fica no backend.
- Termos claros: deixe explícito que doações são para manutenção do servidor e que itens virtuais não têm valor monetário reembolsável, reduzindo disputas.
- Fiscal: ao profissionalizar, considere abrir MEI/empresa e usar gateway que emita comprovantes. Receita recorrente é tributável.
Checklist de lançamento
- HTTPS válido no domínio e nos endpoints de webhook
- Tabela
donationscriada comUNIQUE(provider_txn) - Credenciais de API em
.envfora da raiz pública - Webhook do PayPal registrado e assinatura verificada em código
- Webhook do gateway PIX registrado com validação (mTLS/token)
- Função de creditação idempotente e transacional testada
- Nomes reais de tabela/coluna do game DB confirmados para sua versão
- Cron de reprocessamento de pedidos
paidnãocreditedativo - Logs de webhook e IP habilitados
- Teste ponta a ponta em sandbox (PayPal Sandbox + PIX de teste)
- Termos de doação publicados no site
Com esse fluxo você elimina o crédito manual, reduz fraudes e entrega uma experiência instantânea ao jogador — que doa, paga no PIX em segundos e vê o VIP no personagem sem precisar abrir ticket. Comece sempre em ambiente de testes, valide a idempotência com pagamentos reais de valor baixo e só então libere para toda a comunidade.
Perguntas frequentes
As doações no MU Online são legais no Brasil?
Sim, desde que ofertadas como doação voluntária ou venda de itens virtuais cosméticos. Recomenda-se transparência nos termos, emissão de comprovantes e atenção às regras fiscais aplicáveis a receitas recorrentes.
Preciso de CNPJ para receber PIX de doações?
Não é obrigatório para valores baixos e esporádicos com chave de pessoa física, mas ao crescer o volume o ideal é abrir MEI ou empresa para regularizar a receita e usar gateways que exigem conta empresarial.
O PayPal funciona bem para o público brasileiro?
Funciona, mas muitos jogadores brasileiros preferem PIX pela instantaneidade e ausência de tarifas altas. O ideal é oferecer os dois métodos lado a lado no mesmo painel de doação.
Como evitar fraudes de chargeback no PayPal?
Registre logs de IP e conta, entregue itens marcados como bens virtuais, mantenha os termos claros e monitore doações repetidas de contas novas. Chargebacks são comuns em servidores e devem ser previstos.
É seguro creditar automaticamente sem revisão humana?
Sim, se você validar a assinatura do webhook, conferir o valor e a moeda, e usar uma tabela de idempotência para não creditar a mesma transação duas vezes. Nunca confie apenas em parâmetros de URL de retorno.