本文目录导读:

我将为您设计一个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);
安全建议
- 加密存储:密码必须使用bcrypt或Argon2加密
- SQL注入防护:总是使用PDO预处理语句
- 事务操作:所有涉及资金的操作必须使用数据库事务
- 数据验证:所有输入必须严格验证
- 日志记录:记录所有关键操作
- 速率限制:防止暴力攻击
- 双重验证:关键操作需要二次验证
- 审计追踪:所有交易都有唯一标识并可追踪
这个系统可以作为基础框架,根据实际需求添加更多功能,如多币种支持、智能合约集成、实时价格更新等。