PHP 虚拟货币体系

wen PHP项目 5

本文目录导读:

PHP 虚拟货币体系

  1. 系统架构设计
  2. 安全建议

我将为您设计一个PHP虚拟货币体系的完整实现方案,这个系统包含用户账户、交易、充值、提现等核心功能。

系统架构设计

数据库结构

-- 用户账户表
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password_hash` varchar(255) NOT NULL,
  `balance` decimal(20,8) NOT NULL DEFAULT '0',
  `locked_balance` decimal(20,8) NOT NULL DEFAULT '0',
  `status` tinyint(1) NOT NULL DEFAULT '1',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB;
-- 交易记录表
CREATE TABLE `transactions` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `txid` varchar(64) NOT NULL COMMENT '交易唯一标识',
  `type` enum('deposit','withdraw','transfer','reward','stake','unstake') NOT NULL,
  `amount` decimal(20,8) NOT NULL,
  `fee` decimal(20,8) NOT NULL DEFAULT '0',
  `status` enum('pending','completed','failed','cancelled') NOT NULL DEFAULT 'pending',
  `from_address` varchar(255) DEFAULT NULL,
  `to_address` varchar(255) DEFAULT NULL,
  `description` text,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `txid` (`txid`),
  KEY `user_id` (`user_id`),
  KEY `status` (`status`),
  CONSTRAINT `fk_transactions_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB;
-- 充值记录(模拟链上充值)
CREATE TABLE `deposits` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `tx_hash` varchar(64) NOT NULL,
  `amount` decimal(20,8) NOT NULL,
  `confirmations` int(11) NOT NULL DEFAULT '0',
  `status` enum('pending','confirmed','failed') NOT NULL DEFAULT 'pending',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tx_hash` (`tx_hash`)
) ENGINE=InnoDB;
-- 提现记录表
CREATE TABLE `withdrawals` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `amount` decimal(20,8) NOT NULL,
  `fee` decimal(20,8) NOT NULL DEFAULT '0',
  `withdraw_address` varchar(255) NOT NULL,
  `status` enum('pending','processing','completed','failed') NOT NULL DEFAULT 'pending',
  `tx_hash` varchar(64) DEFAULT NULL,
  `reviewed_by` int(11) DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `status` (`status`)
) ENGINE=InnoDB;
-- 转账记录表
CREATE TABLE `transfers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `from_user_id` int(11) NOT NULL,
  `to_user_id` int(11) NOT NULL,
  `amount` decimal(20,8) NOT NULL,
  `fee` decimal(20,8) NOT NULL DEFAULT '0',
  `description` text,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `from_user_id` (`from_user_id`),
  KEY `to_user_id` (`to_user_id`)
) ENGINE=InnoDB;

核心钱包类实现

<?php
// Wallet.php
namespace CurrencySystem;
use PDO;
use Exception;
class Wallet {
    private $db;
    private $user_id;
    public function __construct(PDO $db, $user_id) {
        $this->db = $db;
        $this->user_id = $user_id;
    }
    /**
     * 获取用户余额
     */
    public function getBalance() {
        $sql = "SELECT balance, locked_balance FROM users WHERE id = :user_id";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':user_id' => $this->user_id]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    /**
     * 转账功能
     */
    public function transfer($to_user_id, $amount, $description = '') {
        $this->db->beginTransaction();
        try {
            // 验证参数
            if ($amount <= 0) {
                throw new Exception('转账金额必须大于0');
            }
            if ($this->user_id == $to_user_id) {
                throw new Exception('不能给自己转账');
            }
            // 锁定用户记录
            $sql = "SELECT balance FROM users WHERE id = :user_id FOR UPDATE";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':user_id' => $this->user_id]);
            $from_balance = $stmt->fetchColumn();
            if ($from_balance < $amount) {
                throw new Exception('余额不足');
            }
            // 检查收款账号
            $sql = "SELECT id FROM users WHERE id = :user_id AND status = 1";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':user_id' => $to_user_id]);
            if (!$stmt->fetch()) {
                throw new Exception('收款用户不存在');
            }
            // 更新余额
            $this->updateBalance($this->user_id, -$amount);
            $this->updateBalance($to_user_id, $amount);
            // 记录转账
            $sql = "INSERT INTO transfers (from_user_id, to_user_id, amount, description) 
                    VALUES (:from_id, :to_id, :amount, :description)";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':from_id' => $this->user_id,
                ':to_id' => $to_user_id,
                ':amount' => $amount,
                ':description' => $description
            ]);
            // 记录交易
            $this->recordTransaction('transfer', $amount, 'completed', "Transfer to user #{$to_user_id}");
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            return $e->getMessage();
        }
    }
    /**
     * 冻结/解冻资金
     */
    public function lockBalance($amount) {
        $this->db->beginTransaction();
        try {
            $sql = "SELECT balance, locked_balance FROM users WHERE id = :user_id FOR UPDATE";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':user_id' => $this->user_id]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($user['balance'] - $user['locked_balance'] < $amount) {
                throw new Exception('可用余额不足');
            }
            $sql = "UPDATE users SET locked_balance = locked_balance + :amount WHERE id = :user_id";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':amount' => $amount,
                ':user_id' => $this->user_id
            ]);
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            return $e->getMessage();
        }
    }
    /**
     * 更新余额(内部方法)
     */
    private function updateBalance($user_id, $amount) {
        $sql = "UPDATE users SET balance = balance + :amount WHERE id = :user_id";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            ':amount' => $amount,
            ':user_id' => $user_id
        ]);
    }
    /**
     * 记录交易
     */
    private function recordTransaction($type, $amount, $status, $description = '') {
        $txid = $this->generateTxid();
        $sql = "INSERT INTO transactions (user_id, txid, type, amount, status, description) 
                VALUES (:user_id, :txid, :type, :amount, :status, :description)";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            ':user_id' => $this->user_id,
            ':txid' => $txid,
            ':type' => $type,
            ':amount' => $amount,
            ':status' => $status,
            ':description' => $description
        ]);
        return $txid;
    }
    /**
     * 生成交易ID
     */
    private function generateTxid() {
        return bin2hex(random_bytes(32));
    }
}

充值处理

<?php
// DepositService.php
namespace CurrencySystem;
use PDO;
use Exception;
class DepositService {
    private $db;
    public function __construct(PDO $db) {
        $this->db = $db;
    }
    /**
     * 创建充值请求
     */
    public function createDeposit($user_id, $amount) {
        $this->db->beginTransaction();
        try {
            // 生成模拟链上交易哈希
            $tx_hash = '0x' . bin2hex(random_bytes(32));
            $sql = "INSERT INTO deposits (user_id, tx_hash, amount) 
                    VALUES (:user_id, :tx_hash, :amount)";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':user_id' => $user_id,
                ':tx_hash' => $tx_hash,
                ':amount' => $amount
            ]);
            $deposit_id = $this->db->lastInsertId();
            $this->db->commit();
            return [
                'deposit_id' => $deposit_id,
                'tx_hash' => $tx_hash
            ];
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
    /**
     * 确认充值
     */
    public function confirmDeposit($deposit_id) {
        $this->db->beginTransaction();
        try {
            // 锁定充值记录
            $sql = "SELECT * FROM deposits WHERE id = :id FOR UPDATE";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':id' => $deposit_id]);
            $deposit = $stmt->fetch(PDO::FETCH_ASSOC);
            if (!$deposit) {
                throw new Exception('充值记录不存在');
            }
            if ($deposit['status'] == 'confirmed') {
                throw new Exception('充值已经确认');
            }
            // 更新用户余额
            $sql = "UPDATE users SET balance = balance + :amount WHERE id = :user_id";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':amount' => $deposit['amount'],
                ':user_id' => $deposit['user_id']
            ]);
            // 更新充值状态
            $sql = "UPDATE deposits SET status = 'confirmed', confirmations = 10 WHERE id = :id";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':id' => $deposit_id]);
            // 记录交易
            $wallet = new Wallet($this->db, $deposit['user_id']);
            $this->recordDepositTransaction($deposit);
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
    /**
     * 记录充值交易
     */
    private function recordDepositTransaction($deposit) {
        $wallet = new Wallet($this->db, $deposit['user_id']);
        // 使用反射调用私有方法或重新实现
        $sql = "INSERT INTO transactions (user_id, txid, type, amount, status, description) 
                VALUES (:user_id, :txid, 'deposit', :amount, 'completed', :description)";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            ':user_id' => $deposit['user_id'],
            ':txid' => $deposit['tx_hash'],
            ':amount' => $deposit['amount'],
            ':description' => 'Deposit confirmed'
        ]);
    }
}

提现处理

<?php
// WithdrawalService.php
namespace CurrencySystem;
use PDO;
use Exception;
class WithdrawalService {
    private $db;
    private $minimum_withdraw = 1; // 最小提现金额
    private $fee_rate = 0.01; // 提现费率
    public function __construct(PDO $db) {
        $this->db = $db;
    }
    /**
     * 创建提现请求
     */
    public function createWithdrawal($user_id, $amount, $address) {
        $this->db->beginTransaction();
        try {
            if ($amount < $this->minimum_withdraw) {
                throw new Exception('提现金额低于最小值');
            }
            if ($amount < 0) {
                throw new Exception('提现金额不能为0');
            }
            // 计算手续费
            $fee = round($amount * $this->fee_rate, 8);
            $net_amount = $amount - $fee;
            // 锁定用户
            $sql = "SELECT balance, locked_balance FROM users WHERE id = :user_id FOR UPDATE";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':user_id' => $user_id]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($user['balance'] - $user['locked_balance'] < $amount) {
                throw new Exception('可用余额不足');
            }
            // 扣减余额
            $sql = "UPDATE users SET balance = balance - :amount WHERE id = :user_id";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':amount' => $amount,
                ':user_id' => $user_id
            ]);
            // 创建提现记录
            $sql = "INSERT INTO withdrawals (user_id, amount, fee, withdraw_address) 
                    VALUES (:user_id, :amount, :fee, :address)";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':user_id' => $user_id,
                ':amount' => $amount,
                ':fee' => $fee,
                ':address' => $address
            ]);
            $withdrawal_id = $this->db->lastInsertId();
            $this->db->commit();
            return $withdrawal_id;
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
    /**
     * 确认提现
     */
    public function confirmWithdrawal($withdrawal_id, $tx_hash = null) {
        $this->db->beginTransaction();
        try {
            $sql = "SELECT * FROM withdrawals WHERE id = :id FOR UPDATE";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':id' => $withdrawal_id]);
            $withdrawal = $stmt->fetch(PDO::FETCH_ASSOC);
            if (!$withdrawal) {
                throw new Exception('提现记录不存在');
            }
            if ($withdrawal['status'] != 'pending') {
                throw new Exception('提现已处理');
            }
            $sql = "UPDATE withdrawals 
                    SET status = 'processing', tx_hash = :tx_hash 
                    WHERE id = :id";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                ':tx_hash' => $tx_hash,
                ':id' => $withdrawal_id
            ]);
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
    /**
     * 完成提现
     */
    public function completeWithdrawal($withdrawal_id) {
        $this->db->beginTransaction();
        try {
            $sql = "UPDATE withdrawals 
                    SET status = 'completed' 
                    WHERE id = :id AND status = 'processing'";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([':id' => $withdrawal_id]);
            if ($stmt->rowCount() == 0) {
                throw new Exception('提现状态不正确');
            }
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
}

主控制器

<?php
// CurrencyController.php
namespace CurrencySystem;
use PDO;
use Exception;
class CurrencyController {
    private $db;
    public function __construct(PDO $db) {
        $this->db = $db;
    }
    /**
     * 获取用户钱包信息
     */
    public function getWalletInfo($user_id) {
        $wallet = new Wallet($this->db, $user_id);
        return $wallet->getBalance();
    }
    /**
     * 执行转账
     */
    public function transfer($user_id, $to_address, $amount) {
        $wallet = new Wallet($this->db, $user_id);
        return $wallet->transfer($to_address, $amount);
    }
    /**
     * 获取交易记录
     */
    public function getTransactions($user_id, $limit = 20, $offset = 0) {
        $sql = "SELECT * FROM transactions 
                WHERE user_id = :user_id 
                ORDER BY id DESC 
                LIMIT :limit OFFSET :offset";
        $stmt = $this->db->prepare($sql);
        $stmt->bindValue(':user_id', $user_id, PDO::PARAM_INT);
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    /**
     * 获取充值历史
     */
    public function getDeposits($user_id) {
        $sql = "SELECT * FROM deposits WHERE user_id = :user_id ORDER BY id DESC";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':user_id' => $user_id]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    /**
     * 获取提现历史
     */
    public function getWithdrawals($user_id) {
        $sql = "SELECT * FROM withdrawals WHERE user_id = :user_id ORDER BY id DESC";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':user_id' => $user_id]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    /**
     * 用户余额汇总
     */
    public function getBalanceSummary($user_id) {
        $wallet = new Wallet($this->db, $user_id);
        $balance = $wallet->getBalance();
        // 计算总资产
        $total = $balance['balance'];
        // 计算可用余额
        $available = $balance['balance'] - $balance['locked_balance'];
        return [
            'total_balance' => $total,
            'available_balance' => $available,
            'locked_balance' => $balance['locked_balance']
        ];
    }
}

路由示例

<?php
// index.php
require_once 'config/database.php';
use CurrencySystem\CurrencyController;
// 创建数据库连接
$db = getDBConnection();
$controller = new CurrencyController($db);
// 模拟用户ID
$user_id = 1;
// 路由处理
$action = $_GET['action'] ?? 'balance';
$user_id = $_SESSION['user_id'] ?? 1; // 实际项目中从session获取
switch ($action) {
    case 'balance':
        $balance = $controller->getBalanceSummary($user_id);
        echo json_encode(['success' => true, 'balance' => $balance]);
        break;
    case 'transfer':
        $to_address = $_POST['to_address'] ?? '';
        $amount = $_POST['amount'] ?? 0;
        $result = $controller->transfer($user_id, $to_address, $amount);
        echo json_encode($result);
        break;
    case 'transactions':
        $transactions = $controller->getTransactions($user_id);
        echo json_encode(['success' => true, 'data' => $transactions]);
        break;
    case 'deposit':
        // 充值处理
        $amount = $_POST['amount'] ?? 0;
        $depositService = new DepositService($db);
        $deposit = $depositService->createDeposit($user_id, $amount);
        echo json_encode(['success' => true, 'data' => $deposit]);
        break;
    case 'withdrawal':
        $amount = $_POST['amount'] ?? 0;
        $address = $_POST['address'] ?? '';
        $withdrawService = new WithdrawalService($db);
        $withdrawal_id = $withdrawService->createWithdrawal($user_id, $amount, $address);
        echo json_encode(['success' => true, 'withdrawal_id' => $withdrawal_id]);
        break;
}

安全注意事项

<?php
// Security.php
namespace CurrencySystem;
class Security {
    private $db;
    public function __construct($db) {
        $this->db = $db;
    }
    /**
     * 防止SQL注入
     */
    public function sanitizeInput($input) {
        return htmlspecialchars(strip_tags(trim($input)));
    }
    /**
     * 验证用户权限
     */
    public function checkPermission($user_id, $action) {
        // 获取用户角色
        $sql = "SELECT is_admin FROM users WHERE id = :user_id";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':user_id' => $user_id]);
        $user = $stmt->fetch();
        // 管理操作需要管理员权限
        if (in_array($action, ['manual_deposit', 'review_withdrawal'])) {
            if (!$user['is_admin']) {
                return false;
            }
        }
        return true;
    }
    /**
     * 验证地址格式
     */
    public function validateAddress($address) {
        // 模拟地址验证
        return preg_match('/^(0x)?[a-fA-F0-9]{40}$/', $address);
    }
}

使用示例

<?php
require_once 'autoload.php';
require_once 'config/database.php';
use CurrencySystem\CurrencyController;
// 初始化
$db = getDBConnection();
$controller = new CurrencyController($db);
// 用户1转账给用户2
$transfer_result = $controller->transfer(1, 2, 10.5);
if (is_string($transfer_result)) {
    echo "转账失败: " . $transfer_result;
} else {
    echo "转账成功";
}
// 查看用户余额
$balance = $controller->getBalanceSummary(1);
print_r($balance);
// 获取交易记录
$transactions = $controller->getTransactions(1, 10);
print_r($transactions);

安全建议

  1. 加密存储:密码必须使用bcrypt或Argon2加密
  2. SQL注入防护:总是使用PDO预处理语句
  3. 事务操作:所有涉及资金的操作必须使用数据库事务
  4. 数据验证:所有输入必须严格验证
  5. 日志记录:记录所有关键操作
  6. 速率限制:防止暴力攻击
  7. 双重验证:关键操作需要二次验证
  8. 审计追踪:所有交易都有唯一标识并可追踪

这个系统可以作为基础框架,根据实际需求添加更多功能,如多币种支持、智能合约集成、实时价格更新等。

抱歉,评论功能暂时关闭!