当前策略:公开 API 保留订单查询、详情与状态接口;自动领取接口 POST /orders/{order_no}/claim 已关闭并返回 403 MANUAL_CLAIM_ONLY。请使用供货商工作台手动接单。历史 API Key 与账号不封禁,已有订单不受影响。
⌗ UPI SCAN
UPI Scan 供货端接单 API
v1.0
供货端工作台

供货端接单 API

版本 v1.0 · 更新于 2026-10-29 · 接口前缀 /api/v1/open/v1

1. 这是什么

用你自己的网站或程序直接接平台的订单,不必登录工作台:

  1. 拉取「可接订单」列表;
  2. 领取订单(claim),拿到这一单的支付链接;
  3. 完成后提交结果(complete),平台自动结算到你账户余额。

谁能用

角色能否申请 API Key说明
老板(is_boss)可以最多 5 个 Key
独立供货商(无上级)可以最多 5 个 Key
工人(挂在老板名下)不可以接口会返回 WORKER_NOT_ALLOWED;工人请继续用工作台

2. 快速开始

  1. 登录供货端工作台 → 左侧「API 接口」→ 创建密钥。
    完整密钥只显示这一次,关闭后无法再次查看,请立刻保存。
  2. 开启接单:POST /accepting,请求体 {"enabled": true}。
    没开启接单时,可接订单接口返回 409 SUPPLIER_NOT_ACCEPTING,心跳也不会生效。
  3. 每 10–30 秒轮询 GET /orders/available,发现订单就 POST /orders/{order_no}/claim, 领取成功后立刻 GET /orders/{order_no}/detail 取支付链接。

2.1 创建密钥时容易踩的三个坑

现象原因怎么处理
点了「创建密钥」没反应名称没填名称必填,最多 50 字
400 INVALID_IP_SPECIP 白名单里有非法项只支持 1.2.3.4 或 1.2.3.0/24,多个用英文逗号分隔;不支持域名,也不支持 1.2.3.4-1.2.3.9 这类区间
400 API_KEY_LIMIT未撤销的密钥已经有 5 个先到「API 接口」页撤销不用的,再创建新的
密钥只在创建那一刻完整显示一次,关闭后无法再看,丢了就撤销重建。IP 白名单留空表示不限制来源 IP;填了就必须和调用方出口 IP 完全匹配(或落在这个网段内),否则接口返回 401。

3. 认证

每个请求都要带密钥,两种写法任选其一:

X-API-Key: ssk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer ssk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
密钥等同于密码。只在你自己的服务端使用,不要写进网页 JS、小程序或 App 前端—— 抓包即可拿走,别人就能用你的账号接单和结算。怀疑泄露请立刻在工作台撤销并重建。

4. 基础约定

项目约定
Base URLhttps://jmlscan.cloud-ip.cc/api/v1/open/v1
协议只支持 HTTPS。HTTP 会被 301 跳转。
请求体Content-Type: application/json,UTF-8。写接口即使无参数也要发 {}。
金额一律是字符串,如 "0.5"、"32.4"。请用十进制类型解析,不要用浮点累加。
时间RFC3339,UTC,如 2026-10-29T05:23:36Z。
分页?page=1&page_size=20,page_size 上限 100,默认 20。
响应信封成功 {"code":"OK","data":{…},"message":"success"};失败 {"code":"错误码","message":"说明"}
邮箱出于隐私,邮箱一律掩码,如 wr***r@i***m。请勿依赖完整邮箱做业务判断。

5. 接口清单

方法路径作用
GET/account账户概览:余额、接单开关、进行中订单数、单价
POST/accepting开启 / 关闭接单,请求体 {"enabled":true}
POST/heartbeat心跳保活(建议 30–60 秒一次)。仅在开启接单时有效
POST/idle-ack确认「挂机提醒」,避免被自动下线
GET/orders/available可接订单列表
GET/orders/mine我已领取的订单
GET/orders/{order_no}/detail单个订单详情(领取后可取支付链接)
POST/orders/{order_no}/claim领取订单
POST/orders/{order_no}/start开始处理(领到单后马上调)
POST/orders/{order_no}/complete提交完成,请求体 {"result":"..."}
POST/orders/{order_no}/fail标记失败,请求体 {"reason":"..."}
POST/orders/{order_no}/cancel放弃已领取但未开始的订单
GET/ledger收益流水
GET/notice平台公告

提现、工人管理、团队订单等接口不对外开放,请在工作台操作。调用这些路径会返回 404 NOT_FOUND。

GET /account

{
  "code": "OK",
  "data": {
    "accepting_orders": true,
    "active_orders": 1,
    "balance": "32.4",
    "frozen_balance": "0",
    "total_earned": "185.1",
    "total_withdrawn": "152.7",
    "pending_withdrawal_amount": "0",
    "supplier_unit_price": "0.5",
    "is_boss": true,
    "is_worker": false,
    "claim_ban_minutes": 0,
    "claim_blocked_until": null,
    "countdown_seconds": 480
  },
  "message": "success"
}

GET /orders/available

{
  "code": "OK",
  "data": {
    "items": [
      {
        "order_no": "O20261029ZN5RTLNNC6YSHSX6",
        "status": "pending",
        "at_email": "pr***s@i***m",
        "link": "https://payments.stripe.com/••••••••",
        "link_locked": true,
        "unit_price": "0.5",
        "supplier_amount": "0.5",
        "charge_amount": "0.5",
        "fulfillment_mode": "supplier",
        "source": "guest",
        "created_at": "2026-10-29T05:26:24Z",
        "claimed_at": null,
        "started_at": null
      }
    ],
    "pagination": { "page": 1, "page_size": 20, "total": 12, "total_pages": 1 }
  },
  "message": "success"
}
支付链接在领取前是掩码的。link_locked: true 表示还不能用; 必须先把这一单 claim 到自己名下,再调 GET /orders/{order_no}/detail 取真实链接。

6. 接单流程与状态机

pending ──claim──▶ claimed ──start──▶ processing ──complete──▶ completed
                          │                    │
                          │                    └──fail──▶ failed
                          └──cancel──▶ cancelled
状态含义你的下一步
pending未领取,在公共池里可以 claim
claimed已被你领取,尚未开始立刻 start
processing处理中,开始计时完成后 complete 或 fail
completed已完成,通常已结算—
failed已标记失败—
cancelled已取消(你放弃或平台取消)—

必须注意的四条规则

  1. claim 之前必须 accepting=true,否则 409 SUPPLIER_NOT_ACCEPTING。
  2. claim 成功要立刻 start。只 claim 不 start 不计入处理时长,容易被判超时。
  3. 一个订单只能被一个人领到。并发 claim 时只有一个人成功,另一个会拿到订单状态相关的错误——按错误码处理即可,不要重试同一个单号。
  4. complete 的 result 要写清楚(例如支付成功凭据、截图地址或说明)。平台会据此判定是否结算,返回里带 settled。

complete 请求与响应

POST /orders/O20261029JOPJIGQPDEIC5ZBO/complete
{"result": "Payment done, screenshot: https://your-site.com/s/abc123.png"}
{
  "code": "OK",
  "data": {
    "order": { "order_no": "O20261029JOPJIGQPDEIC5ZBO", "status": "completed", "result": "…" },
    "settled": true
  },
  "message": "success"
}

settled=true 表示这一单的收益已入账。

7. 代码示例

curl

export KEY="ssk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export BASE="https://jmlscan.cloud-ip.cc/api/v1/open/v1"

# 1) 开启接单
curl -s -X POST "$BASE/accepting" \
     -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
     -d '{"enabled":true}'

# 2) 看可接订单
curl -s "$BASE/orders/available?page=1&page_size=10" -H "X-API-Key: $KEY"

# 3) 领取 + 开始
curl -s -X POST "$BASE/orders/O20261029ZN5RTLNNC6YSHSX6/claim" \
     -H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{}'
curl -s -X POST "$BASE/orders/O20261029ZN5RTLNNC6YSHSX6/start" \
     -H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{}'

Python (stdlib only)

import json, time, urllib.request, urllib.error

KEY  = "ssk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
BASE = "https://jmlscan.cloud-ip.cc/api/v1/open/v1"

def call(method, path, body=None):
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(BASE + path, data=data, method=method)
    req.add_header("X-API-Key", KEY)
    req.add_header("Content-Type", "application/json")
    req.add_header("User-Agent", "my-supplier-bot/1.0")   # 务必带上,别用默认 UA
    try:
        with urllib.request.urlopen(req, timeout=20) as r:
            return json.loads(r.read().decode())
    except urllib.error.HTTPError as e:
        return json.loads(e.read().decode())

call("POST", "/accepting", {"enabled": True})

while True:
    res = call("GET", "/orders/available?page=1&page_size=10")
    for o in (res.get("data") or {}).get("items", []):
        no = o["order_no"]
        if call("POST", f"/orders/{no}/claim", {}).get("code") == "OK":
            call("POST", f"/orders/{no}/start", {})
            detail = call("GET", f"/orders/{no}/detail")
            print("拿到订单", no, "支付链接", (detail.get("data") or {}).get("link"))
    call("POST", "/heartbeat", {})
    time.sleep(15)

PHP

<?php
$key  = 'ssk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$base = 'https://jmlscan.cloud-ip.cc/api/v1/open/v1';

function api($method, $path, $body = null) {
    global $key, $base;
    $ch = curl_init($base . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_HTTPHEADER     => ['X-API-Key: ' . $key, 'Content-Type: application/json'],
        CURLOPT_USERAGENT      => 'my-supplier-bot/1.0',
        CURLOPT_TIMEOUT        => 20,
    ]);
    if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
    $raw = curl_exec($ch);
    curl_close($ch);
    return json_decode($raw, true);
}

api('POST', '/accepting', ['enabled' => true]);
$res = api('GET', '/orders/available?page=1&page_size=10');
foreach ($res['data']['items'] ?? [] as $o) {
    if (api('POST', '/orders/' . $o['order_no'] . '/claim', [])['code'] === 'OK') {
        api('POST', '/orders/' . $o['order_no'] . '/start', []);
    }
}

Node.js (18+)

const KEY = "ssk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const BASE = "https://jmlscan.cloud-ip.cc/api/v1/open/v1";

async function api(method, path, body) {
  const res = await fetch(BASE + path, {
    method,
    headers: { "X-API-Key": KEY, "Content-Type": "application/json" },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  return res.json();
}

(async () => {
  await api("POST", "/accepting", { enabled: true });
  const list = await api("GET", "/orders/available?page=1&page_size=10");
  for (const o of list?.data?.items ?? []) {
    const r = await api("POST", `/orders/${o.order_no}/claim`, {});
    if (r.code === "OK") await api("POST", `/orders/${o.order_no}/start`, {});
  }
})();
关于 User-Agent:平台的边缘有恶意 UA 过滤,python-urllib、 python-requests、Go-http-client/1.1、curl/7.x、 Java/…、libwww-perl 等默认 UA 会被直接断开(连接被重置、没有响应体)。 本接口已对这些常见 UA 做了放行,但仍建议显式设置一个自己的 UA,避免以后策略调整时受影响。

8. 错误码

网关层(本接口自身返回)

HTTPcode含义与处理
401MISSING_API_KEY没带 Key。检查 X-API-Key 头
401INVALID_API_KEYKey 无效或已撤销。到工作台确认
403IP_NOT_ALLOWED来源 IP 不在白名单里。改 Key 的白名单或换出口 IP
403WORKER_NOT_ALLOWED工人账号不能调用。请老板申请
403ACCOUNT_DISABLED账号已停用
404NOT_FOUND路径不存在或不在开放范围
405METHOD_NOT_ALLOWED方法不对,例如对 claim 用了 GET
429RATE_LIMITED调用过于频繁,见第 9 节。请退避重试
502UPSTREAM_UNAVAILABLE平台内部暂时不可用,指数退避重试
503DB_ERROR平台数据库繁忙,稍后重试

业务层(平台原样透传)

HTTPcode含义与处理
409SUPPLIER_NOT_ACCEPTING没开启接单。先调 POST /accepting {"enabled":true}
404ORDER_NOT_FOUND单号不存在或不属于你
409SUPPLIER_CLAIM_BLOCKED领取被临时封禁(抢单过于频繁等)。看 /account 的 claim_blocked_until,到点再试
409ORDER_NOT_OPEN_YET该订单还没到可接时间,稍后再试

遇到未列出的错误码时,请以响应里的 message 为准,并保留 code 便于排查。

9. 限流

建议的轮询节奏:/orders/available 10–30 秒一次,/heartbeat 30–60 秒一次。 正常接单完全够用。收到 429 时请按 1s → 2s → 4s 退避,不要立刻重试。

10. 常见问题

为什么 /orders/available 一直空的?

先确认 GET /account 里 accepting_orders 是 true;再确认 heartbeat 在正常调用。 平台池子本身也会出现暂时无单的情况,属正常现象。

为什么拿到的是掩码邮箱和掩码链接?

邮箱全天候掩码;支付链接只在领取之后才可读(link_locked 变 false)。

Key 泄露了怎么办?

立刻到工作台「API 接口」撤销该 Key,再建一个新的。撤销是立即生效的,进行中的调用会马上失败。

能不能一个 Key 给多个网站用?

可以,但建议按站点分开建(最多 5 个),这样某一个泄露时只需撤销那一个,也便于按 Key 看调用量。

余额和提现呢?

API 可以查余额和收益流水(/account、/ledger),提现不开放 API,请在工作台操作。

新增接口(2026-11-09)

以下接口为近期新增,均使用 Authorization: Bearer <token> 鉴权(公开接口除外)。

GET /api/v1/supplier/board

供货商端「个人看板」6 项指标(本人接单口径)。

{
  "completed": 155,          // 累计完成
  "failed": 46,              // 累计失败
  "last50_sample": 50,       // 最近 50 单已结算的样本数
  "last50_completed": 42,
  "success_rate": 84,        // 百分比整数,样本为 0 时返回 null
  "today_done": 10,          // 今日(北京时间自然日)完成
  "today_failed": 3,
  "today_rate": 77
}
GET /api/v1/supplier/orders/available-viewonly

未开启接单时也能看到「有多少单」的只读池。单号、链接、AT 邮箱全部以 *** 打码,仅保留金额与创建时间。 响应额外带 view_only: true、view_only_reason: "NOT_ACCEPTING",每一项带 masked: true。 点「接单」时后端仍会返回 409 SUPPLIER_NOT_ACCEPTING。

GET /api/v1/public/scan/emails/today?cdks=卡密1,卡密2

公开接口(无需鉴权,知道卡密即可查)。返回这些卡密下今天成功订单的 AT 邮箱明文,一行一个。 用于「CDK 查询」页的「复制今日成功」按钮。

{ "code": "OK", "data": { "count": 2, "emails": ["[email protected]", "[email protected]"] } }
GET /api/v1/user/emails/today

登录用户查看自己名下今天成功订单的 AT 邮箱明文(按 customer_id 过滤,不返回他人数据)。

GET /api/v1/admin/user-invites (管理员)

返回 user_id → 邀请码 的映射,用于管理端「用户管理」的邀请码列。 系统默认码显示为 SYSTEM。