没有匹配的接口
没有内容匹配
带上应用的 X-App-Secret 请求头,向 /api/auth/* POST JSON 即可验证密钥、维持会话。用 Bearer 令牌通过 /api/admin/* 生成密钥。响应固定为 { "success": true|false, … }。
简介
PWF Auth API 按 REST 组织。所有请求与响应均为 HTTPS 上的 JSON。接口分为三族:
- 客户端 API(
/api/auth/*、/api/update/*)— 由你发布的应用携带应用密钥调用。 - 管理 API(
/api/admin/*)— 密钥与应用管理,使用 Bearer 令牌认证。 - 客户 / 代理商 API(
/api/customer/*、/api/reseller/*)— 自助门户。
下文所有路径均相对于该基础 URL。所有接口都要求 HTTPS;纯 HTTP 请求会被重定向。
Prefer machines? The whole surface is described in openapi.json (OpenAPI 3.1) — import it into Postman, Insomnia, or a code generator.
API and platform changes are announced in the changelog — breaking changes are always flagged there first.
身份认证
客户端接口 — App Secret
在 X-App-Secret 请求头中发送你应用的密钥。可在控制台 → 你的应用中找到。该密钥用于标识请求属于哪个应用。
curl https://pwfauth.com/api/auth/check-key.php \
-H "X-App-Secret: 9f02532d9c60… # 64-char hex, from your dashboard" \
-H "Content-Type: application/json" \
-d '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}'
管理接口 — Bearer 令牌
先通过 /api/admin/login.php 认证获取 JWT,之后在每个管理调用中以 Authorization: Bearer <token> 发送。
TOKEN=$(curl -s https://pwfauth.com/api/admin/login.php \
-d '{"username":"admin","password":"…"}' | jq -r .token)
curl https://pwfauth.com/api/admin/keys.php \
-H "Authorization: Bearer $TOKEN"
加密信封
SDK 级接口以 AES-256-CBC + HMAC-SHA256 信封替代纯 JSON。线上格式为 {"p": base64(IV || ciphertext), "t": unix_timestamp, "s": hmac_sha256_hex(p + t)} — 加密密钥与 MAC 密钥均由你的应用密钥派生,无需额外密钥交换。
{
"p": "aXYxNmJ5dGVzY2lwaGVydGV4dA…", // base64( IV || AES-256-CBC ciphertext )
"t": 1785671000, // unix timestamp (±300s accepted)
"s": "b7fd09…" // hmac_sha256_hex( p + t, mac_key )
}
标注为App secret + 加密信封的接口要求请求使用信封,且响应也加密。check-key.php 会自动识别:发纯 JSON 就回纯 JSON(本页的 curl 示例因此可用);发信封则响应加密。
超过 ±300 秒的信封会被拒绝,请保持客户端时钟基本准确。你完全不用手写这些:开源示例客户端(VB.NET、C#、Python)自带现成的 CryptoEnvelope 实现。
How the envelope is built
Both directions use the same recipe. Two keys are derived from your app secret, so every app gets distinct encryption and signing keys:
enc_key = SHA256("enc:" + app_secret) # 32 bytes
mac_key = SHA256("mac:" + app_secret) # 32 bytes
# client → server (and server → client — same recipe)
iv = random_bytes(16)
ct = AES-256-CBC(enc_key, iv, json_body) # PKCS#7 padding
p = base64(iv || ct)
t = unix_time() # ±300 s accepted
s = hex(HMAC-SHA256(p + str(t), mac_key))
send { "p": p, "t": t, "s": s }
# receiving: verify s FIRST (constant-time), check |now - t| ≤ 300,
# then base64-decode p, split off the 16-byte IV, decrypt the rest.
Which endpoints require it
Each endpoint card carries a colour-coded auth pill; this table is the same information at a glance:
| Auth model | Endpoints |
|---|---|
| 公开 | app/changelog · app/pricing · admin/login |
| App secret · 纯文本或信封 | auth/check-key |
| App Secret | auth/trial · auth/request-hwid-reset · auth/account-register · auth/account-login · auth/change-password · update/validate · update/download |
| App secret + 加密信封 | auth/login · auth/heartbeat · auth/logout · app/text · app/slides · app/info · app/social-click · update/check |
| Bearer | admin/* |
| Fulfillment secret | app/fulfill |
响应与错误
每个响应都带有布尔值 success。出错时额外返回给人看的 message 和稳定的机器可读 error_code:
{
"success": false,
"message": "This license key has been banned.",
"error_code": "BANNED"
}
| error_code | 含义 |
|---|---|
MISSING_FIELDS | 缺少必填的报文字段。 |
INVALID_KEY | 该应用下不存在此许可证密钥。 |
INVALID_CREDENTIALS | 用户名/密码组合被拒绝(用户账户 + 管理登录)。 |
HWID_MISMATCH | 密钥已绑定到其他设备。 |
EXPIRED | 许可证已过期。 |
BANNED / PAUSED | 密钥已被所有者封禁或暂停。 |
MAINTENANCE | 应用处于维护模式。 |
SESSION_EXPIRED / SESSION_MISMATCH | 心跳会话无效。 |
TOO_MANY_ATTEMPTS | Login brute-force lockout — the account is locked temporarily; retry_after in the response says for how many seconds. |
TRIAL_DISABLED / TRIAL_USED / TRIAL_LIMIT | 试用创建被拒 — 该应用已禁用试用、此设备已用过,或达到单 IP 上限。 |
HTTP 状态码
| error_code | 含义 |
|---|---|
200 | 成功 — 结果在 JSON 报文中。 |
400 | 校验失败 — 字段缺失或格式错误(见 error_code)。 |
401 | 缺失或无效的 X-App-Secret / Bearer 令牌。 |
403 | 已认证但无权限(应用不对、令牌已吊销……)。 |
404 | 资源不存在。 |
405 | 该接口不支持此 HTTP 方法。 |
429 | Rate limited — wait for the Retry-After header before retrying. |
500 | 服务器错误 — 提示信息刻意保持笼统;请稍后重试。 |
限流与合理使用
没有一刀切的请求配额,但以下防滥用机制处于启用状态:
- 管理登录有暴力破解防护 — 连续失败会临时锁定账户。
- 试用按设备(HWID)和 IP 设有上限;超出返回
TRIAL_LIMIT。 - 密钥重置按应用限流,防止意外的轮换死循环。
- 心跳请保持在登录响应给出的间隔(默认 30 秒)— 打得更快没有任何收益。
快速上手
最小集成:应用启动时验证密钥,之后靠心跳维持会话。
# Validate a key from the terminal (plain JSON works here)
curl -X POST https://pwfauth.com/api/auth/check-key.php \
-H "X-App-Secret: $APP_SECRET" \
-d '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}'
# → { "success": true, "valid": true, "key": { "status": "active", … } }
注意:完整会话流程(login → heartbeat → logout)使用加密信封,无法直接用 curl 手调。用面板内的 Quick Start(以你的真实凭证生成 9 种语言的可用代码)或开源示例客户端,几分钟即可接通。
The same flow with an official SDK
Login, heartbeat and the server-side kill switch in a few lines — the SDK handles the encrypted envelope for you:
// dotnet add package PWFAuth
using PWFAuth;
var client = new PwfClient(APP_SECRET);
client.SessionEnded += (s, e) => // ban / pause / expiry / revoke / offline
{
Console.WriteLine($"{e.ErrorCode}: {e.Message}");
Environment.Exit(0);
};
var login = await client.LoginAsync("XXXXX-XXXXX-XXXXX-XXXXX");
if (!login.Success) { Console.WriteLine(login.Message); return; }
client.StartHeartbeat(); // keeps the session alive AND enforces the kill switch
' dotnet add package PWFAuth
Dim client As New PwfClient(APP_SECRET)
AddHandler client.SessionEnded, Sub(s, e) ' ban / pause / expiry / revoke / offline
MessageBox.Show(e.Message)
Application.Exit()
End Sub
Dim login = Await client.LoginAsync("XXXXX-XXXXX-XXXXX-XXXXX")
If login.Success Then client.StartHeartbeat()
# pip install pwfauth
import sys
from pwfauth import PwfClient
client = PwfClient(APP_SECRET)
client.on_session_ended = lambda code, msg: sys.exit(msg) # kill switch
login = client.login("XXXXX-XXXXX-XXXXX-XXXXX")
if not login.success:
sys.exit(login.message)
client.start_heartbeat() # keeps the session alive AND enforces the kill switch
// npm install pwfauth
import { PwfClient } from 'pwfauth';
const client = new PwfClient(process.env.PWFAUTH_SECRET);
client.on('sessionEnded', ({ errorCode, message }) => { // kill switch
console.error(`${errorCode}: ${message}`);
process.exit(1);
});
const login = await client.login('XXXXX-XXXXX-XXXXX-XXXXX');
if (!login.success) { console.error(login.message); process.exit(1); }
client.startHeartbeat(); // keeps the session alive AND enforces the kill switch
许可证与会话
首次使用时激活密钥、绑定设备 HWID 并开启会话。后续登录会重新验证并开启新会话。通过淘汰最旧会话来强制执行 max_devices。
| 报文字段 | 必填 | 说明 |
|---|---|---|
license_key | 必填 | 要激活 / 验证的许可证密钥。 |
hwid | 必填 | 设备的稳定硬件指纹。 |
{
"success": true,
"session_id": "8f2c…",
"user": {
"license_key": "XXXXX-XXXXX-XXXXX-XXXXX",
"key_type": "days", "duration": 30,
"expires_at": "2026-09-01T12:00:00Z",
"days_remaining": 30, "status": "active"
},
"features": { "pro_tier": true },
"heartbeat_interval": 30
}
维持会话活跃。按 login 返回的 heartbeat_interval 秒定时调用。停止调用后会话将被服务端回收。
| 报文字段 | 必填 | 说明 |
|---|---|---|
session_id | 必填 | login 返回的会话。 |
license_key | 可选 | 若发送,会话必须属于该密钥。 |
{ "success": true, "message": "Heartbeat received" }
// the moment the key is banned / paused / expired / reset, the SAME call answers:
{ "success": false, "error_code": "BANNED",
"message": "This license key has been banned." } // session is dropped — log the user out
立即关闭会话(如应用退出时)。报文:session_id 必填、license_key 可选。
轻量状态查询 — 不创建会话、不绑定 HWID。适合做"这个密钥还有效吗?"的检查。
| 报文字段 | 必填 | 说明 |
|---|---|---|
license_key | 必填 | 要查询的密钥。 |
curl -X POST https://pwfauth.com/api/auth/check-key.php \
-H "X-App-Secret: $APP_SECRET" \
-d '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}'
var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-App-Secret", APP_SECRET);
var res = await http.PostAsync("https://pwfauth.com/api/auth/check-key.php",
new StringContent("{\"license_key\":\"XXXXX-XXXXX-XXXXX-XXXXX\"}"));
import urllib.request, json
req = urllib.request.Request("https://pwfauth.com/api/auth/check-key.php",
data=json.dumps({"license_key": "XXXXX-XXXXX-XXXXX-XXXXX"}).encode(),
headers={"X-App-Secret": APP_SECRET})
print(json.load(urllib.request.urlopen(req)))
const res = await fetch('https://pwfauth.com/api/auth/check-key.php', {
method: 'POST',
headers: { 'X-App-Secret': process.env.APP_SECRET },
body: JSON.stringify({ license_key: 'XXXXX-XXXXX-XXXXX-XXXXX' })
});
console.log(await res.json());
$ch = curl_init('https://pwfauth.com/api/auth/check-key.php');
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-App-Secret: ' . $APP_SECRET],
CURLOPT_POSTFIELDS => '{"license_key":"XXXXX-XXXXX-XXXXX-XXXXX"}']);
$res = json_decode(curl_exec($ch), true);
{
"success": true, "valid": true,
"key": {
"status": "active", "key_type": "days", "duration": 30,
"activated_at": "2026-08-02T12:00:00Z",
"expires_at": "2026-09-01T12:00:00Z",
"days_remaining": 30, "max_devices": 1
}
}
轻量许可证验证,不绑定 HWID、不建会话 — 适合完整登录前的预检。纯 JSON 进,纯 JSON 出。
| 报文字段 | 必填 | 说明 |
|---|---|---|
license_key | 必填 | 要查询的密钥。 |
用户更换机器时,从你的应用内对话框提交 HWID 重置申请。应用所有者在控制台批准。
| 报文字段 | 必填 | 说明 |
|---|---|---|
license_key or username | 必填 | 标识要重置谁的绑定。 |
reason | 可选 | 展示给所有者的备注。 |
免费试用
签发绑定到请求设备的限时试用密钥。每个应用每个 HWID 仅一次试用。试用时长由应用所有者配置。
| 报文字段 | 必填 | 说明 |
|---|---|---|
hwid | 必填 | 请求试用的设备指纹。 |
{
"success": true, "message": "Trial activated! You have 3 days.",
"trial_key": "TRIAL-1A2B3-C4D5E-F6A7B",
"session_id": "8f2c…",
"expires_at": "2026-08-23T12:00:00Z", "days_remaining": 3,
"user": { "key_type": "days", "status": "active", … },
"app": { "name": "…", "version": "…" }
}
用户账户
比起裸密钥更喜欢用户名/密码登录?PWF Auth 自带完整账户体系,含 bcrypt 哈希与 HWID 绑定。
创建终端用户账户。报文:username、password 必填、email 可选。
| 报文字段 | 必填 | 说明 |
|---|---|---|
username | 必填 | 期望的用户名(每个应用内唯一)。 |
password | 必填 | 密码(服务端哈希存储)。 |
email | 可选 | 账户联系邮箱。 |
认证并绑定设备。报文:username、password、hwid 必填。返回与 login 相同的会话结构。
| 报文字段 | 必填 | 说明 |
|---|---|---|
username | 必填 | 账户用户名。 |
password | 必填 | 账户密码。 |
hwid | 必填 | 会话要绑定的设备指纹。 |
更换用户密码。报文:username、current_password、new_password 必填。
| 报文字段 | 必填 | 说明 |
|---|---|---|
username | 必填 | 账户用户名。 |
current_password | 必填 | 当前密码(更改前先校验)。 |
new_password | 必填 | 新密码。 |
应用内容与远程控制
这些接口让你发布的应用拉取你在面板中实时编辑的内容 — 文案、轮播、定价、更新日志 — 无需发新版本。
获取远程文案。带 ?name=… 返回单条;不带则返回该应用全部文案。按密钥的覆盖优先于应用默认值。许可证密钥放在 Authorization: Bearer
| 报文字段 | 必填 | 说明 |
|---|---|---|
?name | 可选 | 要获取的文案键名(留空取全部)。 |
返回应用当前启用的公告轮播。发送信封报文 {"action":"get_slides"};响应加密。
应用元数据 + 社交链接(名称、品牌、URL)。信封响应。
为某个社交链接的点击计数 +1(用于面板的互动统计)。
| 报文字段 | 必填 | 说明 |
|---|---|---|
link_id | 必填 | 社交链接的数字 id。 |
单个应用的公开免认证更新日志源 — 在任何地方渲染"新版本亮点"组件。仅返回已发布条目。
| 报文字段 | 必填 | 说明 |
|---|---|---|
?app_id | 必填 | 要读取更新日志的应用。 |
?limit | 可选 | 1–50,默认 20。 |
?since | 可选 | ISO 日期 — 仅返回该日期及之后发布的条目。 |
?category | 可选 | 筛选:new / improved / fixed / removed / security / deprecated。 |
公开定价面:应用的订阅档位与旧版套餐;POST 创建购买订单;?action=check 查询用户的有效订阅。
| 报文字段 | 必填 | 说明 |
|---|---|---|
?app_id | 必填 | 要读取定价的应用。 |
?type | 可选 | levels 或 plans(留空返回两者)。 |
面向自动化支付网关的服务器间订单履约 — 经验证的 IPN 完成订单并触发密钥生成与交付。以面板设置中的履约共享密钥认证,绝不使用用户会话。
| 报文字段 | 必填 | 说明 |
|---|---|---|
order_id | 必填 | 要履约的订单(ORD-…)。 |
secret | 必填 | 你的 order_fulfill_secret。 |
payment_ref | 可选 | 可选的网关支付参考号。 |
OTA 更新
询问调用方是否有更新版本可用 — 遵循渠道、系统/架构过滤、最低版本门槛与灰度发布(同一调用方稳定分桶)。
| 报文字段 | 必填 | 说明 |
|---|---|---|
v | 必填 | 调用方当前版本(semver)。 |
channel | 可选 | stable(默认)、beta 或 alpha。 |
os / arch | 可选 | windows·macos·linux / x64·x86·arm64。 |
hwid / license_key | 可选 | 用于灰度发布的稳定分桶。 |
{
"success": true, "update_available": true,
"update": {
"version": "1.4.0", "channel": "stable",
"is_mandatory": false, "file_size": 5242880,
"sha256": "…", "changelog": "…",
"download_url": "api/update/download.php?v=1.4.0"
}
}
下载指定版本的安装包。使用 check.php 返回的 download_url,下载后校验 sha256。
Webhooks
Turn events into HTTP calls to your server: key activations, bans, expiries, orders, new updates and more. Configure destinations per app (or for all apps) in the panel under Application → Webhooks, pick the events you care about, and PWF Auth POSTs a signed JSON body to your URL. Delivery is queue-based — expect it within a minute or two of the event, not in the same instant.
Delivery payload
{
"event": "key.activated",
"timestamp": "2026-08-20T14:07:02Z",
"data": {
"app_id": "01af6643-…",
"license_key": "XXXXX-XXXXX-XXXXX-XXXXX",
"hwid": "A1B2C3…"
}
}
Delivery headers
| Header | 说明 |
|---|---|
X-Webhook-Id | Unique delivery id (whd_…). Stays the same across retries of one event — use it to deduplicate. |
X-Webhook-Timestamp | Unix timestamp of the delivery attempt. |
X-Webhook-Attempt | 1-based attempt counter. |
X-Webhook-Signature | Legacy signature: hex(HMAC-SHA256(body, secret)). |
X-Webhook-Signature-V2 | Preferred signature: t=<ts>,v1=hex(HMAC-SHA256(ts + "." + body, secret)) — the timestamp inside the MAC makes replays detectable. |
Verifying the signature
Always verify before trusting a delivery: compute the V2 MAC over timestamp + "." + raw body with your endpoint secret, compare in constant time, and reject stale timestamps (±5 minutes is plenty). Answer 2xx fast — heavy work belongs in your own queue.
// X-Webhook-Signature-V2: t=<ts>,v1=<hex>
[$t, $v1] = explode(',', $_SERVER['HTTP_X_WEBHOOK_SIGNATURE_V2'] ?? ',');
$t = substr($t, 2); $v1 = substr($v1, 3);
$raw = file_get_contents('php://input');
$ok = hash_equals(hash_hmac('sha256', $t . '.' . $raw, $secret), $v1)
&& abs(time() - (int)$t) < 300;
if (!$ok) { http_response_code(400); exit; }
http_response_code(200); // answer fast — queue heavy work
// X-Webhook-Signature-V2: t=<ts>,v1=<hex>
const [t, v1] = req.headers['x-webhook-signature-v2']
.split(',').map(kv => kv.split('=')[1]);
const mac = crypto.createHmac('sha256', secret)
.update(`${t}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1))
&& Math.abs(Date.now() / 1000 - Number(t)) < 300;
res.sendStatus(ok ? 200 : 400); // answer fast — queue heavy work
# X-Webhook-Signature-V2: t=<ts>,v1=<hex>
import hmac, time
t, v1 = (kv.split("=", 1)[1]
for kv in request.headers["X-Webhook-Signature-V2"].split(","))
mac = hmac.new(secret, f"{t}.{raw_body}".encode(), "sha256").hexdigest()
ok = hmac.compare_digest(mac, v1) and abs(time.time() - int(t)) < 300
return ("", 200) if ok else ("", 400) # answer fast — queue heavy work
Retries & failure policy
- A delivery counts as successful on any
2xx/3xxanswered within 5 seconds. - Failures retry up to 5 attempts with widening gaps: 1 m → 5 m → 30 m → 2 h → 6 h.
X-Webhook-Idis minted once per event and reused on every retry — idempotency is oneseen?lookup away.- A destination that fails 20 times in a row is paused automatically; re-enable it from the panel (a
testevent button is right there).
Events
Subscribe to specific events or leave the filter empty to receive them all. event in the body tells you which one fired:
| Event | Fires when |
|---|---|
key.created | Keys are generated (panel or admin API). |
key.activated | A key's first successful login binds it to a device. |
key.banned / key.unbanned | A key is banned / unbanned. |
key.deleted | A key is deleted. |
key.rotated | A key string is rotated (the old string stops working). |
key.suspected_leak | Leak detection flags a key as suspected shared/leaked. |
key_expiring | A key approaches expiry (scheduled sweep). |
key_expired | A key expires (scheduled sweep). |
trial.created | A trial key is issued to a new device. |
hwid_reset.requested | A client requests a device (HWID) reset. |
new_order | A purchase order is created. |
order.approved | An order is approved and fulfilled. |
subscription.* | Subscription lifecycle: created / renewed / cancelled / renewal order created. |
update.published | A new application version is published. |
changelog.published | A changelog entry goes live. |
app.revoked | An application is revoked / disabled. |
suspicious_activity | The suspicious-activity heuristic fires. |
test | Manual test delivery from the panel. |
管理 API
用于管理应用与密钥的服务端接口。全部需要下方登录接口签发的 Authorization: Bearer 令牌。
不想把密码写进脚本?在面板 → 个人资料 → API 令牌中创建一个个人访问令牌,作为 Bearer 值使用(pwf_…)。除非你主动吊销否则永不过期,适用于所有 /api/admin/* 接口,并且按设计无法进行凭证管理(密码、邮箱、2FA、创建更多令牌)。
用凭证换取 JWT。若启用了 2FA,返回 requires_2fa + step1_token,需通过 /api/admin/totp.php 完成。
| 报文字段 | 必填 | 说明 |
|---|---|---|
username | 必填 | 管理员用户名。 |
password | 必填 | 管理员密码。 |
批量生成许可证密钥。同一接口还支持 GET(列表)、PUT(编辑)、DELETE,以及 ?action=ban|pause|extend|reset-hwid、?action=export 等操作查询。
curl -X POST https://pwfauth.com/api/admin/keys.php \
-H "Authorization: Bearer $TOKEN" \
-d '{"app_id":"01af6643-cf3a-4280-b9ce-cb5fa8ae987c",
"count":100,"key_type":"days","duration_value":30}'
管理面还覆盖 apps、sessions、updates、feature-flags、webhooks、analytics、audit、resellers 等 — 均在 /api/admin/ 下,使用同样的 Bearer 认证。登录控制台即可探索。
SDK
任何能发 HTTP 请求的环境都能调用 REST API。VB.NET / C# 提供官方单文件 SDK,Python 与 PHP 有一流示例。其余一切 — Node、Go、Rust、Java、Swift — 都直接调用上文的同一批 JSON 接口。