215 lines
9.6 KiB
Markdown
215 lines
9.6 KiB
Markdown
|
|
# Fastcheck Backend — требования к серверу
|
|||
|
|
|
|||
|
|
Документ для команды бэкенда. Описывает, что должен реализовать сервер `api.fastcheck.store`, чтобы веб-фронт (этот репозиторий) полностью заработал.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Общие требования
|
|||
|
|
|
|||
|
|
### 1.1 Транспорт
|
|||
|
|
- **Протокол**: HTTPS обязателен (валидный TLS-сертификат, Let's Encrypt или иной).
|
|||
|
|
- **Хост**: `api.fastcheck.store` (или другой — тогда поправить `FASTCHECK_API` в `src/app/api.ts`).
|
|||
|
|
- **Формат тел запроса/ответа**: `application/json; charset=utf-8`.
|
|||
|
|
|
|||
|
|
### 1.2 CORS — **критично**
|
|||
|
|
Браузер фронта пойдёт с другого origin. Без правильных CORS-заголовков **ничего не заработает** (preflight упадёт, fetch вернёт network error — ровно то, что мы видим сейчас).
|
|||
|
|
|
|||
|
|
Сервер должен на любой `OPTIONS` (preflight) и на ответы реальных запросов отдавать:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Access-Control-Allow-Origin: https://<домен-фронта> # либо * для dev
|
|||
|
|
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS
|
|||
|
|
Access-Control-Allow-Headers: Content-Type, Authorization
|
|||
|
|
Access-Control-Max-Age: 86400
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Если используются cookies/credentials — добавить `Access-Control-Allow-Credentials: true` и **нельзя** использовать `*` в `Allow-Origin`.
|
|||
|
|
|
|||
|
|
`OPTIONS` должен отвечать `204 No Content` без тела.
|
|||
|
|
|
|||
|
|
### 1.3 Авторизация
|
|||
|
|
Заголовок передаётся как **JSON-строка**, а не Bearer:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Authorization: {"sessionID":"1AF3781BF6B94604B771AEA1D44FA63A"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Парсинг на сервере: `JSON.parse(req.headers.authorization)` → `{ sessionID }`.
|
|||
|
|
Если заголовок отсутствует или сессия невалидна — `404 { "message": "not authorized" }`.
|
|||
|
|
|
|||
|
|
### 1.4 Ошибки
|
|||
|
|
Любая ошибка — JSON `{ "message": "<человекочитаемое описание>" }` + HTTP-статус (4xx/5xx). Фронт показывает `message` пользователю.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Эндпоинты
|
|||
|
|
|
|||
|
|
База: `https://api.fastcheck.store`
|
|||
|
|
|
|||
|
|
### 2.1 `GET /ping`
|
|||
|
|
Healthcheck. Ответ: `200 { "message": "pong" }`. Без авторизации.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.2 `GET /websession`
|
|||
|
|
Создаёт новую веб-сессию для QR-логина через Telegram-бот.
|
|||
|
|
|
|||
|
|
**Запрос**: без тела, без авторизации.
|
|||
|
|
|
|||
|
|
**Ответ** `200`:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
|
|||
|
|
"userId": "",
|
|||
|
|
"expires": "sessionId",
|
|||
|
|
"userSessionId": "",
|
|||
|
|
"Status": false
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`sessionId` фронт подставляет в QR-код и в deeplink на бот:
|
|||
|
|
`https://t.me/DexarSupport_bot?start=<sessionId>`
|
|||
|
|
|
|||
|
|
**TTL сессии**: рекомендуем 5–10 минут. По истечении `GET /websession/:id` должен вернуть `Status: false` навсегда (фронт сам пересоздаст).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.3 `GET /websession/:webSessionID`
|
|||
|
|
Поллинг статуса логина. Фронт зовёт каждые **3 секунды**, пока попап открыт.
|
|||
|
|
|
|||
|
|
**Ответ** `200` пока пользователь не залогинился:
|
|||
|
|
```json
|
|||
|
|
{ "sessionId": "...", "userId": "", "expires": "sessionId", "userSessionId": "", "Status": false }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ответ** `200` после того, как Telegram-бот подтвердил вход:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
|
|||
|
|
"userId": "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo",
|
|||
|
|
"expires": "sessionId",
|
|||
|
|
"userSessionId": "8A94EFEFD003426A9B456C48CAC99BE6",
|
|||
|
|
"Status": true
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Если сессия не найдена/истекла — `404 { "message": "session expired" }`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.4 `DELETE /websession/:webSessionID`
|
|||
|
|
Logout / закрытие попапа.
|
|||
|
|
|
|||
|
|
**Запрос**:
|
|||
|
|
```json
|
|||
|
|
{ "sessionId": "1AF3781BF6B94604B771AEA1D44FA63A" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ответ** `200 {}`. Идемпотентно — повторный вызов не должен ломаться.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.5 `GET /fastcheck`
|
|||
|
|
Проверка существования и срока действия чека (используется опционально перед оплатой).
|
|||
|
|
|
|||
|
|
**Запрос** (тело в GET):
|
|||
|
|
```json
|
|||
|
|
{ "fastcheck": "1234-5678-0001" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ответ** `200`:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"fastcheck": "1234-5678-0001",
|
|||
|
|
"expiration": "2026-07-07T09:08:18Z",
|
|||
|
|
"Status": true
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Не существует / просрочен → `404 { "message": "not found" }`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.6 `POST /fastcheck` — **создание чека**
|
|||
|
|
Юзер уже залогинен, его `sessionID` есть на фронте. С этого аккаунта списывается `amount`, выпускается чек.
|
|||
|
|
|
|||
|
|
**Заголовок**: `Authorization: {"sessionID":"..."}` обязателен.
|
|||
|
|
|
|||
|
|
**Тело**:
|
|||
|
|
```json
|
|||
|
|
{ "amount": 158000, "currency": "RUB" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`amount` — в минимальных единицах валюты (копейки для RUB). Уточнить с фронтом, если иначе.
|
|||
|
|
|
|||
|
|
**Ответ** `200`:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"fastcheck": "1234-5678-0001",
|
|||
|
|
"expiration": "2026-07-07T09:08:18Z",
|
|||
|
|
"code": "5864",
|
|||
|
|
"Status": true
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ошибки**:
|
|||
|
|
- `404 { "message": "not authorized" }` — нет/невалидная сессия.
|
|||
|
|
- `400 { "message": "insufficient balance" }` — мало средств.
|
|||
|
|
- `400 { "message": "invalid amount" }` — некорректная сумма/валюта.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.7 `POST /fastcheck` — **приём чека (оплата)**
|
|||
|
|
Этот же путь, отличается шейпом тела (без `amount`, с `code`).
|
|||
|
|
|
|||
|
|
**Заголовок**: `Authorization: {"sessionID":"..."}` обязателен (сессия получателя — того, кто оплачивает).
|
|||
|
|
|
|||
|
|
**Тело**:
|
|||
|
|
```json
|
|||
|
|
{ "fastcheck": "1234-5678-0001", "code": "5864" }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ответ** `200 { "message": "ok" }` — чек погашен, средства зачислены получателю.
|
|||
|
|
|
|||
|
|
**Ошибки**:
|
|||
|
|
- `404 { "message": "not authorized" }` — сессия невалидна **или** код неверный, **или** чек уже использован, **или** просрочен. (Так в текущей доке. Если можно различать — лучше отдельные сообщения.)
|
|||
|
|
|
|||
|
|
> ⚠️ Серверу важно различать два POST-кейса по наличию поля `amount` vs `code` в теле. Альтернатива (предпочтительнее на проде) — развести на разные пути: `POST /fastcheck` (создание) и `POST /fastcheck/accept` (приём). Если разведёте — скажите, фронт правится за 5 минут.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Интеграция с Telegram-ботом
|
|||
|
|
|
|||
|
|
Фронт сам бот не дёргает — это задача бэкенда.
|
|||
|
|
|
|||
|
|
1. Юзер сканит QR / кликает deeplink → попадает в бот `@DexarSupport_bot` с параметром `?start=<sessionId>`.
|
|||
|
|
2. Бот идентифицирует Telegram-аккаунт (по `from.id`) → находит/создаёт `userId` → биндит его к `sessionId` → ставит `Status: true`, заполняет `userId` и `userSessionId`.
|
|||
|
|
3. Следующий поллинг с фронта вернёт `Status: true` — фронт переходит к `POST /fastcheck`.
|
|||
|
|
|
|||
|
|
Если юзер впервые в боте — стандартный onboarding, потом всё то же самое.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Чеклист «готово к проду»
|
|||
|
|
|
|||
|
|
- [ ] HTTPS с валидным сертификатом на `api.fastcheck.store`.
|
|||
|
|
- [ ] CORS разрешает домен фронта на всех 6 эндпоинтах + OPTIONS.
|
|||
|
|
- [ ] `GET /ping` отвечает.
|
|||
|
|
- [ ] Полный цикл: `GET /websession` → бот ставит `Status:true` → `GET /websession/:id` это видит.
|
|||
|
|
- [ ] `POST /fastcheck` (create) с заголовком `Authorization` создаёт чек, списывает баланс.
|
|||
|
|
- [ ] `POST /fastcheck` (accept) погашает чек только один раз, зачисляет получателю.
|
|||
|
|
- [ ] `DELETE /websession/:id` корректно завершает сессию.
|
|||
|
|
- [ ] Все ошибки в формате `{ "message": "..." }` + правильный HTTP-код.
|
|||
|
|
- [ ] Сессии экспайрятся (5–10 мин для websession, разумный TTL для userSession).
|
|||
|
|
- [ ] Rate-limit на `GET /websession/:id` (фронт поллит каждые 3 с) и на `POST /fastcheck`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Открытые вопросы (нужны ответы от бэкенда)
|
|||
|
|
|
|||
|
|
1. **Единица `amount`**: рубли или копейки?
|
|||
|
|
2. **Currency**: какие коды поддерживаете кроме `RUB`? (фронт уже умеет показывать, но шлёт пока только RUB)
|
|||
|
|
3. **Merchant callback** для эквайринга: после успешного `POST /fastcheck (accept)` нужно ли серверу самому пинговать мерчант-вебхук, или это полностью на фронте через `?return_url=`?
|
|||
|
|
4. **Различение ошибок accept**: можно ли вместо общего `404 not authorized` отдавать `not found` / `wrong code` / `already used` / `expired`?
|
|||
|
|
5. **WebSession TTL** — сколько живёт?
|