写真打赏系统源码全解析:PHP+MySQL+易支付部署与安全加固
简介这是一套2024年发布的写真图片与视频打赏系统源码面向内容站长、个人开发者和初创团队可用于搭建图片打赏站、视频打赏站或与付费进群结合实现内容变现。系统接入易支付内置独立代理后台支持支付渠道管理与域名防洪等基础安全机制且全源码无加密、可自由修改二次开发。压缩包共2000个文件以JavaScript、HTML、CSS、PHP等前后端代码为主分别对应页面脚本、界面样式和服务端逻辑另含图片素材、SQL数据库文件和说明文档便于部署与二次开发整体仅29.65MB结构清晰、部署轻量。内附详细安装课程能帮助不熟悉技术细节的使用者快速完成环境配置与上线运营。目前已有948人学习/下载适合希望低成本开启内容付费或打赏场景的开发者参考使用。1. 写真打赏系统源码全开无加密先搞清楚它能干什么这套“写真图片视频打赏系统源码”最近在开源圈里传得比较快。表面看是带图集和视频封面的打赏页面实际拆开是 PHP MySQL 的完整内容收费方案用户端负责展示作品和提交打赏代理端可以推广和查看佣金管理端处理订单和内容。支付通道集成易支付整包无加密可以随时改逻辑。它解决的核心问题是创作者想搭一个写真站或视频付费站时不需要从零写支付回调、订单对账和代理分佣。适合要快速上线、又要留二开空间的人。下面我从源码结构、易支付对接、部署排错和两个安全加固点来拆这套代码最后给可直接抄的对账脚本。2. 打赏源码的结构与权限模型入口、路由和分佣的三层设计拿到源码后先别急着看界面public.css、oneui.css、bootstrap.min.css 这些只是模板样式核心业务在 app 和 application 目录里。我拆的这套版本是典型 ThinkPHP 风格目录运行入口在 public/index.php所有打赏、回调、代理请求都通过 index.php 路由分发到控制器。前台负责作品列表和打赏按钮管理后台负责作品和订单代理后台单独跑一套推广和分佣逻辑三层结构边界清晰。2.1 静态资源与业务控制器路径作用/public静态资源、入口文件、上传目录/app/controller前台控制器处理打赏请求/app/controller/admin平台管理后台控制器/app/controller/agent代理后台控制器/app/model用户、作品、订单模型/app/service易支付回调、对账、分佣逻辑/config数据库、支付、路由配置/route路由规则定义这里建议部署时把站点根目录指到 public避免有人直接访问 application 目录下的源码。使用宝塔面板创建站点后把运行目录设成 /public伪静态选择 ThinkPHP 即可。手动 Nginx 则需要把非文件请求重写到 index.php后面第 4 章会写完整配置。路由规则决定了打赏入口的地址结构。常见的路由定义是这样一段// route/app.php Route::rule(pay/:id, pay/index); // 提交打赏订单 Route::rule(notify, pay/notify); // 易支付异步回调 Route::rule(agent/login, agent/login/index); // 代理登录 Route::rule(agent/orders, agent/orders/index); // 代理订单列表:id是路径参数访问/pay/12时PayController::index($id)会收到作品 id 12。把支付入口收拢在 Pay 控制器后期接其他支付渠道时只需要改控制器内部逻辑路由完全不用动。很多改崩的打赏站就是直接在路由里写死文件名导致换目录后全部 404。2.2 订单表与代理佣金字段数据库表通常带前缀核心表是 orders用户和作品关系、代理分佣最终都落到订单上。order 表结构大致如下CREATE TABLE orders ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 商户订单号, user_id int(11) NOT NULL COMMENT 用户ID, work_id int(11) NOT NULL COMMENT 作品ID, type tinyint(1) NOT NULL DEFAULT 0 COMMENT 0图片 1视频, amount decimal(10,2) NOT NULL COMMENT 支付金额, commission decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT 代理佣金金额, agent_id int(11) DEFAULT 0 COMMENT 归属代理ID, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 0待支付 1已支付 2已关闭, create_time int(11) NOT NULL, pay_time int(11) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意agent_id和commission是分佣体系的基石。用户第一次通过代理链接进入时系统记录推广关系下单时把agent_id写入订单并算出commission支付成功后直接把这笔佣金累加进代理账户。这里的status必须区分待支付、已支付、已关闭否则回调重复时容易重复加钱。2.3 三者权限关系与推广归属前台用户、代理、管理员是三层相互独立又存在归属关系。管理员给代理开启后台并设定佣金比例代理有自己的登录入口和订单列表但看不到平台全量数据普通用户只能看到作品和打赏记录。校验逻辑一般写在app/middleware/AuthMiddleware.php登录状态会分开存 session避免代理登录后覆盖了管理员身份。我拆这套源码时发现一个隐藏的坑代理关系绑定可能在中间表也可能直接存在用户表bind_agent_id字段。如果发现代理佣金算不出来优先看两个位置一是推广链接是否带了 agent_id 参数二是用户表有没有在首次访问时写入来源代理。很多站点即使支付成功代理也看不到佣金就是这两步没绑住。3. 易支付对接与打赏回调签名、验签与分佣结算易支付是一类第三方聚合支付系统它会把多种支付渠道聚合在一起。你只需要按它的签名规则把订单信息传过去支付完成后由易支付服务器向你的异步接口发通知。把易支付接好的核心是三点下单参数拼接、签名计算、回调验签。3.1 易支付的工作方式流程是用户点打赏 - 提交订单 - 跳转易支付收银台 - 用户扫码支付 - 同步跳转回成功页面同时异步 POST 到 notify 地址。对服务端来说真正可靠的只有异步通知同步跳转只用于展示结果。源码里下单方法一般叫PayController::index通知方法叫notify。易支付强调异步通知地址必须能从公网直接访问否则回调会失败。3.2 下单接口构建支付参数并跳转下面是一段按易支付协议写的下单逻辑参数名直接与网关对应public function index($id) { $order $this-createOrder($id); $params [ pid $this-config[pid], // 平台商户ID type alipay, // 支付方式 out_trade_no $order[order_no], // 商户订单号 notify_url $this-config[notify], // 异步通知地址 return_url $this-config[return], // 同步跳转地址 name $order[title], // 商品名称 money $order[amount], // 金额单位元 sign , sign_type MD5, ]; ksort($params); reset($params); $signStr urldecode(http_build_query($params)) . $this-config[key]; $params[sign] md5($signStr); $this-redirect(https://pay.easy.com/submit.php? . http_build_query($params)); }这里的签名规则是除 sign 和 sign_type 外其它参数按键名升序排列用键值拼成 URL 查询字符串再和商户密钥拼接MD5 后作为 sign。urldecode很关键因为http_build_query会把中文编码签名时要还原成原始字符串否则支付平台验签不通过。3.3 异步回调验签防止伪造支付成功回调是安全边界网上很多源码只看$_POST里的 trade_status 就认为支付成功这很容易被人直接 POST 成功状态刷单。正确做法是先验签再查订单再更新状态public function notify() { $data $_POST; $sign $data[sign]; unset($data[sign], $data[sign_type]); ksort($data); $signStr urldecode(http_build_query($data)) . $this-config[key]; if (md5($signStr) ! $sign) { exit(fail); } $orderNo $data[out_trade_no]; $order db(orders)-where(order_no, $orderNo)-find(); if (!$order || $order[status] 1) { exit(success); // 幂等处理已支付直接返回 } if ($data[trade_status] TRADE_SUCCESS) { // 开启事务更新订单、增加代理佣金、解锁作品 $this-completeOrder($order); } echo success; }参数含义out_trade_no是下单时生成的商户订单号trade_status是易支付返回的状态TRADE_SUCCESS 才表示付款成功。回调接口最后必须输出字符串 success否则支付平台会重复发送通知。验签失败时输出 fail但不要输出具体错误避免给攻击者提示。3.4 代理分佣的结算时机分佣结算有两种常见时机支付成功立即结算或每天定时对账批量结算。这套源码里我看到的是即时结算也就是回调里调用completeOrder时把orders.commission累加到代理账户随后生成佣金明细。字段含义说明agent_id代理ID来源于用户推广关系commission佣金金额根据 amount * 代理比例计算settle_status结算状态0 未结算 / 1 已结算settle_time结算时间回调后立即写入这里有一个容易踩的坑代理比例如果设成动态区间结算时要用下单时刻的比例而不是回调时刻的比例。管理员中途改了代理比例会把前面订单的佣金算错。常见做法是商品表保存一份比例快照或者在生成订单时把比例写进 orders 表的 commission_rate 字段。4. 部署配置与启动故障Nginx 伪静态、PHP 扩展和目录权限这套源码在本地跑和线上跑差异很大问题主要集中在伪静态、运行目录、PHP 扩展和目录权限。我按从零部署的顺序写跟着操作能把站点跑起来。4.1 环境准备PHP 7.4 MySQL 5.7 Nginx 1.18源码基于 PHP 7 开发建议用 PHP 7.4 最稳MySQL 用 5.7。用宝塔时直接装 Nginx MySQL PHP装完开fileinfo、opcache扩展。PHP 版本过高比如 8.2 容易出现字符串偏移异常因为老代码用到的语法在 8.0 里会被当作错误处理。# 检查扩展是否已安装 php -m | grep fileinfo php -m | grep pdo_mysqlphp -m输出里必须至少看到 fileinfo 和 pdo_mysql缺少任一都会在安装环境检测阶段报错。我另外会打开短标签开关short_open_tagOn老源码里出?写法的可能性不低不然页面直接输出乱码。4.2 Nginx 伪静态规则与 PHP-FPM 配置站点根目录指到 public伪静态规则用 ThinkPHP 的标准规则。手动 Nginx 配置如下server { listen 80; server_name your-domain.com; root /www/wwwroot/your-site/public; index index.php index.html; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }注意rewrite ^(.*)$ /index.php?s$1 last;这一行很多打赏站点访问首页正常但点开作品 404就是伪静态没生效。另外 PHP-FPM 监听地址不同宝塔默认是 unix socket需要改成unix:/tmp/php-cgi-74.sock具体看面板里 PHP 套接字路径。4.3 安装步骤与配置项说明常规安装流程是导入数据库、修改.env或config/database.php然后把站点指向 public 目录。# 假设源码放在 /www/wwwroot/site数据库文件在 install/site.sql cd /www/wwwroot/site mysql -uroot -p your_database install/site.sql # 修改数据库配置 vim .env.env里主要改hostname、database、username、password这几项开发时我会把debug临时打开能看到具体报错线上再关掉。如果源码支持安装向导直接访问你的域名/install按步骤填数据库信息和后台管理员账号就行。配置完成后进入后台确认几个开关配置项推荐值说明站点URL填绑定域名用于拼接支付回调地址支付商户ID易支付平台提供在后台基本设置中填写商户密钥易支付平台提供签名用不要泄露代理分佣比例0.10.3按预期控制成本域名备案状态真实填写部分支付渠道校验4.4 启动阶段最容易遇到的五类故障把部署时同行问得最多的错误列在下面表现根因解决首页能开打赏页404伪静态没配检查 rewrite 规则提交打赏一直转跳回调地址没配后台支付回调 URL数据库连接失败.env 未生效删 runtime 缓存安装向导无响应PHP fileinfo 未开开启文件扩展后台验证码不显示GD 扩展缺失安装 gd 扩展看到一个比较隐蔽的问题修改数据库配置后ThinkPHP 的 runtime 缓存会保留旧配置我一般直接清空runtime目录同时重启 PHP-FPM否则明明改了库名还是连不上旧的。另外如果支付回调地址是http而站点启用了https支付通知可能被拦截记得把回调地址改成 https 并保证该地址能公网访问。5. 进阶不抽象对账脚本抗漏单域名拦截防盗用最后落到两个我实际加过的细节上。第一个是支付通知漏单。易支付异步通知是网关主动推送但如果网关漏掉或延迟通知订单会一直停在“待支付”用户却已经扣款。我在源码里补了一个 cron 对账脚本每分钟跑一次。5.1 打赏订单对账脚本// cron/check.php 对账脚本 $orders db(orders) -where(status, 0) -where(create_time, , time() - 3600) -select(); foreach ($orders as $order) { // 调用易支付查单接口 $result $this-queryEasyPay($order[order_no]); if ($result[trade_status] TRADE_SUCCESS) { $this-completeOrder($order); } }用 crontab 每分钟执行一次*/1 * * * * php /www/wwwroot/site/cron/check.php。它的意义是把支付成功但通知没到的订单补救回来订单状态、代理佣金和作品解锁都会同步更新。对账脚本一定要做幂等更新订单前先判断 status因为回调和对账可能同时触发。5.2 Nginx 域名防洪与防盗链第二个是绑定域名防洪。别人拿到这套开源源码也可以部署但如果不校验域名会直接导致你的支付回调被他人的站点频繁调用。最简单的方式是在 Nginx 层面对非绑定域名返回 403server { listen 80; server_name your-domain.com; if ($host ! your-domain.com) { return 403; } # 限制单个IP并发防止恶意刷打赏接口 limit_req zoneperip burst5 nodelay; }配合图片和视频防盗链可以在资源文件 location 里加valid_referers none blocked your-domain.com;防止其他站点直接引用你的同源资源。这一层防御不算高深但对防止别人拿源码换个域名套用你的支付配置已经够用。对账和域名校验加完之后这套源码在真实环境里才算闭环。本文还有配套的精品资源点击获取