html5网页游戏充值提现源码
(图片来源网络,侵删)
  1. 安全第一: 以下代码是前端演示源码,包含了核心逻辑和与后端交互的模拟。真实的资金交易(提现)逻辑必须在后端服务器上实现,并且必须进行严格的安全验证,否则极易造成资金损失。
  2. 仅用于学习: 此代码旨在帮助你理解充值提现功能的实现流程,切勿直接用于生产环境,你需要根据你的实际后端API和安全要求进行修改和加固。
  3. 后端依赖: 这个例子包含一个使用Node.js + Express + MySQL模拟的后端,用于演示API调用,你需要一个真实的服务器环境来运行它。

项目结构

整个项目分为两部分:

  1. 前端 (Client): 玩家在浏览器中看到的游戏界面和充值提现弹窗。
  2. 后端 (Server): 模拟处理业务逻辑、数据库交互和API请求。
game-payment-demo/
├── client/                  # 前端代码
│   ├── index.html          # 主游戏页面
│   ├── style.css           # 样式文件
│   └── script.js           # 前端JavaScript逻辑
└── server/                  # 后端代码 (Node.js)
    ├── package.json        # 项目依赖
    ├── server.js           # Express服务器主文件
    └── database.sql        # MySQL数据库初始化脚本

第一步:后端模拟

后端负责提供API接口,模拟数据库操作,我们使用Node.js, Express, 和 mysql2 库。

初始化后端项目

server 目录下打开终端,执行:

npm init -y
npm install express mysql2 cors body-parser

创建数据库脚本 (server/database.sql)

-- 创建数据库
CREATE DATABASE IF NOT EXISTS game_db;
-- 使用数据库
USE game_db;
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    balance DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
    password VARCHAR(255) NOT NULL -- 实际应用中应存储哈希后的密码
);
-- 插入一个测试用户
INSERT INTO users (username, password, balance) VALUES ('player1', 'password123', 100.00) ON DUPLICATE KEY UPDATE username=username;

创建后端服务器 (server/server.js)

const express = require('express');
const mysql = require('mysql2/promise');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
const PORT = 3000;
// 中间件
app.use(cors());
app.use(bodyParser.json());
// 创建数据库连接池
const pool = mysql.createPool({
    host: 'localhost', // 或你的数据库地址
    user: 'root',      // 你的数据库用户名
    password: 'password', // 你的数据库密码
    database: 'game_db',
    waitForConnections: true,
    connectionLimit: 10,
    queueLimit: 0
});
// --- API 路由 ---
// 获取用户余额
app.get('/api/balance/:userId', async (req, res) => {
    const userId = req.params.userId;
    try {
        const [rows] = await pool.query('SELECT balance FROM users WHERE id = ?', [userId]);
        if (rows.length > 0) {
            res.json({ success: true, balance: parseFloat(rows[0].balance) });
        } else {
            res.status(404).json({ success: false, message: '用户不存在' });
        }
    } catch (error) {
        console.error('获取余额失败:', error);
        res.status(500).json({ success: false, message: '服务器错误' });
    }
});
// 充值
app.post('/api/recharge', async (req, res) => {
    const { userId, amount } = req.body;
    if (!userId || !amount || amount <= 0) {
        return res.status(400).json({ success: false, message: '参数错误' });
    }
    try {
        // 开始事务
        const connection = await pool.getConnection();
        await connection.beginTransaction();
        // 1. 增加用户余额
        await connection.query('UPDATE users SET balance = balance + ? WHERE id = ?', [amount, userId]);
        // 2. (可选) 记录充值订单到orders表
        // await connection.query('INSERT INTO orders (user_id, type, amount, status) VALUES (?, "recharge", ?, "success")', [userId, amount]);
        await connection.commit();
        connection.release();
        res.json({ success: true, message: '充值成功!' });
    } catch (error) {
        console.error('充值失败:', error);
        if (connection) connection.rollback();
        res.status(500).json({ success: false, message: '充值失败,服务器错误' });
    }
});
// 提现
app.post('/api/withdraw', async (req, res) => {
    const { userId, amount, accountNumber } = req.body;
    if (!userId || !amount || !accountNumber || amount <= 0) {
        return res.status(400).json({ success: false, message: '参数错误' });
    }
    try {
        const connection = await pool.getConnection();
        await connection.beginTransaction();
        // 1. 检查用户余额是否充足
        const [rows] = await connection.query('SELECT balance FROM users WHERE id = ? FOR UPDATE', [userId]);
        if (rows.length === 0) {
            connection.release();
            return res.status(404).json({ success: false, message: '用户不存在' });
        }
        const userBalance = parseFloat(rows[0].balance);
        if (userBalance < amount) {
            connection.release();
            return res.status(400).json({ success: false, message: '余额不足' });
        }
        // 2. 扣除用户余额
        await connection.query('UPDATE users SET balance = balance - ? WHERE id = ?', [amount, userId]);
        // 3. (可选) 记录提现订单到orders表,状态为“处理中”
        // await connection.query('INSERT INTO orders (user_id, type, amount, account_number, status) VALUES (?, "withdraw", ?, ?, "pending")', [userId, amount, accountNumber]);
        await connection.commit();
        connection.release();
        // !!! 安全提示:这里只是模拟!真实场景下,提现请求应进入一个异步处理队列,
        // 由风控系统审核,然后通过第三方支付平台(如支付宝、微信)的API打款。
        // 打款成功后,才将订单状态更新为“成功”。
        // 直接在前端显示“提现成功”是极其危险的!
        res.json({ success: true, message: '提现申请已提交,请等待审核!' });
    } catch (error) {
        console.error('提现失败:', error);
        if (connection) connection.rollback();
        res.status(500).json({ success: false, message: '提现失败,服务器错误' });
    }
});
app.listen(PORT, () => {
    console.log(`服务器运行在 http://localhost:${PORT}`);
});

运行后端

确保你的MySQL服务已启动,并已执行 database.sql 脚本,然后在 server 目录下运行:

html5网页游戏充值提现源码
(图片来源网络,侵删)
node server.js

第二步:前端实现

前端是玩家交互的界面,我们将创建一个简单的游戏界面,并包含充值和提现的弹窗。

HTML (client/index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">游戏充值提现演示</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="game-container">
        <h1>我的游戏世界</h1>
        <div class="info-panel">
            <div>玩家ID: <span id="userId">1</span></div>
            <div>我的金币: <span id="userBalance">100</span></div>
        </div>
        <div class="actions">
            <button id="rechargeBtn">充值</button>
            <button id="withdrawBtn">提现</button>
        </div>
    </div>
    <!-- 充值弹窗 -->
    <div id="rechargeModal" class="modal">
        <div class="modal-content">
            <span class="close">&times;</span>
            <h2>充值中心</h2>
            <form id="rechargeForm">
                <div class="form-group">
                    <label for="rechargeAmount">充值金额:</label>
                    <input type="number" id="rechargeAmount" name="amount" min="1" step="0.01" required>
                </div>
                <button type="submit">确认充值</button>
            </form>
        </div>
    </div>
    <!-- 提现弹窗 -->
    <div id="withdrawModal" class="modal">
        <div class="modal-content">
            <span class="close">&times;</span>
            <h2>提现中心</h2>
            <form id="withdrawForm">
                <div class="form-group">
                    <label for="withdrawAmount">提现金额:</label>
                    <input type="number" id="withdrawAmount" name="amount" min="1" step="0.01" required>
                </div>
                <div class="form-group">
                    <label for="accountNumber">收款账号:</label>
                    <input type="text" id="accountNumber" name="accountNumber" placeholder="请输入支付宝或微信账号" required>
                </div>
                <button type="submit">申请提现</button>
            </form>
        </div>
    </div>
    <!-- 消息提示 -->
    <div id="messageBox" class="message-box"></div>
    <script src="script.js"></script>
</body>
</html>

CSS (client/style.css)

body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f2f5;
}
.game-container {
    background: white;
    padding: 2rem;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    text-align: center;
}
.info-panel {
    margin: 1.5rem 0;
    font-size: 1.2rem;
}
.info-panel span {
    font-weight: bold;
    color: #007bff;
}
.actions button {
    padding: 0.8rem 1.5rem;
    margin: 0 0.5rem;
    font-size: 1rem;
    cursor: pointer;
    border: none;
    border-radius: 5px;
    transition: background-color 0.3s;
}
#rechargeBtn {
    background-color: #28a745;
    color: white;
}
#withdrawBtn {
    background-color: #dc3545;
    color: white;
}
.actions button:hover {
    opacity: 0.9;
}
/* 弹窗样式 */
.modal {
    display: none;
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
    background-color: #fefefe;
    margin: 10% auto;
    padding: 2rem;
    border: 1px solid #888;
    width: 80%;
    max-width: 400px;
    border-radius: 8px;
    position: relative;
}
.close {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
    position: absolute;
    right: 1rem;
    top: 1rem;
}
.close:hover,
.close:focus {
    color: black;
}
.form-group {
    margin-bottom: 1rem;
    text-align: left;
}
.form-group label {
    display: block;
    margin-bottom: 0.5rem;
}
.form-group input {
    width: 100%;
    padding: 0.5rem;
    box-sizing: border-box;
    border: 1px solid #ccc;
    border-radius: 4px;
}
.form-group button {
    width: 100%;
    padding: 0.8rem;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 5px;
    font-size: 1rem;
    cursor: pointer;
}
.form-group button:hover {
    background-color: #0056b3;
}
/* 消息提示样式 */
.message-box {
    position: fixed;
    top: 20px;
    right: 20px;
    padding: 1rem;
    background-color: #333;
    color: white;
    border-radius: 5px;
    display: none;
    z-index: 1000;
}

JavaScript (client/script.js)

document.addEventListener('DOMContentLoaded', () => {
    const userId = 1; // 模拟当前登录的用户ID
    let userBalance = 100; // 初始余额
    // DOM 元素
    const userBalanceEl = document.getElementById('userBalance');
    const rechargeBtn = document.getElementById('rechargeBtn');
    const withdrawBtn = document.getElementById('withdrawBtn');
    const rechargeModal = document.getElementById('rechargeModal');
    const withdrawModal = document.getElementById('withdrawModal');
    const rechargeForm = document.getElementById('rechargeForm');
    const withdrawForm = document.getElementById('withdrawForm');
    const closeBtns = document.querySelectorAll('.close');
    const messageBox = document.getElementById('messageBox');
    const API_BASE_URL = 'http://localhost:3000/api';
    // 显示消息
    function showMessage(message, isSuccess = true) {
        messageBox.textContent = message;
        messageBox.style.backgroundColor = isSuccess ? '#28a745' : '#dc3545';
        messageBox.style.display = 'block';
        setTimeout(() => {
            messageBox.style.display = 'none';
        }, 3000);
    }
    // 更新余额显示
    async function updateBalance() {
        try {
            const response = await fetch(`${API_BASE_URL}/balance/${userId}`);
            const data = await response.json();
            if (data.success) {
                userBalance = data.balance;
                userBalanceEl.textContent = userBalance.toFixed(2);
            }
        } catch (error) {
            console.error('获取余额失败:', error);
            showMessage('获取余额失败', false);
        }
    }
    // 初始化余额
    updateBalance();
    // 打开弹窗
    rechargeBtn.onclick = () => {
        rechargeModal.style.display = 'block';
        document.getElementById('rechargeAmount').value = ''; // 清空上次输入
    };
    withdrawBtn.onclick = () => {
        withdrawModal.style.display = 'block';
        document.getElementById('withdrawAmount').value = ''; // 清空上次输入
        document.getElementById('accountNumber').value = '';
    };
    // 关闭弹窗
    closeBtns.forEach(btn => {
        btn.onclick = () => {
            btn.closest('.modal').style.display = 'none';
        };
    });
    // 点击弹窗外部关闭
    window.onclick = (event) => {
        if (event.target.classList.contains('modal')) {
            event.target.style.display = 'none';
        }
    };
    // 处理充值表单提交
    rechargeForm.onsubmit = async (e) => {
        e.preventDefault();
        const amount = parseFloat(e.target.amount.value);
        if (amount <= 0) {
            showMessage('充值金额必须大于0', false);
            return;
        }
        try {
            const response = await fetch(`${API_BASE_URL}/recharge`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ userId, amount })
            });
            const data = await response.json();
            if (data.success) {
                showMessage(data.message);
                rechargeModal.style.display = 'none';
                updateBalance(); // 刷新余额
            } else {
                showMessage(data.message || '充值失败', false);
            }
        } catch (error) {
            console.error('充值请求失败:', error);
            showMessage('充值失败,请检查网络', false);
        }
    };
    // 处理提现表单提交
    withdrawForm.onsubmit = async (e) => {
        e.preventDefault();
        const amount = parseFloat(e.target.amount.value);
        const accountNumber = e.target.accountNumber.value;
        if (amount <= 0) {
            showMessage('提现金额必须大于0', false);
            return;
        }
        try {
            const response = await fetch(`${API_BASE_URL}/withdraw`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ userId, amount, accountNumber })
            });
            const data = await response.json();
            if (data.success) {
                showMessage(data.message);
                withdrawModal.style.display = 'none';
                updateBalance(); // 刷新余额
            } else {
                showMessage(data.message || '提现失败', false);
            }
        } catch (error) {
            console.error('提现请求失败:', error);
            showMessage('提现失败,请检查网络', false);
        }
    };
});

第三步:运行和测试

  1. 启动后端:在 server 目录下,确保MySQL服务运行,然后执行 node server.js
  2. 打开前端:用浏览器打开 client/index.html 文件。
  3. 测试流程
    • 查看初始金币数。
    • 点击“充值”,输入一个金额(如50),点击确认,页面会提示“充值成功!”,并刷新金币数。
    • 点击“提现”,输入一个金额(如30)和一个收款账号,点击申请,页面会提示“提现申请已提交...”,并刷新金币数。
    • 尝试提现超过余额的金额,会收到“余额不足”的提示。

总结与安全加固要点

这个演示版本完整地展示了充值提现的前后端交互流程,但在实际开发中,你必须注意以下几点:

  1. 用户认证:前端需要登录机制(如JWT Token),每次API请求都要在Header中携带Token,后端进行验证。
  2. HTTPS:所有API通信必须使用HTTPS,防止数据在传输过程中被窃听或篡改。
  3. 后端核心逻辑
    • 提现是异步的:前端提交提现申请后,后端应生成一个提现任务,放入队列,一个独立的后台服务(如使用RabbitMQ, Redis队列)负责处理这些任务。
    • 风控系统:提现请求必须经过严格的风控审核(如检测异常大额、高频提现、设备指纹等)。
    • 第三方支付集成:提现最终是通过调用支付宝、微信支付等官方API来实现的,而不是直接操作你自己的数据库,只有当第三方支付接口返回“打款成功”后,才将订单状态更新为“已完成”。
    • 数据库事务:像代码中展示的那样,涉及资金增减的操作必须使用数据库事务,保证原子性,防止出现数据不一致。
  4. 输入验证:后端必须对所有前端传来的数据进行严格校验,防止SQL注入、XSS攻击等。
  5. 日志记录:所有充值、提现操作都必须有详细的日志,包括操作人、时间、金额、IP地址等,便于追溯和审计。
html5网页游戏充值提现源码
(图片来源网络,侵删)