Go to file
2026-04-14 09:47:00 +08:00
api first commit 2026-04-14 09:47:00 +08:00
data first commit 2026-04-14 09:47:00 +08:00
lib first commit 2026-04-14 09:47:00 +08:00
scripts first commit 2026-04-14 09:47:00 +08:00
.gitignore first commit 2026-04-14 09:47:00 +08:00
.htaccess first commit 2026-04-14 09:47:00 +08:00
.user.ini first commit 2026-04-14 09:47:00 +08:00
404.html first commit 2026-04-14 09:47:00 +08:00
config.php first commit 2026-04-14 09:47:00 +08:00
index.html first commit 2026-04-14 09:47:00 +08:00
README.md first commit 2026-04-14 09:47:00 +08:00

sendcard PHP 后端

提供一个接口用来向外部服务拉取 token,并把外部回传的 data 备档写入 SQLite 数据库。

前置要求

  • PHP 8.x已使用 declare(strict_types=1)
  • curl 扩展
  • PDO_SQLITE 扩展
  • SQLite 写入权限(默认写入到 ./data/sendcard.sqlite
  • 如果你用 Apache需要启用 .htaccess(本项目已添加重写规则)

接口说明

1) 获取 token

GET /api/getcard?type=xianyu

请求参数:

  • type:必须等于 xianyu,否则会报错不执行外部请求

说明(避免和轮询脚本混淆):

  • 不需要配置 poll_cron_key,也不需要在 URL 里传 device_code / device_code_md5。浏览器只访问带 type=xianyu 的地址即可。
  • device_codedevice_code_md5external_base_url(例如 82.157.20.83:9091/.../getCredentials?...)都在服务端 config.php 里,由 getcard.php 在服务器上 用 cURL 去调外部接口;调用方看不到也不会传这些参数。此处对应 机器 A(即时 Web 请求)。
  • 定时脚本 getcard_poll.php 仍调同一 external_base_url,但使用 config['poll'] 里机器 Bdevice_code / device_code_md5,与机器 A 分离,便于在上游对 单设备或请求频次 有限制时分流(轮询与即时各走一套设备参数)。

成功响应HTTP 200响应体为纯文本,即 token 字符串本身,不是 JSON 包一层):

<外部返回的 data.token例如 JWT 整段>

错误响应:

  • 未带 type=xianyu
{"error":"Invalid request parameter","need_type":"xianyu"}
  • 外部接口返回异常/缺少数据:
{"error":"External response missing data"}

SQLite 备档

每次成功调用并拿到外部 data 后,会把外部返回的字段逐列拆开写入一行记录(并额外保存 raw_json)。

  • 数据库文件:./data/sendcard.sqlite
  • 表名:cursor_login_backups

表字段包含(部分):

  • external_msg, external_code
  • token, email, deviceCode, activationCode, status, createTime
  • raw_json:保存外部完整 JSON可用于排查

配置文件

config.php 里可配置:

  • external_base_urlgetcardgetcard_poll 共用的外部 getCredentials 基础地址
  • device_codedevice_code_md5机器 A,仅 getcard.php 使用
  • poll.device_codepoll.device_code_md5机器 B,仅 getcard_poll.php 使用(与 A 不同,用于上游限流/限设备时分流)
  • sqlite_pathSQLite 路径

部署路由Apache / Nginx

本项目包含 .htaccess(仅对 Apache 生效),用于把 /api/getcard 重写到 api/getcard.php

如果你部署在宝塔(通常是 Nginx建议直接访问

/api/getcard.php?type=xianyu

这样不依赖重写;如果你确实想保留 /api/getcard 形式,把你宝塔里“重写/伪静态”的设置截图或说明一下,我再给你对应的 Nginx 规则。

接口说明

本仓库里的 config.php 只负责把现成的字符串传给外部接口,没有生成逻辑;下面按你这条 device_code 的常见写法说明各段含义。

device_code 的三段(用 - 拼起来) 示例:

BFEBFBFF000A06A4 - 5210N57MP0004BXP00R2 - c84b8af03a6c40e888f72576a5f34a1f

段 典型来源Windows 上常见叫法) 第 1 段 BFEBFBFF000A06A4 CPU ProcessorIdwmic cpu get ProcessorId / WMI Win32_Processor.ProcessorId。BFEBFBFF 是 Intel 上很常见的 CPUID 特征前缀,后面一般是 family/model/stepping 等拼成的十六进制不是传统意义上的“CPU 出厂序列号”,但常被叫作 CPU 标识/CPU ID。 第 2 段 5210N57MP0004BXP00R2 多为 主板序列号Win32_BaseBoard.SerialNumber或 BIOS/机器厂商序列 一类 OEM 串号;也可能是别的固定硬件标识,取决于采集工具具体取哪一项。 第 3 段 c84b8af…32 位十六进制) 很像 去掉连字符的 UUID128 bit。常见对应是 Windows 机器 GUID注册表 HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid 去掉 -),也可能是程序自己生成的 GUID要看上游工具怎么定义。 所以你记的「CPU 序列 + 别的」大致对应CPU ProcessorId + 主板/机器类序列号 + 机器 UUID或同类唯一 ID中间用 - 连成一条 device_code。

device_code_md5 从形态看是 32 位小写十六进制,符合 MD5 的常见输出。 在本项目里没有对 device_code 做校验或计算;我用 PHP 对整条 device_code 做 md5(),结果不等于你配置里的 bc5613415689b537679ba22c2feae68c。 因此它可能是:对别的规范化字符串(大小写、是否含 -、段顺序等)做的 MD5或 带盐/由别的客户端算法 生成,需要以提供 device_code 的那套工具/文档为准。 若你需要和上游完全一致,建议对照当时生成 device_code 的程序或接口文档;若你手头有那套工具的源码或说明,发一段我可以帮你对齐 device_code_md5 的精确算法。

定时轮询 getcard_poll.php

getcard.php 调同一套外部接口、同一种返回数据;差别在于:getcard 是即时Webgetcard_poll 约每 5 分钟(计划任务 + 脚本内随机间隔)。因上游常对 单设备或请求频次 有限制,即时走机器 A(顶层 device_code / device_code_md5轮询走机器 Bconfig['poll'] 内同名键,值与 A 不同)。

  • getcard.php:带 type=xianyu不要 poll_cron_key,用机器 A 调外部并写 SQLite、回 token。
  • getcard_poll.php:命令行跑定时 不要 key;仅当用 HTTP 触发该脚本时配置 poll_cron_key?key=(见下文方式二)。用机器 B 调外部,并写 txt 日志(与 getcard 的 SQLite 备档可并存)。

已在项目里提供 api/getcard_poll.php:宝塔 计划任务每分钟 执行一次;脚本内未到点会 Skip;到点后按 约 5 分钟 ±3 分钟、不少于 4 分钟 再请求外部,结果追加到 data/getcard_poll_log.txt请务必将 poll 里机器 B 的 device_code / device_code_md5 填全,否则轮询脚本会因配置校验失败退出。

宝塔里怎么设「定时」(二选一)

方式一Shell 里直接跑 PHP不经过 Web

  1. 打开 计划任务Shell 脚本(或同类「执行脚本」)。
  2. 执行周期选 每 1 分钟(要足够密;未到点脚本会 Skip 退出,不会打外部接口)。
  3. 脚本内容把路径换成你站点实际路径,例如:
/usr/bin/php /www/wwwroot/你的域名/sendcard.yunzer.cn/api/getcard_poll.php >>/www/wwwroot/你的域名/sendcard.yunzer.cn/data/getcard_poll_cron.log 2>&1
  • 若不确定 PHP 路径:宝塔 软件商店 → 已安装的 PHP设置命令行版本,常见为 /www/server/php/82/bin/php(版本号按你的来)。

方式二:调你自己站点上的 URL走 HTTP等价于「调接口」

可以,不必在 Shell 里写 php 路径。先在 config.php 里配置 poll_cron_key(一长串随机密钥,勿泄露),则允许通过 Web 访问:

https://你的域名/api/getcard_poll.php?key=你配置的密钥

然后任选其一:

  • 宝塔计划任务选 访问 URL(若有),每分钟访问上述地址;或
  • Shell 计划任务里每分钟执行:curl -fsS 'https://你的域名/api/getcard_poll.php?key=你的密钥' >>/path/to/curl.log 2>&1

注意: key 会出现在 Web 服务器访问日志 里,且可能被中间代理记录;更稳妥仍是 方式一 CLI。若用 Nginx 且未配置重写,请用带 .php 的路径:/api/getcard_poll.php?key=...

写入的文件

  • data/getcard_poll_state.json:下次允许执行的时间(含文件锁,避免并发重复跑)
  • data/getcard_poll_log.txt:每次拉取的一行日志(时间 + HTTP 状态 + 整段 JSON失败则写 ERROR ...

间隔规则(脚本内已实现)

  • 本次执行后,下次间隔秒数:max(240, 300 + random_int(-180, 180)),即 48 分钟 之间随机。

若你希望轮询成功时也像 getcard.php 一样写入 SQLite可以再说一下我可以把那段插入逻辑复用到 getcard_poll.php 里。