简介:这是一套面向企业或个人运营者的商业视频打赏平台源码,基于完整可编译的前后端工程构建,适合具备一定开发基础、希望快速搭建自定义打赏系统的技术人员二次开发。压缩包共2001个文件,以1766个js脚本、134个html页面、23个css样式及少量json、sql、md文档为主,整体约57.45MB,涵盖用户模块、视频管理、双支付接口、打赏逻辑、代理打赏、数据统计与安全机制等核心组成。系统集成支付宝、微信等多种支付渠道,支持代理打赏与收益分配,便于运营者分析用户行为与收入情况。已有625人学习下载,购买者可获得底层源码,按需修改、优化或扩展功能,快速落地具备双支付与代理打赏能力的视频平台。
1. 商业视频打赏系统:从“双支付+代理分账”看一套源码的真实落地门槛
你拿到一个压缩包,名字叫“最新商业视频打赏系统源码 打赏平台 视频打赏双支付系统 含代理打赏源码.zip”,第一反应大概率是:解压、配数据库、跑起来,然后接支付、开代理、上线收钱。但真正做过这类系统的人都知道,视频打赏平台的核心难点从来不是“能不能播放视频”,而是钱怎么进、怎么分、怎么对账、怎么防刷。双支付系统意味着你要同时对接至少两条支付通道(比如微信原生支付+支付宝当面付,或者易支付+码支付),代理打赏则要求每一笔打赏都能按代理层级实时分润,且不能出现超卖、重复分账、掉单不补。这套源码适合谁?适合有一定 PHP/MySQL 基础、想快速搭建垂直视频打赏平台(如才艺直播切片、短剧打赏、私域视频互动)的开发者或小团队。如果你连 Composer 和 Nginx 伪静态都没配过,建议先补课,否则后面每一步都是血泪坑。本章不堆概念,只帮你判断:这套东西值不值得投入时间,以及你会在哪几个环节被卡住。
2. 双支付系统怎么接:从通道选型到异步回调的完整链路
2.1 双支付不是“两个按钮”,而是两套独立回调逻辑
很多新手以为双支付就是在打赏页放两个支付图标,用户点哪个就走哪个。实际代码里,微信支付和支付宝支付的异步通知签名算法、报文格式、重试策略完全不同。以常见的 PHP 实现为例,微信 V3 支付用 SHA256-RSA 验签,支付宝用 RSA2 验签,且支付宝回调是application/x-www-form-urlencoded,微信是 JSON。如果你把两套回调写在一个notify.php里用if($type=='wx')硬分支,后期加通道会非常痛苦。我一般会拆成notify_wxpay.php和notify_alipay.php,各自独立验签、独立写订单状态,只共用同一个order表和user表。
// notify_wxpay.php 核心片段:验签后处理业务 $wechatPay = new WeChatPayV3($mchId, $serialNo, $privateKey); $body = file_get_contents('php://input'); $verifyResult = $wechatPay->verifyNotify($body, $_SERVER['HTTP_WECHATPAY_SIGNATURE'] ?? ''); if (!$verifyResult) { exit('sign fail'); } $data = json_decode($body, true); $outTradeNo = $data['resource']['ciphertext']['out_trade_no'] ?? ''; // 查本地订单,防止重复分账 $order = $db->get('orders', '*', ['out_trade_no' => $outTradeNo, 'status' => 0]); if (!$order) { exit('order not found or already paid'); } // 开启事务:更新订单 + 写分账记录 + 加代理佣金 $db->action(function($db) use ($order, $data) { $db->update('orders', ['status' => 1, 'pay_time' => time()], ['id' => $order['id']]); $db->insert('commission_log', [ 'order_id' => $order['id'], 'agent_id' => $order['agent_id'], 'amount' => $order['price'] * $order['agent_rate'], 'created_at' => time() ]); }); echo json_encode(['code' => 'SUCCESS', 'message' => 'OK']);上面代码的关键点有三个:第一,验签必须在业务处理之前,否则伪造回调直接改订单;第二,查订单要带status=0条件,微信会重复通知,不加条件会重复分账;第三,分账和改状态必须在同一个数据库事务里,否则改完状态但分账失败,代理会来找你。参数方面,$mchId和$serialNo从微信商户平台获取,$privateKey是apiclient_key.pem的内容,不要用公钥。支付宝那边类似,但注意trade_status必须是TRADE_SUCCESS或TRADE_FINISHED才处理,TRADE_CLOSED要忽略。
2.2 支付通道的“降级开关”与订单超时关单
双支付系统最怕一种情况:微信通道临时维护,用户点微信支付一直转圈,但页面没有提示,用户以为打赏成功了,实际上订单没支付。我一般会在后台加一个pay_channel_status配置表,每个通道有enabled字段,前端渲染时只显示启用的通道。同时,订单创建后写入expire_time(通常 5 分钟),用计划任务每分钟扫描status=0 AND expire_time < now()的订单,调用微信/支付宝的关单接口,并把本地订单标记为status=2(已关闭)。这样能避免用户过半小时又去支付一个已关闭的订单,导致回调回来找不到有效订单。
# crontab 每分钟执行关单脚本 * * * * * /usr/bin/php /www/wwwroot/your_domain/cron/close_order.php >> /tmp/close_order.log 2>&1close_order.php里循环查 100 条过期订单,逐条调用closeOrder接口。注意微信关单接口要求订单未支付且未关闭,如果已经支付成功但回调延迟,关单会返回错误,此时应跳过并记录日志,不要强行改状态。这个逻辑写不好,就会出现“用户付了钱但订单被关”的玄学问题,对账时非常头疼。
2.3 代理分账的层级计算与防超卖
代理打赏源码通常支持无限级代理,但实际落地时我建议最多三级,因为层级越深,分账计算越容易出错,且代理之间容易产生纠纷。数据库设计上,agent表要有parent_id和path字段(如1,3,7),这样查上级代理不用递归查询。每笔打赏订单创建时,根据用户绑定的代理 ID 向上追溯,把每一级的佣金比例和金额算好,写入commission_log表,状态为pending。等支付回调成功,再把pending改为settled。这里有一个关键参数:代理佣金比例不能超过平台总抽成,否则平台亏钱。我一般会在后台加校验:所有上级代理佣金之和 + 平台抽成 = 100%,如果配置超过 100%,保存时直接报错。
-- 查询某代理的所有上级,按层级从近到远 SELECT id, parent_id, rate FROM agent WHERE FIND_IN_SET(id, (SELECT path FROM agent WHERE id = ?)) ORDER BY FIELD(id, REVERSE(SUBSTRING_INDEX(REVERSE(path), ',', 1))) DESC;上面 SQL 利用了path字段快速取上级,但注意FIND_IN_SET在数据量大时性能一般,代理表通常几千行以内没问题。如果代理超过 1 万,建议用闭包表或递归 CTE。防超卖方面,打赏本身不涉及库存,但代理佣金余额不能为负。提现时先扣减agent.balance,如果扣减后小于 0,直接拒绝。不要用“先提现再扣款”的逻辑,否则代理可以提现后立刻打赏把余额花掉,平台垫钱。
3. 视频打赏业务侧:从播放器埋点到防刷打赏的工程细节
3.1 视频播放器与打赏按钮的联动埋点
视频打赏系统的前端通常是一个 H5 页面,嵌入<video>标签或第三方播放器。打赏按钮不是一直显示,而是在视频播放到特定时间点(比如高潮片段)才弹出。实现方式有两种:一种是用timeupdate事件监听当前播放进度,当currentTime进入预设区间(如 30s-35s)时显示打赏浮层;另一种是后端在视频元数据里标记reward_points,前端拉取后动态插入。我倾向于第一种,因为不需要改视频文件,运营可以随时在后台调整时间点。
// 视频播放到指定区间显示打赏按钮 const video = document.getElementById('myVideo'); const rewardPoints = [30, 60, 90]; // 秒 let shown = {}; video.addEventListener('timeupdate', function() { const t = Math.floor(video.currentTime); rewardPoints.forEach(point => { if (t >= point && t < point + 5 && !shown[point]) { document.getElementById('rewardLayer').style.display = 'block'; shown[point] = true; } }); });这段代码的逻辑是:每到一个打赏点,显示浮层,并用shown对象防止同一区间重复弹出。参数rewardPoints从后端接口获取,不要硬编码。注意移动端浏览器可能限制自动播放,打赏浮层出现时如果视频暂停,用户体验会割裂,建议浮层出现时不暂停视频,只覆盖半屏。
3.2 防刷打赏:频率限制、金额校验与黑名单
没有防刷的打赏系统上线当天就会被羊毛党盯上。最常见的攻击方式是:用脚本批量注册账号,每个账号打赏 0.01 元,刷代理佣金。防御手段分三层:第一层,同一用户 ID 打赏间隔不少于 10 秒,用 Redis 的SETNX加过期时间实现;第二层,单笔打赏金额最低 1 元,低于 1 元直接拒绝,因为支付通道手续费可能都不止;第三层,同一 IP 注册账号数超过 5 个,禁止打赏,这个在注册时就要限制。
// 打赏频率限制:10 秒内同一用户只能打赏一次 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $key = 'reward_limit:' . $userId; if (!$redis->set($key, 1, ['nx', 'ex' => 10])) { exit(json_encode(['code' => 429, 'msg' => '操作太频繁,请稍后再试'])); } // 继续后续打赏逻辑set的nx表示 key 不存在时才设置,ex是过期秒数。如果返回 false,说明 10 秒内已经打过赏。这个逻辑要放在创建订单之前,而不是支付回调里,否则用户频繁创建订单但不支付,也会拖垮数据库。另外,黑名单表blacklist要支持按用户 ID、IP、设备指纹三个维度封禁,封禁后前端直接隐藏打赏按钮,后端接口也返回 403。
3.3 打赏记录的实时推送与对账文件生成
用户打赏成功后,前端需要立即看到“打赏成功”提示和排行榜更新。如果只靠轮询,延迟高且费服务器。我一般用 WebSocket 或 SSE 推送。PHP 环境下,Workerman 是比较常见的选择。打赏成功后,在支付回调里向 Workerman 推送一条消息,Workerman 再广播给对应房间的所有客户端。对账方面,每天凌晨生成前一天的order表和支付通道账单的对比文件,重点核对三个字段:订单号、金额、状态。差异记录写入reconcile_diff表,人工介入。不要小看对账,双支付系统最容易出的问题就是“微信显示成功,本地显示未支付”,没有对账文件你根本不知道丢了多少钱。
4. 避坑与排查:代理分账、回调、数据库的五个血泪教训
4.1 现象:代理佣金重复到账,提现时余额多了一倍
原因:支付回调没有做幂等,微信重复通知时,每次都对commission_log插入新记录,且没有唯一索引约束。解决:在commission_log表上建唯一索引UNIQUE KEY order_agent (order_id, agent_id),插入时用INSERT IGNORE或ON DUPLICATE KEY UPDATE。同时,订单表status更新要加AND status=0条件,影响行数为 0 说明已处理过,直接返回成功。
4.2 现象:支付宝回调偶尔收不到,订单一直未支付
原因:支付宝异步通知要求返回纯文本success,如果 PHP 文件有 BOM 头或额外输出,支付宝会认为通知失败并重试,重试几次后不再通知。解决:检查notify_alipay.php文件编码为 UTF-8 无 BOM,且<?php前不能有任何空格或换行。处理完业务后echo 'success'; exit;,不要输出 JSON 或 HTML。
4.3 现象:代理提现时余额扣成负数,平台垫钱
原因:提现逻辑先查余额,再扣减,但两个操作之间没有加锁,代理同时发起两笔提现,都查到足够余额,都扣成功。解决:用数据库行锁SELECT ... FOR UPDATE或 Redis 分布式锁。我一般用UPDATE agent SET balance = balance - ? WHERE id = ? AND balance >= ?,根据影响行数判断是否扣减成功,影响行数为 0 说明余额不足。
4.4 现象:视频打赏按钮在 iOS 上点击无反应
原因:iOS Safari 对click事件在video标签上的冒泡处理有差异,且如果打赏按钮是绝对定位覆盖在视频上,z-index不够或父元素有pointer-events: none。解决:给打赏按钮单独设置z-index: 9999,并确保父元素没有pointer-events: none。如果还是不行,改用touchstart事件并preventDefault。
4.5 现象:数据库连接数暴涨,页面 502
原因:每个支付回调都新建数据库连接,且没有及时关闭,或者用了pconnect长连接但 PHP-FPM 进程数配置过高。解决:检查代码里是否用了new PDO后没有置空,建议用单例模式封装数据库连接。同时调整php-fpm.conf的pm.max_children,根据内存计算,一般 2GB 内存的服务器设 20-30 即可。不要盲目调大,否则 MySQL 先崩。
5. 进阶技巧:用对账脚本反推支付通道的“掉单率”并自动补单
最后一章不讲虚的,讲一个我实际用了两年的技巧:用对账脚本自动补单。双支付系统最怕掉单,用户付了钱但回调没来,订单一直是未支付。常规做法是人工查支付平台账单,手动补。但你可以写一个脚本,每天凌晨拉取微信和支付宝的对账单,和本地orders表比对,找出“支付平台成功但本地未支付”的订单,自动调用内部补单接口,把订单状态改为已支付,并补发代理佣金。
// reconcile.php 核心逻辑:拉取微信账单,比对本地订单 $bill = $wechatPay->downloadBill(date('Ymd', strtotime('-1 day'))); foreach ($bill as $row) { $outTradeNo = $row['out_trade_no']; $order = $db->get('orders', '*', ['out_trade_no' => $outTradeNo]); if ($order && $order['status'] == 0) { // 支付平台成功但本地未支付,触发补单 $db->action(function($db) use ($order, $row) { $db->update('orders', ['status' => 1, 'pay_time' => strtotime($row['time_end'])], ['id' => $order['id']]); // 补发代理佣金,注意幂等 $db->insert('commission_log', [ 'order_id' => $order['id'], 'agent_id' => $order['agent_id'], 'amount' => $order['price'] * $order['agent_rate'], 'created_at' => time() ], 'IGNORE'); }); // 记录补单日志,方便后续审计 file_put_contents('/tmp/reconcile_fix.log', date('Y-m-d H:i:s') . " fixed order: {$outTradeNo}\n", FILE_APPEND); } }这段代码的关键参数:downloadBill的日期是前一天,因为当天账单可能不完整;commission_log插入用IGNORE防止重复;补单日志必须写,否则你永远不知道系统自动补了多少单。运行一段时间后,你可以统计补单率,如果超过 0.5%,说明回调接口不稳定,需要检查服务器网络或支付通道配置。我一般会把这个脚本放在 crontab 每天凌晨 3 点执行,执行完发一封邮件给自己,邮件里包含补单数量和总金额。这个习惯帮我发现过一次微信回调域名被误封,及时换了备用域名,避免了更大损失。希望帮到你。
本文还有配套的精品资源,点击获取