双零互联——积分通行证开放平台 · 第三方对接文档

API 接入文档

双零互联——积分通行证 为第三方站点提供统一账号登陆 / 注册 / 积分服务。全部接口采用跳转授权安全模式(类 OAuth 2.0):用户的登陆密码与支付密码只在 双零互联——积分通行证 页面输入,任何情况下都不会经过您的站点、也不会出现在任何 API 请求中,从根本上杜绝第三方站点(无论善意还是恶意)获取用户密码的可能。

跳转授权安全模式 密码零接触 订单幂等防重复扣款 state 防 CSRF 一次性票据(10 分钟有效)

1. 接入概述与安全模型

您的站点需要用到三类能力:登陆 / 注册 / 支付扣积分(需要用户输入密码的,走浏览器跳转);积分增加 / 积分查询 / 资料查询(无需用户参与,服务端直连调用)。

安全模型一句话:凡是需要密码的动作,都把用户浏览器重定向到 双零互联——积分通行证,用户在 双零互联——积分通行证 页面输入密码完成操作后,浏览器再带着一张一次性票据(ticket)回到您的回调地址,您的服务端用 ticket 向 API 换取结果。您的站点全程接触不到任何密码。

完整时序

1
您的站点 → 通行证(浏览器重定向)用户点击「用通行证登陆」或「去支付」,您的站点生成随机 state 存入会话,把浏览器重定向到 https://ddd.dedos.top/oauth.php(携带 apikey / redirect_uri / state,支付另带 amount / out_trade_no)。
2
用户在通行证完成操作登陆:输入通行证账号密码 → 确认授权;支付:确认金额订单 → 输入支付密码。密码只提交给通行证。
3
通行证 → 您的回调地址(浏览器重定向)通行证完成校验、扣款(支付时)后,把浏览器重定向回您的 redirect_uri,附加参数 ?ticket=64位票据&state=原样回传
4
您的服务端 → 通行证 API(服务端调用)回调页先校验 state(防伪造),再用 ticketPOST https://ddd.dedos.top/api.phpaction=oauth_ticket 换取用户信息或支付结果。
5
完成您的业务登陆:写入您站点的会话;支付:按 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_ticketPOST https://ddd.dedos.top/api.php❌ 服务端用一次性 ticket 换取用户信息 / 支付结果(核心)
action=points_incPOST https://ddd.dedos.top/api.php❌ 服务端给用户增加积分
action=pointsPOST https://ddd.dedos.top/api.php❌ 服务端查询积分余额及月/年统计(需用户授权,限速)
action=profilePOST https://ddd.dedos.top/api.php❌ 服务端查询用户必要公开资料(隐私最小化,需用户授权,限速)

⚠ 已停用接口:旧版的 login / register / points_dec(由第三方站点代收用户密码再转发给 API 的模式)已全面停用,调用将返回 code=12 并附迁移指引。这是为了防止第三方站点恶意收集用户密码。

2. 快速开始(5 步接入)

1
注册通行证账号https://ddd.dedos.top/index.php 注册一个账号(站长本人)。
2
申请站点登录后进入 开放平台https://ddd.dedos.top/kf.php)填写站点域名 / 名称 / 关键词提交申请,等待管理员审核。
3
获取 APIKEY审核通过后在「站点总览」获得专属 APIKEY(48 位,站点机密,请勿外泄)。
4
生成对接代码在「开发助手」(https://ddd.dedos.top/kf.php?mod=dev)勾选需要的功能,一键生成完整可运行的 PHP 演示代码,上传到您的站点即可测试。
5
并入业务把演示代码中的跳转函数与回调处理逻辑搬进您的业务代码(见本文第 6 节完整示例)。

域名校验提醒:API 通讯与跳转回调都会严格校验域名 —— domain 参数和 redirect_uri 的域名必须与站点登记域名完全一致(自动忽略 www. 前缀与协议,但主域名必须相同),否则通讯被拒绝。更换域名需在开放平台修改并重新审核。

3. 跳转授权页 oauth.php

地址:https://ddd.dedos.top/oauth.php。把用户浏览器重定向(302)到该地址并携带下表参数;用户在通行证完成操作后,浏览器会被重定向回您的 redirect_uri 并附加 ticketstate 参数。

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_idint通行证用户 ID(您站点内的用户主键,建议以 user_id 关联)
usernamestring用户名(3-30 位字母/数字/下划线,全网唯一)
nicknamestring昵称(用户资料未填时为空字符串)
emailstring邮箱(资料邮箱优先,其次账号邮箱,可为空)
pointsint当前积分余额
status / status_textint/string账号状态(1 正常 / 0 封禁)
onlinebool是否在线(近期有活跃)
has_pay_passwordbool是否已设置支付密码(未设置则无法跳转支付)
has_questionsbool是否已设置 3 条密保问题
created_atstring账号注册时间(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 重复(该流水已入账),可用于防重复提交 */
参数必填说明
usernameuser_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参数错误检查请求参数拼写与格式
2APIKEY 无效 / 站点未授权核对 APIKEY;待审核/被拒绝/被封禁均返回此码,msg 有具体原因
3域名不匹配domainredirect_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 拦截的地址即可;服务端交换逻辑完全一致。
双零互联——积分通行证 开放平台 · 跳转授权安全模式 API 文档
接入过程中遇到问题:先查阅本文档与「开发助手」生成的示例代码,仍无法解决时请联系站点管理员。