Premium API

Roxera Premium — подписка на проекты Roxera: $5 за 30 дней или 250 Telegram Stars. Автопродления нет: каждая оплата добавляет 30 дней к текущему сроку.

Параметр Free Premium
Цена 0 $5 / 30 дней · 250 XTR
Почтовые ящики 1 3
Хранилище почты 1 ГБ 10 ГБ
Временная почта 15 минут · 1 адрес 24 часа · 5 адресов
Uptime 50 мониторов · 5 минут 200 мониторов · 1 минута
Links 100 активных ссылок 5 000 активных ссылок
QR 3 динамических без лимита
Proxy 10 ГБ · 7 дней 20 ГБ · 14 дней
Zonu.org 1 домен 3 домена

Публичные endpoint’ы

GET https://premium.roxera.net/api/v1/premium/plans
GET https://premium.roxera.net/api/v1/premium/projects

plans возвращает цены и полный объект лимитов. projects — включённые проекты из реестра (ключ, имя, URL, описание, иконка). Classroom в реестр не входит.

{
  "plans": [
    { "code": "free", "name": "Free", "priceUsd": 0, "periodDays": 0 },
    { "code": "premium", "name": "Premium", "priceUsd": 5, "priceStars": 250, "periodDays": 30 }
  ],
  "currency": "USD",
  "starsCurrency": "XTR"
}

Проверка для first-party проекта

Проекты на поддоменах .roxera.net используют общую cookie. Передайте её на API — и получите статус и лимиты без OIDC-редиректа.

POST https://mail.roxera.net/api/v1/premium/check
Content-Type: application/json

{ "projectKey": "uptime" }
{
  "projectKey": "uptime",
  "premium": true,
  "tier": "premium",
  "until": "2029-06-24T12:58:14.476Z",
  "limits": { "monitors": 200, "intervalSec": 60, "historyDays": 365 },
  "entitlements": {
    "mail": { "boxes": 3, "storageGb": 10 },
    "uptime": { "monitors": 200, "intervalSec": 60 },
    "link": { "activeLinks": 5000 }
  }
}

Есть и короткая GET-версия для UI-бейджа:

GET https://mail.roxera.net/api/v1/premium/check/uptime

Ответ содержит premium, tier, until и limits для указанного проекта.

Проверка с сервера (ключ проекта)

Для вызовов «сервис→хаб» без cookie выпустите ключ проекта в админке (/admin/#projects → «Ключи»). Ключ имеет вид rx_pk_..., в базе хранится только его хэш, значение видно один раз. Передайте его в заголовке Authorization: Bearer:

POST https://mail.roxera.net/api/v1/premium/check
Authorization: Bearer rx_pk_xxx_yyy
Content-Type: application/json

{ "email": "user@example.com", "metric": "monitors", "amount": 1 }
{
  "ok": true,
  "projectKey": "uptime",
  "tier": "premium",
  "limit": 200,
  "used": 8,
  "remaining": 192,
  "allowed": true
}

used — дневной счётчик (usage_counters, сброс в 00:00 UTC), allowed становится false при превышении лимита. Ключ можно ограничить набором метрик (scopes): чужие метрики получают 403 scope_required.

Серверный middleware

import type { NextFunction, Request, Response } from 'express';

interface PremiumCheck {
  premium: boolean;
  tier: 'free' | 'premium';
  until: string | null;
  limits: { monitors: number; intervalSec: number } | null;
}

export async function requirePremiumUptime(req: Request, res: Response, next: NextFunction) {
  const response = await fetch('https://mail.roxera.net/api/v1/premium/check', {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      cookie: req.headers.cookie ?? '',
    },
    body: JSON.stringify({ projectKey: 'uptime' }),
  });

  if (!response.ok) return res.status(401).json({ error: 'unauthorized' });

  const premium = (await response.json()) as PremiumCheck;
  if (!premium.premium) {
    return res.status(402).json({
      error: 'premium_required',
      upgradeUrl: 'https://premium.roxera.net/#pricing',
    });
  }

  res.locals.premium = premium;
  next();
}

Ограничение по метрике

Лимиты — это числа, а не только флаг. Проверяйте количество ресурсов проекта:

const check = await response.json();
const used = await db.countMonitors(user.id);
if (used >= check.limits.monitors) {
  return res.status(402).json({ error: 'limit_reached', limit: check.limits.monitors });
}

Счётчики использования (usage_counters) ведутся внутри проекта. Roxera не хранит чужие мониторы и ссылки — только статус подписки и entitlements.

Внешний проект через OAuth

Если проект не на .roxera.net, cookie недоступна. Проверяйте ID token или запрашивайте userinfo с access token:

const response = await fetch('https://oauth.roxera.net/oauth/userinfo', {
  headers: { authorization: `Bearer ${accessToken}` },
});
const profile = await response.json();

if (profile.active) {
  // Premium активен до profile.until
}

В ID token уже есть claims premium, premium_until, plan. Для UI достаточно premium; для выдачи лимитов используйте entitlements из userinfo.

Кэшируйте результат на 60 секунд по sub + until. Не запрашивайте userinfo на каждый HTTP-запрос.

Реестр проектов

Проект Ключ URL
Roxera Mail mail https://mail.roxera.net
Roxera Uptime uptime https://uptime.roxera.net
Roxera Links link https://link.s-g.lol
Roxera Cases case https://case.roxera.net
Roxera Proxy proxy https://proxy.roxera.net
Roxera QR qr https://qr.roxera.net
Zonu.org domains https://nic.zonu.org
Roxera Premium premium https://premium.roxera.net

Список хранится в SQLite и редактируется в админке (/admin/#projects). Лендинг подтягивает его через /api/v1/premium/projects без пересборки.

Оплата

Способ Сумма Endpoint
Telegram Stars 250 XTR POST /api/v1/premium/checkout/stars
CryptoBot 5 USDT POST /api/v1/premium/checkout/crypto

Оба endpoint’а требуют cookie Roxera ID. Ответ содержит invoiceUrl (Stars) или payUrl (CryptoBot). После оплаты статус обновляется автоматически; в личном кабинете есть кнопка «Обновить статус».

const response = await fetch('/api/v1/premium/checkout/stars', {
  method: 'POST',
  credentials: 'include',
});
const { invoiceUrl } = await response.json();
window.location.href = invoiceUrl;

Ручные продления и возвраты выполняет администратор — каждое действие попадает в audit log. Промокодов, рефералок и автосписаний нет.

Личные ключи и вебхуки

В кабинете (/account) пользователь может выпустить личный API-ключ (rx_k_..., область premium:read) и добавить вебхук на событие payment.paid.

Личный ключ заменяет cookie на GET /api/v1/premium/status и POST /api/v1/premium/check — для скриптов самого пользователя:

GET https://mail.roxera.net/api/v1/premium/status
Authorization: Bearer rx_k_xxx_yyy

Вебхук получает POST при оплате Premium. Тело подписано HMAC-SHA256 секретом вебхука (значение видно один раз при создании):

POST https://example.com/roxera
X-Roxera-Event: payment.paid
X-Roxera-Signature: sha256=<hex hmac>
Content-Type: application/json

{ "event": "payment.paid", "deliveryId": "...", "sentAt": "...", "data": { ... } }

Доставку выполняет воркер: только https, с защитой от SSRF (проверка разрешённого IP перед запросом). При не-2xx ответе запрос повторяется; последний статус виден в кабинете.

Как добавить новый проект

  1. Добавьте запись в projects через админку (/admin/#projects) с уникальным key.
  2. Добавьте лимиты в packages/shared/src/plans.ts для Free и Premium.
  3. Создайте OAuth-клиент и redirect URI в /admin/#oauth.
  4. В проекте проверяйте /api/v1/premium/check (если он на .roxera.net) или userinfo (если внешний).
  5. Ограничьте функции по limits, а не по названию тарифа.