| 项目 | 值 |
|---|---|
| API Base | https://softapi.openai2000.cn |
| 接口版本 | 0.1.0 |
| 认证方式 | JWT Bearer Token |
| 签名算法 | MD5(app级接口必须) |
| 签名时效 | 5分钟 |
sign = MD5(app_id + sorted_params + app_key + timestamp)
app_id + 排序后的 key1value1key2value2... + app_key + timestampsign 和 timestamp 都放在 body 中import hashlib
import time
def make_sign(app_id: str, app_key: str, params: dict) -> dict:
timestamp = str(int(time.time()))
# 排序参数(排除sign和timestamp)
keys = sorted([k for k in params if k not in ('sign', 'timestamp')])
raw = app_id + ''.join(f"{k}{params[k]}" for k in keys) + app_key + timestamp
sign = hashlib.md5(raw.encode()).hexdigest()
return {"sign": sign, "timestamp": timestamp}
# 使用示例
app_id = "APP1756000000123"
app_key = "your_app_key_here"
params = {"app_id": app_id, "username": "test", "password": "12345678"}
result = make_sign(app_id, app_key, params)
# result = {"sign": "abc123...", "timestamp": "1756000000"}
const crypto = require('crypto');
function makeSign(appId, appKey, params) {
const timestamp = Math.floor(Date.now() / 1000).toString();
// 排序参数(排除sign和timestamp)
const keys = Object.keys(params)
.filter(k => k !== 'sign' && k !== 'timestamp')
.sort();
const raw = appId + keys.map(k => k + params[k]).join('') + appKey + timestamp;
const sign = crypto.createHash('md5').update(raw).digest('hex');
return { sign, timestamp };
}
// 使用示例
const appId = "APP1756000000123";
const appKey = "your_app_key_here";
const params = { app_id: appId, username: "test", password: "12345678" };
const result = makeSign(appId, appKey, params);
// result = { sign: "abc123...", timestamp: "1756000000" }
POST /api/app/user/register
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | ✅ | 软件ID |
| username | string | ✅ | 用户名(3-20字符) |
| password | string | ✅ | 密码(至少8位) |
| string | ❌ | 邮箱(可选,用于找回密码等) | |
| sign | string | ✅ | 签名 |
| timestamp | string | ✅ | 时间戳(秒) |
POST /api/app/user/login
参数同注册。返回:
{
"code": 200,
"msg": "登录成功",
"data": {
"token": "jwt_token_xxx",
"user_id": 123,
"username": "test",
"vip_type": 2, // 0普通 1日卡 2月卡 3年卡 4永久
"vip_expire_time": "2026-12-31 23:59:59"
}
}
GET /api/app/user/auth?app_id=xxx
Header: Authorization: Bearer {token}
POST /api/app/recharge/create
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | ✅ | 软件ID |
| token | string | ✅ | 用户登录token |
| goods_type | int | ✅ | 套餐类型:1日卡 / 2月卡 / 3年卡 / 4永久 |
| sign | string | ✅ | 签名 |
| timestamp | string | ✅ | 时间戳(秒) |
套餐价格对应表:
| goods_type | 套餐 | 默认价格 |
|---|---|---|
| 1 | 日卡 | 9.9元 |
| 2 | 月卡 | 29.9元 |
| 3 | 年卡 | 199.9元 |
| 4 | 永久 | 520元 |
返回示例:
{
"code": 200,
"msg": "下单成功",
"data": {
"order_sn": "SA216947891234abc1",
"amount": 520,
"pay_url": "weixin://wxpay/bizpayurl?pr=xxxx"
}
}
pay_url 就是微信扫码支付的 code_url,客户端生成二维码让用户扫码即可订单查询 接口获取支付结果GET /api/app/recharge/query
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | ✅ | 软件ID |
| token | string | ✅ | 用户token |
| order_sn | string | ✅ | 下单返回的订单号 |
返回示例:
{
"code": 200,
"msg": "ok",
"data": {
"order_sn": "SA2xxxx",
"amount": 520,
"goods_type": 4,
"status": 1,
"pay_time": "2026-09-15 12:34:56"
}
}
说明:status 0=未支付,1=已支付
GET /api/app/recharge/list
Header: Authorization: Bearer {token}
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | ✅ | 软件ID |
| page | int | ❌ | 页码,默认1 |
| page_size | int | ❌ | 每页数量,默认20 |
POST 你的软件 notify_url(添加软件时填写)
当用户支付成功后,平台会主动POST通知你的服务端。
| 参数 | 类型 | 说明 |
|---|---|---|
| app_id | string | 软件ID |
| order_sn | string | 订单号 |
| amount | float | 支付金额 |
| goods_type | int | 套餐类型 |
| user_id | int | 你的软件用户ID |
| status | int | 固定为1(支付成功) |
| timestamp | string | 时间戳 |
| sign | string | 签名,请务必验签确认 |
验签算法:
sign = MD5(
app_id +
f"amount{amount}" +
f"goods_type{goods_type}" +
f"order_sn{order_sn}" +
f"status1" +
f"timestamp{timestamp}" +
f"user_id{user_id}" +
app_key +
timestamp
)
GET /api/captcha
返回:
{
"code": 200,
"msg": "获取成功",
"data": {
"captcha_id": "a1b2c3d4e5f6g7h8",
"image": "data:image/png;base64,iVBORw0KGgo..."
}
};
captcha_id 用于后续校验,有效期5分钟image 是base64编码的PNG图片,可直接用于 <img src="...">POST /api/captcha/verify
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| captcha_id | string | ✅ | 验证码ID(从获取接口返回) |
| captcha_code | string | ✅ | 用户输入的验证码 |
返回:
{
"code": 200,
"msg": "校验成功",
"data": { "valid": true }
}
POST /api/email/captcha/send
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | ✅ | 用户邮箱地址 |
返回:
{
"code": 200,
"msg": "发送成功,请查收",
}
POST /api/email/captcha/verify
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | ✅ | 用户邮箱地址 | |
| captcha_code | string | ✅ | 用户输入的验证码 |
返回:
{
"code": 200,
"msg": "校验成功",
"data": { "valid": true }
}
| goods_type | 套餐名称 | 天数 | 默认价格 |
|---|---|---|---|
| 1 | 日卡 | 1天 | 9.9元 |
| 2 | 月卡 | 30天 | 29.9元 |
| 3 | 年卡 | 365天 | 199.9元 |
| 4 | 永久授权 | 永久 | 520元 |
平台支持为每个软件单独设置价格,你的软件当前定价:
| 日卡 | 月卡 | 年卡 | 永久 |
|---|---|---|---|
| 1.9元 | 29.9元 | 199.9元 | 520元 |
如需修改价格,请联系平台管理员在后台修改。
┌─────────────────────────────────────────────────────────────┐
│ 客户端启动流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. 检查本地是否保存了 token │
│ └─ 无 → 显示登录界面 │
│ │
│ 2. 调用 /api/app/user/auth 校验 token │
│ ├─ 200 → 进入主界面,根据 vip_type 开启功能 │
│ └─ 401/403 → 跳转登录页 │
│ │
│ 3. 登录/注册 │
│ ├─ POST /api/app/user/register(带签名) │
│ └─ POST /api/app/user/login(带签名) │
│ │
│ 4. 充值 │
│ ├─ POST /api/app/recharge/create(带签名) │
│ ├─ 展示微信二维码 │
│ └─ 轮询 /api/app/recharge/query 等待支付结果 │
└─────────────────────────────────────────────────────────────┘
import hashlib
import time
import requests
class SoftapiClient:
def __init__(self, base_url, app_id, app_key):
self.base_url = base_url
self.app_id = app_id
self.app_key = app_key
def _make_sign(self, params):
timestamp = str(int(time.time()))
keys = sorted([k for k in params if k not in ('sign', 'timestamp')])
raw = self.app_id + ''.join(f"{k}{params[k]}" for k in keys) + self.app_key + timestamp
sign = hashlib.md5(raw.encode()).hexdigest()
return sign, timestamp
def register(self, username, password):
params = {"app_id": self.app_id, "username": username, "password": password}
sign, timestamp = self._make_sign(params)
params.update({"sign": sign, "timestamp": timestamp})
return requests.post(f"{self.base_url}/api/app/user/register", json=params).json()
def login(self, username, password):
params = {"app_id": self.app_id, "username": username, "password": password}
sign, timestamp = self._make_sign(params)
params.update({"sign": sign, "timestamp": timestamp})
return requests.post(f"{self.base_url}/api/app/user/login", json=params).json()
def auth(self, token):
headers = {"Authorization": f"Bearer {token}"}
return requests.get(f"{self.base_url}/api/app/user/auth?app_id={self.app_id}", headers=headers).json()
def create_order(self, token, goods_type):
params = {"app_id": self.app_id, "token": token, "goods_type": str(goods_type)}
sign, timestamp = self._make_sign(params)
params.update({"sign": sign, "timestamp": timestamp})
return requests.post(f"{self.base_url}/api/app/recharge/create", json=params).json()
# 使用
client = SoftapiClient("https://softapi.openai2000.cn", "APPxxx", "your_app_key")
result = client.login("test", "12345678")
print(result)
const crypto = require('crypto');
const axios = require('axios');
class SoftapiClient {
constructor(baseUrl, appId, appKey) {
this.baseUrl = baseUrl;
this.appId = appId;
this.appKey = appKey;
}
_makeSign(params) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const keys = Object.keys(params)
.filter(k => k !== 'sign' && k !== 'timestamp')
.sort();
const raw = this.appId + keys.map(k => k + params[k]).join('') + this.appKey + timestamp;
const sign = crypto.createHash('md5').update(raw).digest('hex');
return { sign, timestamp };
}
async register(username, password) {
const params = { app_id: this.appId, username, password };
const { sign, timestamp } = this._makeSign(params);
const res = await axios.post(`${this.baseUrl}/api/app/user/register`, {
...params, sign, timestamp
});
return res.data;
}
async login(username, password) {
const params = { app_id: this.appId, username, password };
const { sign, timestamp } = this._makeSign(params);
const res = await axios.post(`${this.baseUrl}/api/app/user/login`, {
...params, sign, timestamp
});
return res.data;
}
async auth(token) {
const res = await axios.get(`${this.baseUrl}/api/app/user/auth?app_id=${this.appId}`, {
headers: { Authorization: `Bearer ${token}` }
});
return res.data;
}
async createOrder(token, goodsType) {
const params = { app_id: this.appId, token, goods_type: goodsType.toString() };
const { sign, timestamp } = this._makeSign(params);
const res = await axios.post(`${this.baseUrl}/api/app/recharge/create`, {
...params, sign, timestamp
});
return res.data;
}
}
// 使用
const client = new SoftapiClient("https://softapi.openai2000.cn", "APPxxx", "your_app_key");
client.login("test", "12345678").then(console.log);
// 完整 TypeScript SDK 请下载:
// /opt/software_auth/docs/softapi-client-sdk.ts
import crypto from 'crypto';
interface SoftapiConfig {
baseUrl: string;
appId: string;
appKey: string;
}
export class SoftapiClient {
private config: SoftapiConfig;
constructor(config: SoftapiConfig) {
this.config = config;
}
private makeSign(params: Record) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const keys = Object.keys(params)
.filter(k => k !== 'sign' && k !== 'timestamp')
.sort();
const raw = this.config.appId + keys.map(k => k + params[k]).join('') + this.config.appKey + timestamp;
const sign = crypto.createHash('md5').update(raw).digest('hex');
return { sign, timestamp };
}
// ... 更多方法见 SDK 文件
}