这是站长根据自身需求开发的简易的商城系统,集成支付宝扫码支付、授权密钥生成/验证/设备绑定等完整功能。同时作为小型的在线商店,适用于各类数字商品的授权销售。
RONGYAN.CC 授权支付与商城系统,集成支付宝扫码支付、授权密钥生成/验证/设备绑定、SMTP 邮件发送、下载令牌、商品套餐、商品详情页等完整功能。同时作为小型的在线商店运营,适用于各类数字商品的授权销售。
| 层级 | 技术 |
|---|---|
| 后端语言 | PHP 8.2 |
| 数据库 | MySQL 8.0.45 |
| Web 服务器 | Nginx 1.31 |
| 支付 | 支付宝 alipay.trade.precreate(扫码支付) |
| 邮件 | SMTP SSL/TLS(纯 Socket 自实现,无第三方库) |
| 前端 | 原生 HTML/CSS/JS,无框架 |
| 管理后台 | SPA 单页应用 |
首页 /index.php:
/product/{slug}.html(SEO 友好 URL)商品详情页 /product.php:
流程: 用户扫码 → 前端轮询 status.php → 异步 notify.php 创建订单 + 发邮件
pay.php:支持 plan_id 参数,按套餐显示价格名称;调用 alipay.trade.precreate 获取二维码;检测 UA 判断移动端展示深链;60 秒轮询倒计时notify.php:验证 RSA2 签名(UTF-8/GBK 双兼容),幂等性检查,事务创建订单+授权+下载令牌,发送双邮件(客户+管理员);读取 passback_params 中的 plan_id 传递给 createOrderFromPayment()status.php:查询 payment_records 返回 {paid: true/false}return.php:同步跳转页,显示授权密钥或购买凭证verify.php:双模式(license_key 优先,order_no 兼容旧版),首次激活设备绑定,IP 限流(10 次/小时)validate.php:离线签名验证,校验 HMAC-SHA256 签名,检查过期lib/ProductLicense.php:RSA 2048 密钥对生成/签名/验证;使用 substr($payload, -256) 提取签名(修复 RSA 二进制签名含 | 的 bug)download_id,使用 product_downloads 中的链接;否则使用商品默认 download_urlreadfile() 代理下载(不暴露真实路径)download_id)plan_id → 订单使用套餐价格和时长download_iddownloads_data JSON 批量保存uploads/YYYY/MM/ 目录products 表 slug 唯一索引/product/{slug}.htmltry_files + PHP 路由 实现(不是 Nginx rewrite),详情见下方"部署说明"12 个功能模块:
| 页面/功能 | 说明 |
|---|---|
| 数据看板 | 统计卡片 + 7 日趋势图 |
| 商品管理 | 增删改查,内联表单(非弹窗),套餐编辑,下载文件管理,图片上传 |
| 订单管理 | 列表/搜索/筛选,延长/撤销/删除/重置下载 |
| 授权管理 | 列表,启用/禁用/删除 |
| 支付设置 | 支付宝 AppID、RSA2 密钥、沙箱模式 |
| 支付记录 | 列表 + 删除 |
| 系统设置 | 站点标题、Logo、导航栏、Hero、SEO |
| 帐户设置 | 修改用户名/邮箱/头像/密码 |
| 邮件设置 | SMTP 配置 + 测试发送 |
| 操作日志 | 查询/按日期清除 |
0 2 * * * php /path/to/cron/expire-licenses.php
执行三项操作:
| 决策 | 说明 |
|---|---|
| RSA 签名(非 HMAC) | 防止授权密钥被伪造,即使数据库被拖 |
| 域名无关验证 | 不绑域名,授权通用 |
| 失败关闭 | 授权缺失/过期 → 前端 403,管理员始终可访问 |
| 开发/发布分离 | 开发环境在 key-api/,final/ 构建版添加授权检+混淆 |
| Tokenizer 混淆 | 避免正则替换误匹配 |
substr($payload, -256) 提取签名 |
修复 RSA 2048 二进制签名含 \| 导致截断的 bug |
licenses.license_key 改为 VARCHAR(500) |
适配 RSA 密钥长度 |
| Slug URL 走 PHP 路由 | 比 Nginx rewrite 简单:try_files $uri /index.php?$query_string + PHP 检测 /product/{slug}.html |
| inline 商品表单 | 不弹窗,内容直接嵌在页面中,自然填充右侧面板 |
| 套餐嵌入商品表单 | plans_data JSON 序列化到隐藏 input,随商品一起提交,无需独立 API |
| 下载文件 CRUD + 套餐引用 | 可复用,多个套餐可共用同一下载文件 |
RONGYAN.CC 授权支付与商城系统 https://rongyan.cc mail:me@rongyan.cc
导入 database.sql(需依次执行全部建表/改表语句),MySQL 8.0+。
复制 .env.example 为 .env,配置数据库连接、APP_URL、SIGN_SECRET。
这一步是关键,必须手动修改 Nginx 配置,而不是用宝塔的"伪静态"功能。
宝塔自带的伪静态功能会加在 location ~ [^/]\.php(/|$) 块之后,而 slug 路由需要先于 PHP 解析生效。具体的做法是:
找到网站设置 → Nginx 配置文件中 #REWRITE-END 注释的位置,在它下面添加以下规则:
location ~ \.html$ {
try_files $uri /index.php?$query_string;
}
为什么不能使用宝塔的"伪静态"(设置 → 伪静态):
/product/shop_system.html)会先被 PHP 解析块匹配(按 php 文件处理),导致 404#REWRITE-END 标记手动插入,确保 .html 先被 try_files 捕获验证方式:访问 https://lab.rongyan.cc/product/shop_system.html 不返回 404 即为生效。
# 宝塔自动生成的 PHP 解析块(保留不动)
location ~ [^/]\.php(/|$) {
# ... 宝塔自动内容 ...
}
# 手动添加:在 #REWRITE-END 下插入
location ~ \.html$ {
try_files $uri /index.php?$query_string;
}
0 2 * * * php /path/to/cron/expire-licenses.phpupdateProduct() 先保存 downloads(取新 ID 映射),再保存 plans(用映射后 download_id)loadDownloads() 和 addDownload()/removeDownload() 后调用 renderPlans() 同步更新api.php /plans/save 缺少 download_id 字段)return.php 改用 download_tokens 表判断,不再依赖 p.download_url/product/{slug}.htmlstrrpos 改为 substr($payload, -256))</div> 缺失 Bugis_licensed 字段区分授权类/非授权类商品error_log 轻量审计