灵策中台 · 开发者门户

给你的产品接上
账号、收款和模型

灵策中台是灵策的公共能力层。登录、收款、模型、通知、文件都在同一个域名下,一个应用登记一次,按需开通,新产品不用再把这五件事各对接一遍。

面向灵策自家子站、内部工具和合作方的 Web 应用。

https://center.lynxce.cn
  • GET/.well-known/openid-configuration登录
  • POST/api/oauth/site/token登录
  • POST/api/center/pay/orders收款
  • POST/api/v1/chat/completions模型
  • POST/api/center/notify/messages通知
  • POST/api/center/files/presign文件

为什么用中台

接一次,五件事都有了

这五件事每个产品都要做,做法又都差不多。放到中台后,你的产品只保留业务逻辑,用户也只有一个灵策账号。

能力各自对接接入中台
登录每种登录方式写一套对接,自己管令牌续期和账号合并一个 client_id,用 sub 绑定本地账号
收款申请商户号,对接两家支付方,处理回调验签、掉单和对账一个下单接口加一个 Webhook,款项进灵策商户号
模型逐家接供应商,自己做密钥管理、计费和限流一个 baseURL 加一把 sk- Key
通知自建消息中心和发送记录一次 POST,用户在灵策消息中心看到
文件自建存储桶、签名和权限隔离一次 presign,文件直传对象存储

能力

五项能力,逐项开通

每项能力默认关闭,用到哪项开哪项。登录是基础,收款和通知都要用到登录拿到的用户 id。

收款

在服务端下单,用户用微信支付或支付宝付款,结果推到你的 Webhook。收款和灵策积分无关,由你自己发货。

  • 微信支持扫码、手机浏览器、微信内网页;支付宝支持电脑网站、手机网站、当面付扫码
  • 一单可以多次部分退款,outRefundNo 幂等,结果未知时同号重试不会退两次
  • Webhook 用 HMAC-SHA256 签名,失败按退避重试 8 次,约 45 小时
  • 待付单过期自动关单,并推送 order.closed
  • 平台每日拉取支付方账单对账,中台收款单单独核对,差异由运营跟进
  • payment.succeeded
  • refund.succeeded
  • refund.failed
  • order.closed

模型

OpenAI 兼容接口,支持流式。用应用自己的 Key 调用,费用和限额都和其他应用分开。

  • 独立的 sk- Key,默认绑定专属组织和钱包,消耗单独记账
  • 每把 Key 单独设 RPM、TPM、每日和每月积分上限
  • 可限定允许的模型,以及对话、出图、出视频能力
  • 余额检查在调用上游之前,非流式请求支持 Idempotency-Key
  • RPM
  • TPM
  • 日额度
  • 月额度

通知

给登录过你应用的灵策用户发站内信,用户在消息中心看到,同时进邮件摘要。

  • 标题自动加「【应用名】」前缀,用户分得清来源
  • eventKey 幂等,重试不会重复发送
  • 只能发给授权过你应用的用户,单个用户默认 24 小时 20 条
  • eventKey
  • 站内信

文件

拿签名地址把文件直传对象存储,不占你的服务器带宽。公共文件给链接,私有文件现换下载地址。

  • 签名地址 10 分钟有效,路径由中台生成,按应用隔离,不能覆盖
  • 私有文件的下载地址有效期 60 到 3600 秒,只能换自己应用上传的文件
  • 上限:图片 20MB,PDF 50MB,音频 100MB,视频 500MB
  • public
  • private

代码

写起来是这样

下面都是 SDK 的真实用法,只在你的服务端运行。模型部分直接用 OpenAI 官方 SDK。

统一登录

createLoginRequest 生成 PKCE、state 和 nonce,handleCallback 换令牌并验 id_token。

看完整文档
TypeScript
import { CenterClient } from "@ai-employee/center-sdk";

const center = new CenterClient({
  issuer: "https://center.lynxce.cn",
  clientId: "your-app",
  redirectUri: "https://your-app.example.com/auth/lynxce/callback",
  scope: "openid profile roles",
});

// 发起登录:state、nonce、codeVerifier 存进服务端会话,再跳转
const login = await center.createLoginRequest();

// 回调:校验 state,换令牌,验 id_token
const { claims } = await center.handleCallback({
  callback: new URL(req.originalUrl, "https://your-app.example.com"),
  expectedState: pending.state,
  codeVerifier: pending.codeVerifier,
  nonce: pending.nonce,
});

// claims.sub 是稳定的灵策用户 id,用它绑定本地账号
// claims.roles 是用户在本应用的角色

收款

下单按 outTradeNo 幂等,发货只认验签通过的 Webhook,部分退款带 outRefundNo。

看完整文档
TypeScript
import { CenterServerClient, constructWebhookEvent } from "@ai-employee/center-sdk";

const server = new CenterServerClient({
  baseUrl: "https://center.lynxce.cn",
  clientId: process.env.LYNXCE_CLIENT_ID!,
  clientSecret: process.env.LYNXCE_CLIENT_SECRET!,
});

const order = await server.createOrder({
  outTradeNo: "demo-20261029-0001", // 幂等键,网络错误用同号重试
  subject: "专业版月卡",
  amountFen: 1990,
  provider: "alipay",
  scene: "page",
  returnUrl: "https://your-app.example.com/paid",
});
// 浏览器跳转 order.payment?.payUrl

// Webhook:用原始请求体验签,按事件 id 去重
const event = await constructWebhookEvent({
  payload: rawBody,
  signature: req.header("x-lynxce-signature"),
  secret: process.env.LYNXCE_WEBHOOK_SECRET!,
});
if (event.type === "payment.succeeded") await fulfill(event.data.order.outTradeNo);

// 部分退款:outRefundNo 是幂等键,结果未知时用同号重试
await server.refund(order.orderNo, { amountFen: 500, outRefundNo: "rf-0001", reason: "部分退" });

模型

模型调用不走 SDK,用 OpenAI 官方客户端,把 baseURL 指向中台。

看完整文档
TypeScript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://center.lynxce.cn/api/v1",
  apiKey: process.env.LYNXCE_MODEL_API_KEY, // 开通时拿到的 sk-…
});

const completion = await client.chat.completions.create({
  model: "deepseek-chat", // 以 GET /api/v1/models 返回的 id 为准
  messages: [{ role: "user", content: "用一句话介绍灵策智算" }],
});

console.log(completion.choices[0].message.content);

通知

userId 用登录时拿到的 sub。网络错误时用同一个 eventKey 重试,不会重复发送。

看完整文档
TypeScript
import { CenterServerClient } from "@ai-employee/center-sdk";

const server = new CenterServerClient({
  baseUrl: "https://center.lynxce.cn",
  clientId: process.env.LYNXCE_CLIENT_ID!,
  clientSecret: process.env.LYNXCE_CLIENT_SECRET!,
});

const { messageId, idempotent } = await server.sendNotification({
  userId: claims.sub,
  title: "订单已发货", // 中台自动加上「【你的应用名】」前缀
  body: "你的订单 A-1001 已发货,预计明天送达。",
  eventKey: "order-shipped:A-1001",
});
// idempotent 为 true:这个 eventKey 之前发过,本次没有新发

文件

服务端申请签名地址,文件 PUT 到对象存储。私有文件用时再换短时下载地址。

看完整文档
TypeScript
import { CenterServerClient } from "@ai-employee/center-sdk";

const server = new CenterServerClient({
  baseUrl: "https://center.lynxce.cn",
  clientId: process.env.LYNXCE_CLIENT_ID!,
  clientSecret: process.env.LYNXCE_CLIENT_SECRET!,
});

// 公共文件:上传后用 upload.publicUrl 访问
const upload = await server.presignFileUpload({
  contentType: "image/png",
  sizeBytes: file.size,
});
await fetch(upload.uploadUrl, { method: upload.method, headers: upload.headers, body: file });

// 私有文件:publicUrl 为 null,要读时现换短时地址
const contract = await server.presignFileUpload({
  contentType: "application/pdf",
  sizeBytes: file.size,
  visibility: "private",
});
const { downloadUrl } = await server.getFileDownloadUrl({ key: contract.key, expiresInSeconds: 300 });

接入流程

一笔订单从登录到通知

四项能力串起来是这样。接入样例实现的就是这条链路。

  1. 第 1 步登录

    用户从你的产品跳到灵策授权,回来时带着授权码。你的服务换令牌、验 id_token,拿到稳定的 sub。

    center.handleCallback()
    用户你的服务灵策中台支付方跳转授权(PKCE)回调 code + state换令牌,验 id_token
  2. 第 2 步下单付款

    你的服务在中台下单,拿到二维码或支付链接交给用户。用户直接向支付方付款,你的服务不经手支付信息。

    server.createOrder()
    用户你的服务灵策中台支付方下单 outTradeNoqrCode / payUrl用户扫码或跳转付款
  3. 第 3 步Webhook

    支付方确认后,中台把 payment.succeeded 推给你。验签、按事件 id 去重、再发货,10 秒内返回 2xx;没收到 2xx 会按退避重试。

    constructWebhookEvent()
    用户你的服务灵策中台支付方付款结果payment.succeeded(已签名)验签去重后返回 2xx
  4. 第 4 步通知用户

    发货后给用户发一条站内信。userId 用第一步拿到的 sub,eventKey 保证重试时不会重复发送。

    server.sendNotification()
    用户你的服务灵策中台支付方发通知 eventKey站内信「【应用名】…」

开始

三步接入

  1. 1

    登记应用

    把应用 ID、名称、首页和回调地址发给灵策运营。登记后,应用 ID 就是你的 client_id。

    要准备什么
  2. 2

    开通能力,领取凭证

    登录不需要密钥。收款、通知、文件用 client_secret,模型用 sk- Key,都只显示一次,只放在服务端。

    凭证怎么用
  3. 3

    按文档接入

    TypeScript 项目用 SDK,其他语言调 HTTP 接口。接入样例演示了登录、收款、通知、文件的完整写法。

    看接入样例

从快速开始读起

先看完整路径,再决定开通哪几项能力。