概述

中台地址 https://userpay.aitalker.vip,提供统一用户系统 + 微信支付通道。金额单位统一为(1元=100分)。所有接口返回 JSON。

鉴权

职责边界:中台只管用户身份 + 收款 + 管理后台;会员等级、VIP 权益由各应用自己实现,中台不提供。
凭据说明用法
X-API-Key业务服务器调支付接口用(.env API_KEY/api/pay/create/api/pay/query/api/pay/refund。仅服务端,不能登录管理后台,不能暴露给前端。
X-App-Key外部应用调额度接口用/api/app/*。后台创建应用后生成。
JWT Token用户身份凭证,登录成功后返回,默认 24 小时过期请求头 Authorization: Bearer <token>
微信登录获取 JWT 的方式(扫码 / 网页授权)调用下方「用户 & 微信扫码登录」中的接口换取 token
admin_token Cookie管理后台登录凭证浏览器访问管理后台,微信扫码后自动设置。与 X-API-Key 无关。

JWT 适用的接口:/api/user/*/api/pay/create-for-app(前端 App 下单)。

管理员:在 ADMIN_USER_IDS 配置或后台「用户管理」设为管理员。部署参数见 agent skill.MDP0

用户 & 微信扫码登录

本中台仅支持微信/QR 扫码登录。用户扫码→自动注册或登录→返回 JWT Token。没有密码注册流程。

使用 snsapi_base 静默授权,不弹窗。用户首次扫码自动注册。

步骤 1:获取授权链接

GET/api/user/wechat/auth-url

参数:redirect_uri 你的回调地址,state 任意字符串(可选)

GET /api/user/wechat/auth-url?redirect_uri=https://你的域名/callback&state=abc

→ {
  "code": 0,
  "data": {
    "auth_url": "https://open.weixin.qq.com/connect/oauth2/..."
  }
}

auth_url 作为微信登录按钮跳转链接。用户点击→授权→微信回调你的 redirect_uri?code=xxx

步骤 2:code 换 Token

POST/api/user/wechat/login
Content-Type: application/json

{ "code": "微信回调带来的 code" }

→ {
  "code": 0,
  "data": {
    "user_id": 3,
    "token": "eyJhbG...",
    "expires_in": 86400,
    "is_new": true   // 首次扫码为 true
  }
}
简化:redirect_uri 直接设为 https://userpay.aitalker.vip/callback,中台自动完成登录全流程,前端一行后端代码不用写。

QR 扫码登录(推荐)

无需微信开放平台。用户扫码→微信内静默授权→桌面自动登录。纯前端 3 步集成。

微信信息:扫码授权后自动获取用户昵称、头像、省份信息,存入用户表。需服务号已微信认证。

步骤 1:发起 QR 登录

POST/api/user/qr-login/start
POST /api/user/qr-login/start

→ {
  "code": 0,
  "data": {
    "key": "a1b2c3...",
    "qr_url": "https://userpay.aitalker.vip/api/user/qr-login/go?key=a1b2c3...",
    "expire_seconds": 300
  }
}

qr_url 生成二维码显示给用户。

步骤 2:轮询检查

GET/api/user/qr-login/check?key=a1b2c3...
GET /api/user/qr-login/check?key=a1b2c3...

→ { "code": 0, "data": { "status": "pending" } }   // 继续等
→ { "code": 0, "data": { "status": "done", "token": "eyJh...", "user_id": 1 } }  // 完成
→ { "code": 0, "data": { "status": "expired" } }  // 超时

每 2 秒轮询一次。done 返回 token(查后即焚,同一个 key 只能取一次)。

集成的完整前端代码

// 1. 发起 QR 登录
const resp = await fetch('https://userpay.aitalker.vip/api/user/qr-login/start', {method:'POST'});
const {key, qr_url} = (await resp.json()).data;

// 2. 生成二维码(用 qrcodejs 库)
new QRCode(el, {text: qr_url, width: 200, height: 200});

// 3. 轮询
const timer = setInterval(async () => {
  const r = await fetch(`https://userpay.aitalker.vip/api/user/qr-login/check?key=${key}`);
  const {status, token, user_id} = (await r.json()).data;
  if (status === 'done') {
    clearInterval(timer);
    localStorage.setItem('token', token);  // 存 token,后续 API 用
    window.location.href = '/dashboard';   // 登录成功跳转
  } else if (status === 'expired') {
    clearInterval(timer);  // 刷新页面重试
  }
}, 2000);

绑定手机号

微信扫码只拿 openid。如需手机号,前端调 wx.getPhoneNumber 拿 code:

POST/api/user/bind-phone
Authorization: Bearer <token>
Content-Type: application/json

{ "code": "wx.getPhoneNumber 返回的 code" }

→ { "code": 0, "message": "ok" }

账号合并逻辑

场景结果
手机号空闲直接绑定
被老用户占用,老用户没绑微信自动合并:删新号,openid 迁到老号,返回 {"merged":true}
被老用户占用,老用户已有微信400 "该手机号已绑定其他微信账号"
新用户已有支付订单400 拒绝合并,提示联系客服

获取用户信息

GET/api/user/profile
Authorization: Bearer <token>

→ {
  \"code\": 0,
  \"data\": {
    \"user_id\": 1,
    \"phone\": \"138****8000\",
    \"nickname\": \"张三\",
    \"created_at\": \"2026-06-06T00:00:00\"
  }
}

微信支付

中台提供两种下单方式,适用于不同的接入场景:

接口鉴权user_id适用场景
POST /api/pay/create X-API-Key(服务端密钥) 手动传 你的后端 Server 调用。你有自己的后端服务,在中台完成下单后自行处理后续逻辑。
POST /api/pay/create-for-app Authorization: Bearer ***(JWT 登录 Token) 自动从 Token 取 纯前端 App 调用(如 1 元建站)。用户 QR 扫码登录后拿到 JWT,直接下单,无需经过你的后端。

这么设计的原因

之前只有 /api/pay/create 一个接口,它需要 X-API-Key 鉴权——这个 Key 是服务端密码,绝不能暴露给前端浏览器。但 1 元建站这类纯前端应用没有自己的后端,前端直接调中台就会把 Key 暴露出去。

新增 /api/pay/create-for-app 后,前端只需要 QR 扫码登录拿 JWT Token,然后用 Token 鉴权下单。用户 ID 自动从 Token 提取,前端不用管 user_id,也不用管 API Key。

创建支付订单(服务端调用)

适合有后端的 Server B 接入。需要 X-API-Key 头。金额单位

POST/api/pay/create
X-API-Key: <api-key>
Content-Type: application/json

{
  "user_id": 1,
  "out_trade_no": "MYAPP20260606001",
  "description": "会员月卡",
  "amount": 990,
  "pay_type": "jsapi",
  "openid": "oUpF8u...",
  "notify_url_b": "https://你的域名/callback"
}
字段类型必填说明
user_idint中台用户 ID
out_trade_nostring你的订单号。建议 产品前缀+日期+序号
descriptionstring商品描述,显示在微信支付页
amountint金额,单位分(990=9.9元)
pay_typestringjsapinative
openidstringjsapi必填用户微信 openid
notify_url_bstring支付成功通知地址

JSAPI 返回 pay_params,前端直接传 wx.requestPayment(pay_params)
Native 返回 code_url,生成二维码给用户扫码付。

创建支付订单(前端 App 调用)

适合纯前端 App(如 1 元建站)。不需要 X-API-Key不需要传 user_id。用户通过 QR 扫码登录拿到 JWT Token 后,用 Bearer Token 鉴权直接下单。

POST/api/pay/create-for-app
Authorization: Bearer <JWT token>
Content-Type: application/json

{
  "out_trade_no": "YIJIAN20260607001",
  "description": "1元建站 - 会员",
  "amount": 100,
  "pay_type": "native",
  "notify_url_b": ""    // 可选,支付成功通知地址
}

→ {
  "code": 0,
  "message": "ok",
  "data": {
    "prepay_id": "",
    "pay_params": {
      "code_url": "weixin://wxpay/bizpayurl?pr=xxx"
    }
  }
}
字段类型必填说明
out_trade_nostring你的订单号。建议 产品前缀+日期+序号
descriptionstring商品描述,显示在微信支付页
amountint金额,单位分(100=1元)
pay_typestringjsapinative
openidstringjsapi必填用户微信 openid
notify_url_bstring支付成功通知地址

1 元建站完整接入代码

// 1. QR 扫码登录(已有 token 则跳过)
const startResp = await fetch('https://userpay.aitalker.vip/api/user/qr-login/start', {method:'POST'});
const {key, qr_url} = (await startResp.json()).data;

// 生成二维码显示给用户扫码
new QRCode(document.getElementById('qrcode'), {text: qr_url, width: 200, height: 200});

// 轮询等待扫码结果
const timer = setInterval(async () => {
  const r = await fetch(`https://userpay.aitalker.vip/api/user/qr-login/check?key=${key}`);
  const {status, token, user_id} = (await r.json()).data;
  if (status === 'done') {
    clearInterval(timer);
    localStorage.setItem('token', token);   // 存 token

    // 2. 用 token 直接下单(不需要 X-API-Key,不需要传 user_id)
    const payResp = await fetch('https://userpay.aitalker.vip/api/pay/create-for-app', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({
        out_trade_no: 'YIJIAN' + Date.now(),
        description: '1元建站 - 会员',
        amount: 100,        // 100 分 = 1 元
        pay_type: 'native'  // 扫码付
      })
    });
    const {pay_params} = (await payResp.json()).data;
    // pay_params.code_url 就是微信支付二维码链接
    new QRCode(document.getElementById('pay-qrcode'), {text: pay_params.code_url, width: 200, height: 200});
  } else if (status === 'expired') {
    clearInterval(timer);
    alert('二维码已过期,请刷新页面重试');
  }
}, 2000);

查询订单

GET/api/pay/query?out_trade_no=MYAPP20260606001
X-API-Key: <api-key>

→ {
  "out_trade_no": "...",
  "trade_state": "SUCCESS",
  "transaction_id": "420000...",
  "paid_amount": 990,
  "paid_at": "2026-06-06T13:07:49",
  "source": "wechat"
}

trade_stateNOTPAY / SUCCESS / REFUND / CLOSED
source: "wechat" 表示微信实时查询,"local" 表示微信失败用了缓存。

退款

POST/api/pay/refund
X-API-Key: <api-key>
Content-Type: application/json

{
  "out_trade_no": "MYAPP20260606001",
  "refund_amount": 990,
  "reason": "用户申请退款"
}

可部分退款。订单必须 SUCCESS

支付回调(你接的)

支付成功后,中台 POST 到你传的 notify_url_b

POST https://你的域名/callback
Content-Type: application/json

{
  "out_trade_no": "MYAPP20260606001",
  "transaction_id": "4200001234567890",
  "amount": 990,
  "user_id": 1,
  "trade_state": "SUCCESS"
}
要求:返回 HTTP 200。失败每 5 分钟重试,最多 10 次。

应用平台 API

外部应用通过 X-App-Key 头鉴权调用。密钥从后台管理 App 获取。

通用设计:这些接口按 app_id 隔离,同一套 API 支撑多个产品(一言建站、知识库等),每个 App 有独立额度和密钥。

查询用户额度

GET/api/app/user/credits?external_user_id=xxx
X-App-Key: <app-key>

→ {
  "code": 0,
  "data": {
    "credits": 100,
    "credits_used": 30,
    "available": 70
  }
}

用户不存在时返回全 0。

扣减额度

POST/api/app/user/credits/deduct
X-App-Key: <app-key>
Content-Type: application/json

{
  "external_user_id": "user_abc",
  "amount": 1,
  "action": "generate",
  "reason": "生成文章"
}
字段类型必填说明
external_user_idstring你系统的用户标识
amountint扣减点数
actionstring业务场景:generate / edit,默认 deduct
reasonstring原因备注

增加额度(管理员)

POST/api/app/user/credits/add
X-App-Key: <app-key>
Content-Type: application/json

{
  "external_user_id": "user_abc",
  "amount": 100,
  "reason": "初始赠送"
}

应用用户列表

GET/api/app/users?page=1&size=50&search=xxx
X-App-Key: <app-key>

→ {
  "code": 0,
  "data": {
    "total": 3,
    "users": [{ "external_user_id": "...", "credits": 100, ... }]
  }
}

额度变更日志

GET/api/app/credits/logs?external_user_id=xxx&page=1&size=50
X-App-Key: <app-key>

→ {
  "code": 0,
  "data": {
    "total": 12,
    "logs": [{ "action": "deduct", "change_amount": -1, ... }]
  }
}

不传 external_user_id 则查全应用日志。

管理后台 API

仅供中台管理员使用,应用开发方接入支付/用户接口不需要调用本节。

鉴权方式:浏览器 Cookieadmin_token)。流程:访问 /admin/login → 微信扫码 → 中台校验该用户是否为管理员 → 设置 Cookie。不再支持 X-API-Key 调用管理接口。

管理员登录

GET/admin/login

返回二维码扫码页面。仅 ADMIN_USER_IDS 或后台「设为管理员」的用户可登录。

GET/admin/check

检查登录状态(需 Cookie)。返回 {id, nickname, avatar_url, is_super}

用户管理

GET/admin/api/users?page=1&size=20&search=xxx

需管理员 Cookie。

→ {
  "code": 0,
  "data": {
    "total": 3,
    "users": [{
      "id": 3,
      "phone": "138****1234",
      "nickname": "囍",
      "has_openid": true,
      "is_admin": true,
      "admin_via_env": true,
      "avatar_url": "https://...",
      "province": "上海"
    }]
  }
}

search 可选。后台 UI 可对用户「设为管理员」/「取消管理员」。

GET/admin/api/users/{user_id}

用户详情 + 最近 50 条订单。需管理员 Cookie。

POST/admin/api/users/{user_id}/grant-admin

设为管理员。用户须已绑定微信(有 openid)。需管理员 Cookie。

POST/admin/api/users/{user_id}/revoke-admin

取消管理员。不能取消自己或唯一管理员。若仍在 ADMIN_USER_IDS 中会提示改 .env。需管理员 Cookie。

应用管理

GET/admin/api/apps

列出所有应用,含用户数。需管理员 Cookie。

POST/admin/api/apps/create
Content-Type: application/json

{
  "name": "一言建站",
  "description": "...",
  "callback_url": "https://yiyan.aitalker.vip/api/pay/callback"
}

返回 {id, name, api_key}api_key 仅创建时返回一次,给应用服务端作 X-App-Key 用。

GET/admin/api/apps/{app_id}

应用详情,含用户额度列表。需管理员 Cookie。

PUT/admin/api/apps/{app_id}

更新应用名称 / 说明 / 回调地址。需管理员 Cookie。

手动调整额度

POST/admin/api/apps/{app_id}/credits/adjust
Content-Type: application/json

{
  "external_user_id": "user_abc",
  "amount": 100,
  "reason": "用户充值"
}

amount 正数=增加,负数=扣减。需管理员 Cookie。

概览统计

GET/admin/api/stats

总用户数、订单数、收入等。需管理员 Cookie。

订单管理

GET/admin/api/orders?page=1&size=20&app_id=0&date_from=&date_to=&trade_state=

订单列表,支持应用/日期/状态筛选。需管理员 Cookie。

管理员认证

POST/admin/qr-auth
Content-Type: application/json

{ "token": "用户 JWT Token", "headimgurl": "头像URL(可选)" }

QR 登录成功后,用用户 JWT 换取管理员 Cookie。仅管理员用户成功。

POST/admin/logout

清除管理员 Cookie。需管理员 Cookie。

错误码

HTTPdetail原因
400手机号已注册 / 订单号已存在 / 用户不存在参数错误
401无效的 API Key / Token 无效或已过期鉴权失败
429请求过于频繁速率限制
502微信支付接口调用失败微信端异常
503支付服务未配置中台没配微信支付