开发文档
云验证 HTTP API · 支持网站后端、前端 JavaScript、桌面程序与 Android APK
接入流程
云验证平台通过一组简单的 HTTP JSON 接口,为你的网站或软件提供卡密验证能力。整体流程如下:
- 注册账号:在平台注册(若站点开启付费注册,需先完成支付)后登录控制台。
- 创建应用:每个网站 / APK 建议创建独立应用,获得专属 API Key 与 Secret。
- 生成卡密:在「卡密管理」中批量生成,设置每张卡密的可用次数与有效期。
- 接入代码:在你的代码中填入 API Key,将用户输入的卡密提交到验证接口。
- 验证成功即扣次:接口返回剩余次数,验证失败不扣次。
获取 API 凭证
登录后进入「我的应用」,创建应用即可看到以下信息:
| 名称 | 示例 | 用途 |
|---|---|---|
| API Key | pk_8f3a...c1d2 | 请求时标识你的应用,必填 |
| API Secret | sk_2b7e...9f00 | 计算请求签名(可选),不要写进网页前端 |
生成卡密
在「卡密管理」中可以为指定应用批量生成卡密,参数含义:
- 生成数量:单次 1 ~ 100 张。
- 每张可用次数:每成功验证一次扣减 1 次,次数用完后卡密自动失效。
- 有效期:从生成时间开始计算,0 表示永久有效。
- 备注:仅用于后台识别,例如「月卡」「渠道 A」。
卡密格式为 16 位字符,通常按 XXXX-XXXX-XXXX-XXXX 展示。接口同时兼容带与不带分隔符的写法。
验证接口
验证卡密并扣减一次使用次数。支持 application/json、表单或 Query 参数。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
key | string | 是 | 应用 API Key |
card | string | 是 | 用户输入的卡密,可带 - |
device | string | 条件必填 | 设备唯一标识。应用开启「设备绑定」后必须提供,且首次验证的标识会被绑定 |
sign | string | 否 | 请求签名,见下方「签名校验」 |
ts | int | 否 | Unix 时间戳,使用签名时必填 |
请求示例
curl -X POST https://cloud.spent.cc/api/verify.php \
-H 'Content-Type: application/json' \
-d '{"key":"pk_你的APIKey","card":"ABCD-EFGH-JKLM-NPQR","device":"device-001"}'成功时返回:
{
"ok": true,
"code": 0,
"msg": "验证成功,扣除 1 次",
"data": {
"card": "ABCD-EFGH-JKLM-NPQR",
"remaining": 9,
"used": 1,
"max_uses": 10,
"expires_at": null,
"bind_device": false,
"device": "device-001",
"server_time": "2026-01-01 12:00:00"
}
}返回格式
所有接口均返回 JSON,HTTP 状态码通常为 200,通过 ok 与 code 判断业务结果。
| 字段 | 类型 | 说明 |
|---|---|---|
ok | bool | 业务是否成功 |
code | int | 0 表示成功,其余为错误码 |
msg | string | 可直接展示给用户的提示 |
data.remaining | int | 本次验证后剩余可用次数 |
data.used | int | 已使用次数 |
data.max_uses | int | 总可用次数 |
data.expires_at | string|null | 到期时间,null 表示永久有效 |
错误码
| code | 含义 | 建议处理 |
|---|---|---|
| 0 | 成功 | — |
| 1001 | 缺少 API Key | 检查请求参数 key |
| 1002 | API Key 无效 | 检查是否复制完整或凭证是否被重置 |
| 1003 | 应用 / 账号已被禁用 | 联系平台管理员 |
| 1004 | 签名校验失败或时间戳过期 | 检查签名算法、Secret 与服务器时间 |
| 1005 | 缺少卡密参数 | 检查请求参数 card |
| 2001 | 卡密不存在或不属于该应用 | 确认卡密归属的应用与输入的卡密 |
| 2002 | 卡密已被禁用 | 提示用户联系管理员 |
| 2003 | 卡密已过期 | 提示用户续费 |
| 2004 | 卡密次数已用完 | 提示用户续费 |
| 2005 | 缺少设备标识 | 开启设备绑定后必须传 device |
| 2006 | 设备不匹配 | 卡密已绑定其他设备 |
| 5000 | 服务异常 | 稍后重试或联系管理员 |
查询接口
仅查询卡密状态与剩余次数,不会扣减次数,可用于「查询余额」等功能。
参数与验证接口一致。返回的 data.status 取值:
active:可用disabled:已被禁用expired:已过期used_up:次数已用完
{
"ok": true,
"code": 0,
"msg": "查询成功",
"data": {
"card": "ABCD-EFGH-JKLM-NPQR",
"status": "active",
"status_text": "可用",
"remaining": 9,
"used": 1,
"max_uses": 10,
"expires_at": null,
"bind_device": false,
"bound_device": null,
"server_time": "2026-01-01 12:00:00"
}
}签名校验(可选)
API Key 需要写入客户端,可能被他人获取。启用签名后,服务端会校验请求是否由持有 Secret 的一方发起:
sign = HMAC_SHA256("card|device|ts", API Secret),ts 为 Unix 时间戳,有效期 ±300 秒。
<?php
// 可选:为请求附加签名,防止他人拿到 API Key 后随意调用
$card = 'ABCD-EFGH-JKLM-NPQR'; // 卡密
$device = 'device-fingerprint'; // 设备标识
$ts = time(); // Unix 时间戳(±300 秒内有效)
// API Secret 来自后台「我的应用」页面
$sign = hash_hmac('sha256', $card . '|' . $device . '|' . $ts, $apiSecret);
$payload = [
'key' => $apiKey,
'card' => $card,
'device' => $device,
'ts' => $ts,
'sign' => $sign,
];请求时携带 sign 与 ts 两个参数即可,平台会自动校验。APK 场景仍建议把关键业务校验放在服务端,客户端签名只能提高门槛,无法绝对防止逆向。
示例代码
以下代码可直接复制使用,记得替换 API Key。接口地址已自动填充为你当前站点的地址。
适用于网站后端。建议将卡密校验放在服务端,验证通过后再放行内容。
<?php
// 服务端验证示例:建议在网站后端调用,避免 API Key 暴露给浏览器
const CLOUD_API_KEY = 'pk_请替换成你的APIKey';
const CLOUD_VERIFY_URL = 'https://cloud.spent.cc/api/verify.php';
/**
* 验证卡密(每次成功调用会扣减 1 次)
* @return array{ok:bool, code:int, msg:string, data?:array}
*/
function cloud_verify(string $card, string $device): array
{
$payload = json_encode([
'key' => CLOUD_API_KEY,
'card' => $card,
'device' => $device,
], JSON_UNESCAPED_UNICODE);
$ch = curl_init(CLOUD_VERIFY_URL);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 8,
]);
$body = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($body === false) {
return ['ok' => false, 'code' => -1, 'msg' => '网络请求失败: ' . $error];
}
$result = json_decode($body, true);
return is_array($result)
? $result
: ['ok' => false, 'code' => -1, 'msg' => '返回数据格式错误'];
}
// 使用示例:用户提交卡密后调用
$result = cloud_verify($_POST['card'] ?? '', $_SERVER['REMOTE_ADDR']);
if ($result['ok']) {
echo '验证成功,剩余 ' . $result['data']['remaining'] . ' 次';
} else {
echo '验证失败:' . $result['msg'];
}适用于网页前端演示。API Key 会暴露给浏览器,正式项目建议通过你自己的后端代理请求。
<form id="verify-form">
<input id="card-input" placeholder="请输入卡密">
<button type="submit">验证</button>
</form>
<p id="verify-result"></p>
<script>
const API_KEY = 'pk_请替换成你的APIKey';
const API_URL = 'https://cloud.spent.cc/api/verify.php';
// 生成并持久化一个浏览器设备标识(仅作示例,可按需替换)
function getDeviceId() {
let id = localStorage.getItem('cloud_device_id');
if (!id) {
id = 'web-' + (crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2));
localStorage.setItem('cloud_device_id', id);
}
return id;
}
document.getElementById('verify-form').addEventListener('submit', async (event) => {
event.preventDefault();
const resultEl = document.getElementById('verify-result');
resultEl.textContent = '验证中...';
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
key: API_KEY,
card: document.getElementById('card-input').value.trim(),
device: getDeviceId()
})
});
const result = await response.json();
resultEl.textContent = result.ok
? '验证成功,剩余 ' + result.data.remaining + ' 次'
: '验证失败(' + result.code + '):' + result.msg;
} catch (error) {
resultEl.textContent = '网络错误:' + error.message;
}
});
</script>适用于桌面工具、脚本或自动化程序,使用 Python 标准库,无需安装额外依赖。
"""云验证 Python 示例(仅使用标准库,无需额外依赖)"""
import json
import urllib.request
API_KEY = "pk_请替换成你的APIKey"
API_URL = "https://cloud.spent.cc/api/verify.php"
def verify_card(card: str, device: str) -> dict:
"""验证成功时平台会扣减 1 次,失败不会扣减"""
payload = json.dumps({
"key": API_KEY,
"card": card,
"device": device,
}).encode("utf-8")
request = urllib.request.Request(
API_URL,
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=8) as response:
return json.loads(response.read().decode("utf-8"))
except Exception as exc:
return {"ok": False, "code": -1, "msg": f"请求失败: {exc}"}
if __name__ == "__main__":
result = verify_card("ABCD-EFGH-JKLM-NPQR", "desktop-device-001")
if result.get("ok"):
print("验证成功,剩余", result["data"]["remaining"], "次")
else:
print("验证失败:", result.get("msg"))适用于 Android APK。请务必在子线程中调用,并声明 INTERNET 权限。
// Android 示例(API 21+)
// 请在 AndroidManifest.xml 中声明网络权限:
// <uses-permission android:name="android.permission.INTERNET" />
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public final class CloudVerify {
private static final String API_KEY = "pk_请替换成你的APIKey";
private static final String API_URL = "https://cloud.spent.cc/api/verify.php";
/** 必须在子线程调用,Android 禁止主线程发起网络请求 */
public static JSONObject verify(String card, String deviceId) throws Exception {
URL url = new URL(API_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
JSONObject payload = new JSONObject();
payload.put("key", API_KEY);
payload.put("card", card);
payload.put("device", deviceId);
try (OutputStream out = conn.getOutputStream()) {
out.write(payload.toString().getBytes(StandardCharsets.UTF_8));
}
int status = conn.getResponseCode();
BufferedReader reader = new BufferedReader(new InputStreamReader(
(status >= 200 && status < 300) ? conn.getInputStream() : conn.getErrorStream(),
StandardCharsets.UTF_8));
StringBuilder body = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
body.append(line);
}
reader.close();
conn.disconnect();
return new JSONObject(body.toString());
}
}
// 调用示例(子线程):
// String deviceId = Settings.Secure.getString(
// getContentResolver(), Settings.Secure.ANDROID_ID);
// JSONObject result = CloudVerify.verify(card, deviceId);
// if (result.optBoolean("ok")) {
// int remaining = result.getJSONObject("data").optInt("remaining");
// }下载示例文件
下载完整示例文件后,替换其中的 API Key 即可运行:
常见问题
验证失败会扣次数吗?
不会。只有验证成功时才会扣减 1 次。查询接口也不会扣次。
设备绑定是如何工作的?
开启后,卡密首次验证成功时会记录设备标识;之后同一张卡密在其它设备上验证会返回 2006。请尽量使用稳定的设备标识,例如 Android 的 ANDROID_ID 或浏览器持久化 ID。
卡密次数可以修改吗?
已生成的卡密不能直接修改次数,可以禁用后重新生成。次数与有效期在生成时确定。
一个应用可以给多个网站使用吗?
可以。同一个应用下的卡密可以在任何一处使用该 API Key 的代码中验证。不同业务建议创建不同应用,便于区分数据。
API Key 泄露了怎么办?
在「我的应用」中点击「重置凭证」,旧 Key 立即失效,然后更新你的代码。同时建议开启设备绑定,降低损失。
数据存储在哪里?
所有数据保存在你部署服务器上的 SQLite 数据库中(默认 data/cloudauth.db),不经过第三方服务器。