O maior portal de MU Online do Brasil — desde 2003
Tutorial Intermediário Servidor

Como configurar o Cash Shop (loja virtual) do servidor de MU

Configure o Cash Shop no servidor de MU Online — WCoin shop do GameServer, loja integrada ao site, sistema de créditos, tabelas SQL, entrega automática de itens e gateway de pagamento.

EQ Equipe ViciadosMU · Atualizado em 1 jul 2026 · ⏱ 20 min de leitura
Resposta rápida

O Cash Shop (loja virtual) é a principal fonte de receita de servidores privados de MU Online. A implementação mais comum usa um sistema web com créditos (WCoins, Cash Points ou similar) que são comprados por doação e usados para adquirir itens diretamente via banco de dados.

O Cash Shop (loja virtual) é a principal fonte de receita de servidores privados de MU Online. A implementação mais comum usa um sistema web com créditos (WCoins, Cash Points ou similar) que são comprados por doação e usados para adquirir itens diretamente via banco de dados.

Arquitetura do sistema

JOGADOR → doação → gateway pagamento → webhook → script PHP
                                                      │
                                              creditar WCoins
                                                      │
                                              MEMB_CREDITS (banco)
                                                      │
                                     jogador compra na loja do site
                                                      │
                                              entregar item via SQL
                                                      │
                                             inventário do personagem

Tabela de créditos (WCoins)

-- Tabela de créditos WCoin (Season 8+ tem MEMB_CREDITS nativa)
-- Para versões mais antigas, criar:

CREATE TABLE MEMB_CREDITS (
    memb_id   VARCHAR(10) NOT NULL PRIMARY KEY,
    WCoin_C   INT NOT NULL DEFAULT 0,          -- WCoins de compra (C=Cash)
    WCoin_P   INT NOT NULL DEFAULT 0,          -- WCoins de evento/presente (P=Point)
    WCoin_G   INT NOT NULL DEFAULT 0,          -- WCoins de giftbox
    CONSTRAINT FK_Credits_Memb FOREIGN KEY (memb_id) REFERENCES MEMB_INFO(memb_id)
);
GO

-- Inicializar para todas as contas existentes
INSERT INTO MEMB_CREDITS (memb_id)
SELECT memb_id FROM MEMB_INFO
WHERE memb_id NOT IN (SELECT memb_id FROM MEMB_CREDITS);
GO

-- Ver créditos de uma conta
SELECT m.memb_id, c.WCoin_C, c.WCoin_P
FROM MEMB_INFO m
JOIN MEMB_CREDITS c ON c.memb_id = m.memb_id
WHERE m.memb_id = 'minhaConta';

Tabela de itens da loja

-- Tabela de produtos da loja (MySQL — banco do site)
CREATE TABLE shop_items (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(100) NOT NULL,
    description TEXT,
    item_group  TINYINT NOT NULL,      -- grupo do item no MU
    item_index  SMALLINT NOT NULL,     -- ID do item no MU
    item_level  TINYINT NOT NULL DEFAULT 0,
    item_option TINYINT NOT NULL DEFAULT 0,  -- luck, exc, add
    item_exc    TINYINT NOT NULL DEFAULT 0,  -- opções excellent
    price       INT NOT NULL,           -- preço em WCoins
    quantity    INT NOT NULL DEFAULT 1, -- quantos itens por compra
    image       VARCHAR(255),
    active      TINYINT(1) NOT NULL DEFAULT 1,
    category    VARCHAR(50) NOT NULL DEFAULT 'geral',
    sort_order  INT NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Inserir alguns produtos de exemplo
INSERT INTO shop_items (name, description, item_group, item_index, item_level, item_option, price, category) VALUES
('Jewel of Bless (x10)',   '10 Joias de Bless',     14, 13, 0, 0, 1000,  'joias'),
('Jewel of Soul (x10)',    '10 Joias de Soul',      14, 14, 0, 0, 1000,  'joias'),
('Jewel of Life (x10)',    '10 Joias de Life',      14, 31, 0, 0, 2000,  'joias'),
('Jewel of Guardian (x3)','3 Joias de Guardian',   14, 22, 0, 0, 5000,  'joias'),
('Wings of Elf +9',        'Asas de Elfa nível +9', 12, 0,  9, 4, 10000, 'asas'),
('Wings of Heaven +9',     'Asas do Céu nível +9',  12, 1,  9, 4, 10000, 'asas'),
('VIP Gold 30 dias',       'VIP Ouro por 30 dias',  -1, -1, 0, 0, 15000, 'vip'),
('Reset Ticket',           '1 reset grátis',        -1, -2, 0, 0, 5000,  'especial');

Tabela de transações

-- Tabela de compras na loja (MySQL)
CREATE TABLE shop_transactions (
    id           INT AUTO_INCREMENT PRIMARY KEY,
    account_id   VARCHAR(10) NOT NULL,
    char_name    VARCHAR(16) NOT NULL,
    item_id      INT NOT NULL,
    quantity     INT NOT NULL DEFAULT 1,
    cost         INT NOT NULL,
    status       ENUM('pending','delivered','failed') DEFAULT 'pending',
    created_at   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    delivered_at DATETIME NULL,
    FOREIGN KEY (item_id) REFERENCES shop_items(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Tabela de recargas de WCoins (MySQL)
CREATE TABLE wcoin_recharges (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    account_id  VARCHAR(10) NOT NULL,
    amount      DECIMAL(10,2) NOT NULL,     -- valor em R$
    wcoin_value INT NOT NULL,               -- WCoins creditados
    payment_id  VARCHAR(100),               -- ID da transação no gateway
    status      ENUM('pending','completed','failed','refunded') DEFAULT 'pending',
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    confirmed_at DATETIME NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

PHP — loja de itens no site

<?php
// shop.php — loja virtual integrada

require 'config/db-muserver.php';
require 'config/db-site.php';   // conexão MySQL do site
session_start();

$conta = $_SESSION['conta_mu'] ?? null;
if (!$conta) { header('Location: /login'); exit; }

$muConn  = getMuDB();        // SQL Server
$siteDb  = getSiteDB();      // MySQL PDO

// Buscar saldo de WCoins
$wcoinStmt = sqlsrv_query($muConn,
    "SELECT WCoin_C FROM MEMB_CREDITS WHERE memb_id = ?",
    [[$conta, SQLSRV_PARAM_IN]]
);
$wcoin = sqlsrv_fetch_array($wcoinStmt, SQLSRV_FETCH_ASSOC);
$saldo = $wcoin ? $wcoin['WCoin_C'] : 0;

// Buscar personagens da conta (para saber qual receberá o item)
$chars = [];
$charStmt = sqlsrv_query($muConn,
    "SELECT Name FROM Character WHERE AccountID = ? AND CtlCode = 0 ORDER BY cLevel DESC",
    [[$conta, SQLSRV_PARAM_IN]]
);
while ($row = sqlsrv_fetch_array($charStmt, SQLSRV_FETCH_ASSOC)) {
    $chars[] = $row['Name'];
}

// Categorias de produtos
$categoria = $_GET['cat'] ?? 'joias';
$produtos = $siteDb->prepare("SELECT * FROM shop_items WHERE active = 1 AND category = ? ORDER BY sort_order, price");
$produtos->execute([$categoria]);
$produtosLista = $produtos->fetchAll();

// Processar compra
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['buy_item'])) {
    $itemId   = (int)$_POST['item_id'];
    $charName = $_POST['char_name'];
    
    // Verificar que o personagem pertence à conta
    if (!in_array($charName, $chars)) {
        $erro = "Personagem inválido";
    } else {
        // Buscar produto
        $produto = $siteDb->prepare("SELECT * FROM shop_items WHERE id = ? AND active = 1");
        $produto->execute([$itemId]);
        $item = $produto->fetch();
        
        if (!$item) {
            $erro = "Produto não encontrado";
        } elseif ($saldo < $item['price']) {
            $erro = "Saldo insuficiente. Você tem $saldo WCoins, o item custa {$item['price']}.";
        } else {
            // Verificar personagem offline
            $offlineStmt = sqlsrv_query($muConn,
                "SELECT ConnectStat FROM Character WHERE Name = ? AND AccountID = ?",
                [[$charName, SQLSRV_PARAM_IN], [$conta, SQLSRV_PARAM_IN]]
            );
            $charStatus = sqlsrv_fetch_array($offlineStmt, SQLSRV_FETCH_ASSOC);
            
            if ($charStatus['ConnectStat'] == 1) {
                $erro = "O personagem precisa estar OFFLINE para receber itens.";
            } else {
                // Debitar WCoins
                sqlsrv_query($muConn,
                    "UPDATE MEMB_CREDITS SET WCoin_C = WCoin_C - ? WHERE memb_id = ?",
                    [[$item['price'], SQLSRV_PARAM_IN], [$conta, SQLSRV_PARAM_IN]]
                );
                
                // Registrar transação
                $siteDb->prepare("
                    INSERT INTO shop_transactions (account_id, char_name, item_id, quantity, cost, status)
                    VALUES (?, ?, ?, 1, ?, 'pending')
                ")->execute([$conta, $charName, $itemId, $item['price']]);
                
                $txId = $siteDb->lastInsertId();
                
                // Entregar item
                if (entregarItem($muConn, $charName, $item)) {
                    $siteDb->prepare("UPDATE shop_transactions SET status='delivered', delivered_at=NOW() WHERE id=?")->execute([$txId]);
                    $sucesso = "Item '{$item['name']}' entregue a $charName!";
                    $saldo -= $item['price'];
                } else {
                    // Estornar WCoins se entrega falhou
                    sqlsrv_query($muConn,
                        "UPDATE MEMB_CREDITS SET WCoin_C = WCoin_C + ? WHERE memb_id = ?",
                        [[$item['price'], SQLSRV_PARAM_IN], [$conta, SQLSRV_PARAM_IN]]
                    );
                    $siteDb->prepare("UPDATE shop_transactions SET status='failed' WHERE id=?")->execute([$txId]);
                    $erro = "Erro ao entregar o item. WCoins estornados.";
                }
            }
        }
    }
}

// Função de entrega de item no inventário
function entregarItem($conn, string $charName, array $item): bool {
    if ($item['item_group'] < 0) {
        // Itens especiais (VIP, Reset) — tratamento diferente
        return entregarEspecial($conn, $charName, $item);
    }
    
    // Encontrar slot livre no inventário
    // Esta implementação depende da estrutura da tabela de inventário
    // Na maioria das distribuições, a tabela Character tem a coluna Inventory (binary)
    // Para inserção simples, use a tabela Character_Items (se existir) ou o storage
    
    // Inserção no Personal Store/Vault como alternativa mais simples:
    $stmt = sqlsrv_query($conn, "
        INSERT INTO Character_Items (AccountID, Inventory, wPos, ItemGUID, ItemGroup, ItemIndex, ItemLevel, ...)
        -- implementação depende da distribuição
    ");
    
    return $stmt !== false;
}
Nota: A entrega de itens diretamente no inventário do personagem requer conhecimento da estrutura binária do campo Inventory da tabela Character — que varia por versão do MuServer. A alternativa mais prática é usar um sistema de "inbox" (caixa de entrada do jogo) ou pedir para o jogador estar no warehouse/baú para receber.

Recarregar WCoins via Pix (integração com Mercado Pago)

<?php
// webhook-mp.php — receber notificações do Mercado Pago

// Verificar assinatura do webhook (segurança)
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

// Validar assinatura com sua chave secreta do MP
// (implementação específica do SDK do Mercado Pago)

$data = json_decode($payload, true);

if ($data['type'] === 'payment' && $data['action'] === 'payment.updated') {
    $paymentId = $data['data']['id'];
    
    // Buscar detalhes do pagamento via API do MP
    // $payment = $mp->get("/v1/payments/$paymentId");
    
    // Se aprovado, creditar WCoins
    if ($payment['status'] === 'approved') {
        $externalRef = $payment['external_reference'];  // = "conta|plano" que você definiu
        [$conta, $planoId] = explode('|', $externalRef);
        
        // Tabela de planos de recarga
        $planos = [
            1 => ['wcoin' => 5000,  'preco' => 15.00],
            2 => ['wcoin' => 12000, 'preco' => 30.00],
            3 => ['wcoin' => 25000, 'preco' => 60.00],
            4 => ['wcoin' => 60000, 'preco' => 120.00],
        ];
        
        if (isset($planos[$planoId])) {
            $wcoin = $planos[$planoId]['wcoin'];
            
            // Creditar no SQL Server
            $conn = getMuDB();
            sqlsrv_query($conn,
                "UPDATE MEMB_CREDITS SET WCoin_C = WCoin_C + ? WHERE memb_id = ?",
                [[$wcoin, SQLSRV_PARAM_IN], [$conta, SQLSRV_PARAM_IN]]
            );
            
            // Registrar no banco do site
            getSiteDB()->prepare("
                UPDATE wcoin_recharges 
                SET status='completed', confirmed_at=NOW()
                WHERE payment_id = ?
            ")->execute([$paymentId]);
        }
    }
}

http_response_code(200);
echo "OK";

Checklist da Cash Shop

BANCO DE DADOS
[ ] Tabela MEMB_CREDITS criada e populada
[ ] Tabela shop_items com produtos cadastrados
[ ] Tabela shop_transactions para histórico
[ ] Tabela wcoin_recharges para recargas

SITE
[ ] Saldo de WCoins exibido no painel do jogador
[ ] Catálogo de produtos funcionando
[ ] Processo de compra testado (com personagem offline)
[ ] Estorno automático se entrega falhar

PAGAMENTOS
[ ] Gateway de pagamento integrado (Mercado Pago/PagSeguro/Pix)
[ ] Webhook de confirmação funcionando
[ ] WCoins creditados automaticamente após pagamento

SEGURANÇA
[ ] Verificar que personagem pertence à conta logada
[ ] Verificar saldo antes de debitar
[ ] Usar transação SQL para debit+entrega
[ ] Webhook validado com assinatura
[ ] Rate limiting nas compras

Perguntas frequentes

O Cash Shop do servidor de MU funciona igual ao oficial?

Parcialmente. O WCoin shop do GameServer (Season 8+) funciona com a tabela MEMB_CREDITS. A maioria dos servidores privados implementa uma loja no site PHP que entrega itens via banco de dados, sem usar o sistema oficial do cliente.

Como os jogadores compram na Cash Shop?

O fluxo típico: jogador faz doação via Pix/cartão → site registra o pagamento → credita WCoins/pontos na conta → jogador usa os pontos na loja do site ou no Cash Shop ingame.

É possível integrar com gateway de pagamento (Mercado Pago, PagSeguro)?

Sim — ambos oferecem APIs e webhooks. Quando o pagamento é confirmado, o webhook chama seu script PHP que credita os pontos na conta do jogador.

Cash Shop com entrega automática de itens funciona sem jogar?

Sim — a entrega pode ser feita diretamente no inventário via banco SQL (se o personagem estiver offline) ou enviada para a caixa de correio do jogo.

EQ

Equipe editorial do ViciadosMU — portal de MU Online no ar desde 2003.

Continue lendo

Artigos relacionados