本文目录导读:

实现PHP会员充值功能,通常涉及用户管理、支付网关对接、订单系统和余额/积分变动记录这几个核心模块。
由于支付网关(支付宝、微信支付)的对接比较复杂且需要商户资质,以下我会分核心业务逻辑、简单演示代码(假设是余额充值)以及支付对接思路三个层面详细说明。
核心业务流程
- 用户发起充值:用户在前端选择充值金额(或自定义输入)。
- 创建充值订单:后端在数据库
recharge_orders表中插入一条记录,状态设为“待支付”。 - 调用支付接口:根据用户选择的支付方式(支付宝/微信),生成支付二维码或跳转链接。
- 用户支付:用户在外部完成支付(扫码或跳转)。
- 支付回调处理:支付平台异步通知你的服务器(Webhook)或前端轮询查询订单状态。
- 更新余额:收到支付成功的通知后,验证订单号和金额,然后更新用户余额表,并记录资金流水。
- 结果返回:用户界面显示充值成功。
数据库结构设计
需要准备至少三张核心表:
-- 1. 用户表(已有,假设包含 balance 字段) CREATE TABLE `users` ( `id` int PRIMARY KEY AUTO_INCREMENT, `username` varchar(50) NOT NULL, `balance` decimal(10,2) DEFAULT 0.00 COMMENT '用户余额', `updated_at` timestamp ); -- 2. 充值订单表 CREATE TABLE `recharge_orders` ( `id` int PRIMARY KEY AUTO_INCREMENT, `user_id` int NOT NULL, `order_sn` varchar(64) NOT NULL UNIQUE COMMENT '订单号,唯一', `amount` decimal(10,2) NOT NULL COMMENT '充值金额', `pay_type` tinyint COMMENT '1=支付宝 2=微信', `status` tinyint DEFAULT 0 COMMENT '0=待支付 1=支付成功 2=已退款 3=已关闭', `trade_no` varchar(64) DEFAULT NULL COMMENT '支付平台流水号', `pay_time` datetime DEFAULT NULL COMMENT '支付时间', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 3. 资金变动流水表(用于对账和展示) CREATE TABLE `fund_flow` ( `id` int PRIMARY KEY AUTO_INCREMENT, `user_id` int NOT NULL, `order_sn` varchar(64) DEFAULT NULL, `type` tinyint NOT NULL COMMENT '1=充值 2=消费 3=退款 4=提现', `amount` decimal(10,2) NOT NULL, `before_balance` decimal(10,2) NOT NULL, `after_balance` decimal(10,2) NOT NULL, `remark` varchar(255) DEFAULT NULL, `created_at` timestamp DEFAULT CURRENT_TIMESTAMP );
核心PHP功能代码(简版,非完整项目)
以下使用 PDO 和 OOP 思想演示核心逻辑,支付回调部分以注释形式说明。
创建充值订单
<?php
// create_recharge.php
require_once 'db.php'; // 包含PDO连接
function createRechargeOrder($userId, $amount, $payType = 1) {
global $pdo;
// 1. 生成唯一订单号
$orderSn = date('YmdHis') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
// 2. 插入订单记录 (事务可选,但单条插入可以不用)
$stmt = $pdo->prepare("INSERT INTO recharge_orders (user_id, order_sn, amount, pay_type) VALUES (?, ?, ?, ?)");
$stmt->execute([$userId, $orderSn, $amount, $payType]);
// 3. 根据 payType 调用支付网关,返回二维码URL或表单
// 这里假设你已经有支付类 PayService
// $payUrl = PayService::getInstance()->alipay($orderSn, $amount, '会员充值');
return [
'order_sn' => $orderSn,
'amount' => $amount,
// 'pay_url' => $payUrl
];
}
// 前端请求示例:POST ['user_id'=>1, 'amount'=>100]
$input = json_decode(file_get_contents('php://input'), true);
$result = createRechargeOrder($input['user_id'], $input['amount']);
echo json_encode(['code' => 0, 'data' => $result]);
支付成功的回调处理(关键!)
支付平台通常会 POST 通知你的服务器一个接口。
<?php
// callback.php (支付宝/微信的异步通知)
require_once 'db.php';
function handlePayNotify($data) {
global $pdo;
// 1. 验证签名(使用支付宝SDK或自己验签) 此处省略
// if(!verifySign($data)) exit('fail');
$orderSn = $data['out_trade_no']; // 商户订单号
$tradeNo = $data['trade_no']; // 支付平台交易号
$totalAmount = $data['total_amount']; // 支付金额
// 2. 查询订单,防止重复处理
$stmt = $pdo->prepare("SELECT * FROM recharge_orders WHERE order_sn = ?");
$stmt->execute([$orderSn]);
$order = $stmt->fetch();
if (!$order || $order['status'] != 0) {
// 订单状态异常或已处理,返回成功(避免微信/支付宝重复通知)
exit('success');
}
// 3. 金额校验(防止篡改)
if (bccomp($order['amount'], $totalAmount, 2) !== 0) {
// 记录日志,可能是攻击
exit('fail');
}
// 4. 开启事务,更新订单和用户余额
try {
$pdo->beginTransaction();
// 更新订单状态
$stmt = $pdo->prepare("UPDATE recharge_orders SET status=1, trade_no=?, pay_time=NOW() WHERE order_sn=?");
$stmt->execute([$tradeNo, $orderSn]);
// 增加用户余额 (使用 FOR UPDATE 防止并发)
$stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ? FOR UPDATE");
$stmt->execute([$order['user_id']]);
$user = $stmt->fetch();
$before = $user['balance'];
$after = bcadd($before, $order['amount'], 2); // 使用高精度加法
$stmt = $pdo->prepare("UPDATE users SET balance = ? WHERE id = ?");
$stmt->execute([$after, $order['user_id']]);
// 插入资金流水
$stmt = $pdo->prepare("INSERT INTO fund_flow (user_id, order_sn, type, amount, before_balance, after_balance, remark) VALUES (?, ?, 1, ?, ?, ?, '会员充值')");
$stmt->execute([$order['user_id'], $orderSn, $order['amount'], $before, $after]);
$pdo->commit();
echo 'success'; // 告诉支付平台我们已经处理成功
} catch (Exception $e) {
$pdo->rollBack();
echo 'fail';
}
}
// 支付宝/微信会POST数据过来
handlePayNotify($_POST);
用户查询余额
<?php
// get_balance.php
require_once 'db.php';
header('Content-Type: application/json');
$userId = $_GET['user_id'] ?? 0;
$stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ?");
$stmt->execute([$userId]);
$user = $stmt->fetch();
echo json_encode(['balance' => $user['balance'] ?? 0.00]);
如何对接支付网关(真正难点)
- 使用第三方支付SDK:推荐 Yansongda/Pay(PHP支付包,支持支付宝、微信)。
- 基本步骤:
- 去支付宝/微信商户平台申请 支付商户号(需要企业资质)。
- 下载官方SDK或使用上述封装包。
- 获得支付宝公钥、应用私钥等参数。
- 在
createRechargeOrder中调用支付包生成 订单支付链接。 - 配置 异步回调地址 (
notify_url) 指向你的callback.php。 - 安全注意:回调地址必须校验签名,并且校验金额与订单一致。
Tip:开发测试阶段可以使用支付宝沙箱环境(沙箱账户有测试资金)。
需要注意的安全与高并发问题
- 订单幂等性:支付回调可能重复通知,一定要用
status != 0或SELECT ... FOR UPDATE防止重复加钱。 - 金额使用高精度:不要用
float,使用decimal或 PHP 的bcmath函数(bcadd,bccomp)。 - 回调日志:每次收到回调,先记录原始数据到日志文件,方便排查问题。
- 防止并发扣款:使用数据库行锁(
FOR UPDATE)或 Redis 分布式锁,处理用户余额增减。 - 界面思路:前端生成订单后,轮询订单状态接口
/check_order.php?order_sn=xxx,直到返回支付成功。
补充:想简化开发?
如果不需要自己从零写支付对接,可以考虑:
- 使用三方聚合支付平台:如 PayJs、虎皮椒、码支付,它们通常提供个人也能用的 API(手续费高一些),但避免了自己申请商户的麻烦。
- Demo项目参考:GitHub 搜索关键词
PHP充值系统、PHP积分商城等。
核心是订单生成 -> 支付回调 -> 余额增加 + 流水记录,注意并发安全即可。