API 接入文档
双零互联——积分通行证 为第三方站点提供统一账号登陆 / 注册 / 积分服务。全部接口采用跳转授权安全模式(类 OAuth 2.0):用户的登陆密码与支付密码只在 双零互联——积分通行证 页面输入,任何情况下都不会经过您的站点、也不会出现在任何 API 请求中,从根本上杜绝第三方站点(无论善意还是恶意)获取用户密码的可能。
1. 接入概述与安全模型
您的站点需要用到三类能力:登陆 / 注册 / 支付扣积分(需要用户输入密码的,走浏览器跳转);积分增加 / 积分查询 / 资料查询(无需用户参与,服务端直连调用)。
安全模型一句话:凡是需要密码的动作,都把用户浏览器重定向到 双零互联——积分通行证,用户在 双零互联——积分通行证 页面输入密码完成操作后,浏览器再带着一张一次性票据(ticket)回到您的回调地址,您的服务端用 ticket 向 API 换取结果。您的站点全程接触不到任何密码。
完整时序
state 存入会话,把浏览器重定向到 https://ddd.dedos.top/oauth.php(携带 apikey / redirect_uri / state,支付另带 amount / out_trade_no)。redirect_uri,附加参数 ?ticket=64位票据&state=原样回传。state(防伪造),再用 ticket 调 POST https://ddd.dedos.top/api.php,action=oauth_ticket 换取用户信息或支付结果。out_trade_no 幂等发货。接口总览
| 接口 | 方式 | 用户参与 | 用途 |
|---|---|---|---|
https://ddd.dedos.top/oauth.php?action=login | 浏览器跳转 | ✅ 需要跳转 | 用户在通行证输入密码完成登陆/授权 |
https://ddd.dedos.top/oauth.php?action=register | 浏览器跳转 | ✅ 需要跳转 | 用户在通行证注册新账号并授权 |
https://ddd.dedos.top/oauth.php?action=pay | 浏览器跳转 | ✅ 需要跳转 | 扣除用户积分(用户输入支付密码) |
action=oauth_ticket | POST https://ddd.dedos.top/api.php | ❌ 服务端 | 用一次性 ticket 换取用户信息 / 支付结果(核心) |
action=points_inc | POST https://ddd.dedos.top/api.php | ❌ 服务端 | 给用户增加积分 |
action=points | POST https://ddd.dedos.top/api.php | ❌ 服务端 | 查询积分余额及月/年统计(需用户授权,限速) |
action=profile | POST https://ddd.dedos.top/api.php | ❌ 服务端 | 查询用户必要公开资料(隐私最小化,需用户授权,限速) |
⚠ 已停用接口:旧版的 login / register / points_dec(由第三方站点代收用户密码再转发给 API 的模式)已全面停用,调用将返回 code=12 并附迁移指引。这是为了防止第三方站点恶意收集用户密码。
2. 快速开始(5 步接入)
https://ddd.dedos.top/index.php 注册一个账号(站长本人)。https://ddd.dedos.top/kf.php)填写站点域名 / 名称 / 关键词提交申请,等待管理员审核。https://ddd.dedos.top/kf.php?mod=dev)勾选需要的功能,一键生成完整可运行的 PHP 演示代码,上传到您的站点即可测试。域名校验提醒:API 通讯与跳转回调都会严格校验域名 —— domain 参数和 redirect_uri 的域名必须与站点登记域名完全一致(自动忽略 www. 前缀与协议,但主域名必须相同),否则通讯被拒绝。更换域名需在开放平台修改并重新审核。
3. 跳转授权页 oauth.php
地址:https://ddd.dedos.top/oauth.php。把用户浏览器重定向(302)到该地址并携带下表参数;用户在通行证完成操作后,浏览器会被重定向回您的 redirect_uri 并附加 ticket 与 state 参数。
3.1 登陆 / 注册跳转
跳转地址格式:
https://ddd.dedos.top/oauth.php?action=login&apikey=您的APIKEY&redirect_uri=https%3A%2F%2Fwww.yoursite.com%2Fpp-callback.php&state=随机32位串| 参数 | 必填 | 说明 |
|---|---|---|
action | 必填 | login(登陆)或 register(注册)。两者都会在未登陆时展示登陆/注册表单,已登陆时直接展示授权确认页 |
apikey | 必填 | 您站点的 APIKEY |
redirect_uri | 必填 | 完成后的回调地址,必须是完整的 http(s):// 地址,域名须与站点登记域名一致 |
state | 强烈建议 | 随机字符串,回调时原样回传。用于防跨站请求伪造(CSRF):发起时存会话,回调时校验一致 |
用户流程:未登陆通行证 → 输入账号密码(或切换注册)→ 显示「授权确认」页 → 点击「同意授权」→ 重定向回 redirect_uri?ticket=...&state=...。
3.2 支付(扣积分)跳转
跳转地址格式:
https://ddd.dedos.top/oauth.php?action=pay&apikey=您的APIKEY&redirect_uri=https%3A%2F%2Fwww.yoursite.com%2Fpp-callback.php&amount=100&out_trade_no=PAY20260904001&description=%E4%BC%9A%E5%91%98%E6%9C%88%E5%8D%A1&state=随机32位串| 参数 | 必填 | 说明 |
|---|---|---|
action | 必填 | 固定为 pay |
apikey | 必填 | 您站点的 APIKEY |
redirect_uri | 必填 | 支付完成后的回调地址,域名须与登记一致 |
amount | 必填 | 扣除的积分数量,正整数,最大 10000000 |
out_trade_no | 必填 | 您的订单号,站点内唯一。同一订单号重复发起支付不会重复扣款(幂等),直接返回上次结果 |
description | 选填 | 商品/订单说明,展示在支付确认页 |
state | 强烈建议 | 随机字符串,回调时原样回传 |
用户流程:未登陆通行证 → 先登陆 → 显示支付确认页(金额 / 订单号 / 支付后余额)→ 输入支付密码 → 扣款 → 重定向回 redirect_uri?ticket=...&state=...。
支付安全机制:支付密码连续错误 5 次将锁定 15 分钟;余额不足 / 风控拦截会以失败票据返回具体原因;用户尚未设置支付密码会引导其先到通行证设置。
3.3 回调与票据
用户在通行证完成(或取消)操作后,浏览器被重定向回您的 redirect_uri:
https://www.yoursite.com/pp-callback.php?ticket=64位十六进制一次性票据&state=您发起时传入的state- ticket:64 位十六进制一次性票据,有效期 10 分钟,与您的站点(apikey)绑定,其他站点无法使用。
- 幂等重试:票据首次交换成功后 10 分钟内重复交换会返回同一结果(方便网络重试),之后作废。
- state:您发起时传入的字符串原样回传,务必校验其与发起时存入会话的值一致,防止伪造回调。
- 取消:用户在通行证点击「取消」也会正常回调,此时换取结果返回
code=14(用户取消)。 - 回调地址只承载 ticket 交换,不要在 URL 中传递订单金额等敏感信息(金额以您本地订单为准)。
拿到 ticket 后,由您的服务端调用 action=oauth_ticket 交换结果(见 4.2 节)。
4. 服务端接口 api.php
地址:POST https://ddd.dedos.top/api.php。请求体支持 JSON(推荐,Content-Type: application/json)或表单(application/x-www-form-urlencoded)。返回统一为 JSON:{"code": 0, "msg": "ok", "data": {...}}。
4.1 公共参数与鉴权
| 参数 | 必填 | 说明 |
|---|---|---|
apikey | 必填 | 站点 APIKEY。无效、待审核、被拒绝、被封禁均返回 code=2。APIKEY 可随时在「开放平台 → 站点总览」重新生成(旧 KEY 立即失效) |
domain | 必填 | 您站点的域名,须与登记域名一致(忽略 www 前缀),否则返回 code=3 |
action | 必填 | 接口名:oauth_ticket / points_inc / points / profile / site_info |
- 频率限制:默认单站点每分钟 600 次请求,超限返回
code=11(后台可调)。 - 域名校验同时校验请求 Referer(若存在),与登记域名不一致同样拒绝。
- 域名不匹配 = 接口疑似被盗用:KEY 正确但
domain/ Referer /redirect_uri域名与登记不一致时,除拒绝本次通讯外,站点会被系统自动封禁(后台可关闭该规则),站长需联系管理员核查解封。 - IP 校验(可选):管理员开启后,api.php 会解析您登记域名的 DNS 并要求请求方 IP 与之一致,不一致返回
code=3。若您的服务器使用 CDN / 多出口 IP,请提前与管理员沟通。 - 任何接口都不接受密码字段。请求中出现
password/pay_password字段也不会被处理。
4.2 oauth_ticket(票据交换)★ 核心
用户跳转授权回调后,您的服务端用 ticket 换取「用户信息」(登陆/注册)或「支付结果」(支付)。
请求示例
POST /api.php HTTP/1.1
Host: passport.example.com
Content-Type: application/json
{
"apikey": "您的APIKEY",
"domain": "www.yoursite.com",
"action": "oauth_ticket",
"ticket": "回调地址上 ticket 参数的完整值(64位十六进制)"
}登陆 / 注册成功返回(code=0)
{
"code": 0,
"msg": "授权成功",
"data": {
"action": "login",
"state": "您发起时传入的state原样回传",
"first_exchange": true,
"ticket_status": "granted",
"user": {
"user_id": 10001,
"username": "zhangsan",
"nickname": "张三",
"email": "zhangsan@example.com",
"points": 1250,
"status": 1,
"status_text": "正常",
"online": true,
"has_pay_password": true,
"has_questions": true,
"created_at": "2026-08-01 12:30:00"
}
}
}data.user 字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
user_id | int | 通行证用户 ID(您站点内的用户主键,建议以 user_id 关联) |
username | string | 用户名(3-30 位字母/数字/下划线,全网唯一) |
nickname | string | 昵称(用户资料未填时为空字符串) |
email | string | 邮箱(资料邮箱优先,其次账号邮箱,可为空) |
points | int | 当前积分余额 |
status / status_text | int/string | 账号状态(1 正常 / 0 封禁) |
online | bool | 是否在线(近期有活跃) |
has_pay_password | bool | 是否已设置支付密码(未设置则无法跳转支付) |
has_questions | bool | 是否已设置 3 条密保问题 |
created_at | string | 账号注册时间(Y-m-d H:i:s) |
支付成功返回(code=0)
{
"code": 0,
"msg": "支付成功",
"data": {
"action": "pay",
"state": "您发起时传入的state原样回传",
"first_exchange": true,
"ticket_status": "paid",
"code": 0,
"user_id": 10001,
"username": "zhangsan",
"amount": 100,
"balance": 1150,
"trade_no": "T260904103015123456",
"out_trade_no": "PAY20260904001",
"paid_at": "2026-09-04 10:30:15",
"duplicate_order": false
}
}data 关键字段:amount 扣除积分、balance 支付后余额、trade_no 通行证流水号、out_trade_no 您的订单号、paid_at 支付时间、duplicate_order 是否幂等重放(true 表示该订单之前已支付,本次未重复扣款)。
用户取消(code=14)
{
"code": 14,
"msg": "用户取消了支付",
"data": {
"action": "pay",
"state": "您发起时传入的state原样回传",
"ticket_status": "cancelled"
}
}失败情形
code=13:票据无效 / 已过期 / 不属于当前站点。code=8:余额不足;code=9:风控拦截;code=10:订单号已被其他账号支付 —— 具体原因见msg,且data.ticket_status='pay_failed'。
提示:首次交换返回 data.first_exchange=true;重试返回 false。同一 ticket 短时间重复交换结果一致,可放心做网络重试。
4.3 points_inc(积分增加)
给用户增加积分,服务端直连,无需用户参与。适合充值到账、签到奖励、消费返利等场景。
<?php
/* 服务端直连:给用户增加积分(无需用户参与,如签到奖励、充值到账) */
$r = pp_api('points_inc', array(
'user_id' => 10001, // 或 'username' => 'zhangsan',二选一
'amount' => 50, // 正整数
'out_trade_no' => 'INC' . date('YmdHis') . mt_rand(100, 999), // 您的流水号,必须唯一
'description' => '每日签到奖励',
));
if ($r['code'] === 0) {
echo '入账成功,用户当前余额:' . $r['data']['balance'];
}
/* code=10 表示 out_trade_no 重复(该流水已入账),可用于防重复提交 */| 参数 | 必填 | 说明 |
|---|---|---|
username 或 user_id | 二选一 | 目标用户(来自 ticket 交换结果中的 user 信息) |
amount | 必填 | 正整数,最大 10000000 |
out_trade_no | 必填 | 您的流水号,站点内唯一。重复提交返回 code=10(防重复入账) |
description | 选填 | 说明(展示在用户的积分记录里) |
成功返回:data.user_id / data.username / data.amount / data.balance(新余额)。
4.4 points(查询积分)与 4.5 profile(查询资料)
<?php
/* 服务端直连:查询用户积分 */
$r = pp_api('points', array('username' => 'zhangsan'));
if ($r['code'] === 0) {
$d = $r['data'];
// $d['points'] 当前余额
// $d['month_in'] / $d['month_out'] 本月收入/支出
// $d['year_in'] / $d['year_out'] 本年度收入/支出
}
/* 注意:points / profile 要求该用户已通过跳转授权本站点(历史交易 或 oauth.php 授权)后才可查询;
未授权返回 code=16,请引导用户登录主站点击「同意授权」。
未授权批量遍历 user_id 还会触发查询频率限制(默认每分钟 60 次,返回 code=17)。 */
/* 服务端直连:查询用户资料(隐私最小化) */
$r = pp_api('profile', array('user_id' => 10001));
if ($r['code'] === 0) {
$u = $r['data']['user']; // user_id/username/nickname/points(仅必要公开资料)
$p = $r['data']['profile']; // real_name/nickname/gender
}
/* 出于隐私保护,手机号/Tel、QQ/微信/微博、邮箱、家庭住址、身份证号等敏感字段
不再通过 API 返回;如需用户完整资料,须用户 OAuth 授权后在主站个人中心查看。 */4.6 已停用接口(返回 code=12)
| 旧接口 | 停用原因 | 替代方案 |
|---|---|---|
action=login(密码直传登陆) | 第三方站点代收用户密码再转发,存在恶意收集用户密码的风险,已全面停用 | https://ddd.dedos.top/oauth.php?action=login 跳转授权 + oauth_ticket 交换 |
action=register(密码直传注册) | https://ddd.dedos.top/oauth.php?action=register 跳转注册 + oauth_ticket 交换 | |
action=points_dec(支付密码直传扣款) | https://ddd.dedos.top/oauth.php?action=pay 跳转支付(用户在通行证输入支付密码)+ oauth_ticket 交换 |
调用停用接口时,返回的 msg 中包含完整的迁移指引,可直接按提示改造。
5. 返回码总表
| code | 含义 | 处理建议 |
|---|---|---|
| 0 | 成功 | — |
| 1 | 参数错误 | 检查请求参数拼写与格式 |
| 2 | APIKEY 无效 / 站点未授权 | 核对 APIKEY;待审核/被拒绝/被封禁均返回此码,msg 有具体原因 |
| 3 | 域名不匹配 | domain 或 redirect_uri 与登记域名不一致 |
| 8 | 余额不足 | 引导用户充值积分 |
| 9 | 风控拦截 | 该笔交易触发大额/高频风控,msg 有具体原因 |
| 10 | 重复流水号 / 订单号被占用 | points_inc:该 out_trade_no 已入账;跳转支付:订单号已被其他账号支付 |
| 11 | 请求频率超限 | 稍后重试,做好本地限流 |
| 12 | 已停用的接口 | 旧密码直传模式,按 msg 中指引迁移到跳转授权 |
| 13 | 票据无效 / 过期 / 不属于当前站点 | 引导用户重新发起跳转 |
| 14 | 用户取消了授权或支付 | 回到您的页面并提示用户 |
| 16 | 用户未授权本站点查询资料/积分 | 引导用户通过 oauth.php 跳转授权(用户在主站点击「同意授权」)后重试 |
| 17 | 查询频率超限(profile/points) | 已按站点限制(默认每分钟 60 次),稍后重试;请勿批量遍历 user_id |
| 99 | 系统错误 | 稍后重试;持续出现请联系管理员 |
6. 完整 PHP 接入示例
以下为可直接复制使用的最小可用示例(与「开发助手」生成的代码逻辑一致)。将配置项换成您的实际信息,把跳转调用与回调处理放进对应页面即可。
<?php
/* ========== 通行证跳转授权接入(最小可用示例) ========== */
session_start();
$PP = array(
'api_url' => 'https://passport.example.com/api.php', // 通行证 API 地址
'oauth_url' => 'https://passport.example.com/oauth.php', // 通行证跳转授权页
'apikey' => '您的APIKEY', // 开放平台获得
'domain' => 'www.yoursite.com', // 您站点的登记域名
'callback' => 'https://www.yoursite.com/pp-callback.php',// 回调地址(域名须与登记一致)
);
/* 服务端调用 API(加积分 / 查询 / 票据交换) */
function pp_api($action, $params = array()) {
global $PP;
$ch = curl_init($PP['api_url']);
curl_setopt_array($ch, array(
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => array('Content-Type: application/json; charset=utf-8'),
CURLOPT_POSTFIELDS => json_encode(array_merge(array(
'apikey' => $PP['apikey'],
'domain' => $PP['domain'],
'action' => $action,
), $params)),
));
$res = curl_exec($ch);
curl_close($ch);
$j = json_decode($res, true);
return is_array($j) ? $j : array('code' => -1, 'msg' => '接口无响应');
}
/* 发起跳转(登陆 / 注册 / 支付共用) */
function pp_redirect($action, $extra = array()) {
global $PP;
$state = bin2hex(random_bytes(16)); // 随机 state,回调时校验
$_SESSION['pp_state'] = $state;
$q = array_merge(array(
'action' => $action, // login / register / pay
'apikey' => $PP['apikey'],
'redirect_uri' => $PP['callback'],
'state' => $state,
), $extra); // pay 时传 amount / out_trade_no / description
header('Location: ' . $PP['oauth_url'] . '?' . http_build_query($q));
exit;
}
/* ========== 回调处理(pp-callback.php 的核心逻辑) ========== */
if (isset($_GET['ticket'])) {
/* ① 校验 state,防跨站请求伪造 */
if (!isset($_SESSION['pp_state']) || ($_GET['state'] ?? '') !== $_SESSION['pp_state']) {
exit('state 校验失败,请重新发起');
}
unset($_SESSION['pp_state']);
/* ② 用一次性 ticket 换取结果 */
$r = pp_api('oauth_ticket', array('ticket' => $_GET['ticket']));
if ($r['code'] === 0 && $r['data']['action'] === 'pay') {
/* ③ 支付成功:按 out_trade_no 幂等发货(同一订单只处理一次) */
$d = $r['data']; // amount / balance / trade_no / out_trade_no / paid_at ...
// mark_order_paid($d['out_trade_no']); // 您的发货逻辑
} elseif ($r['code'] === 0) {
/* ④ 登陆/注册成功:写入您站点的会话 */
$_SESSION['pp_user'] = $r['data']['user']; // user_id/username/nickname/email/points ...
} elseif ($r['code'] === 14) {
/* 用户在通行证取消了授权或支付 */
}
}
/* ========== 业务页面里发起跳转 ========== */
// 用户点「用通行证登陆」按钮时:
// pp_redirect('login');
// 用户下单扣积分时:
// pp_redirect('pay', array('amount' => 100, 'out_trade_no' => $orderId, 'description' => '会员月卡'));生产接入要点:① 发起支付前先在您本地数据库创建「待支付」订单;② 回调换取到支付成功结果后,按 out_trade_no 幂等标记「已支付」再发货;③ 登陆成功后建议按 user_id 关联/创建您站点的本地用户;④ 积分余额展示请以 action=points 实时查询为准。
7. 安全最佳实践
- APIKEY 是站点机密:只在服务端使用,绝不写入前端页面 / 小程序 / App 客户端代码,不提交到公开仓库。
- 务必携带并校验 state:发起跳转时生成随机 state 存入会话,回调时严格比对,防止伪造回调骗取发货。
- 幂等发货:同一
out_trade_no只处理一次;票据重复交换(duplicate_order=true)时不要重复发货。 - 金额以本地订单为准:回调 URL 中的参数不可信,支付金额请与您本地订单核对(API 返回的
amount应等于订单金额)。 - 全站 HTTPS:回调地址与跳转地址均建议使用 https,防中间人篡改。
- 本地限流与异常告警:接口偶发
code=11属正常限流;错误率突增请检查自身逻辑或联系管理员。 - 不收集用户密码:您的任何页面都不要出现「通行证密码」输入框 —— 安全模式下也不需要。
8. 常见问题
| 问题 | 解答 |
|---|---|
| 调用 API 返回 code=3 域名不匹配? | domain 参数必须填您在开放平台登记的域名(不带协议、不带路径,如 www.yoursite.com);redirect_uri 的域名也必须一致。 |
| 用户明明已登陆,为什么还出现密码输入页? | 用户在通行证的会话与您站点独立。跳转后若通行证会话不存在或已过期,会要求重新输入;输入一次后短期内再次跳转会直接到确认页。 |
| 票据交换返回 code=13? | ticket 有效期 10 分钟(首次交换后可再重试 10 分钟),超时需用户重新发起;同时确认使用的是发起授权的那个站点的 apikey。 |
| 用户支付时提示「尚未设置支付密码」? | 该用户还没在通行证设置支付密码。通行证页面会引导其前往设置,设置完成后回来重新发起支付即可。 |
| 同一订单用户重复支付会扣两次吗? | 不会。out_trade_no 幂等:重复发起直接返回上次结果(duplicate_order=true),不重复扣款。 |
| 想给用户加积分需要跳转吗? | 不需要。points_inc / points / profile 均为服务端直连接口,任何时候可在后端调用。 |
| 票据交换必须在回调页同步完成吗? | 建议同步完成(10 分钟窗口内)。若您的架构需要异步,务必先把 ticket 存入队列并在 10 分钟内完成交换。 |
| 能否在 App / 小程序内使用? | 可以。在内嵌 WebView 或系统浏览器中完成跳转授权,回调回到您指定的可被 App 拦截的地址即可;服务端交换逻辑完全一致。 |
接入过程中遇到问题:先查阅本文档与「开发助手」生成的示例代码,仍无法解决时请联系站点管理员。