created auth system
This commit is contained in:
@@ -1,168 +1,266 @@
|
||||
# Backend API Changes Required
|
||||
# API Changes Required for Backend
|
||||
|
||||
## Cart Quantity Support
|
||||
## Overview
|
||||
|
||||
### 1. Add Quantity to Cart Items
|
||||
Frontend has been updated with two new features:
|
||||
1. **Region/Location system** — catalog filtering by region
|
||||
2. **Auth/Login system** — Telegram-based authentication required before payment
|
||||
|
||||
**Current GET /cart Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"itemID": 123,
|
||||
"name": "Product Name",
|
||||
"price": 100,
|
||||
"currency": "RUB",
|
||||
...other item fields
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**NEW Required Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"itemID": 123,
|
||||
"name": "Product Name",
|
||||
"price": 100,
|
||||
"currency": "RUB",
|
||||
"quantity": 2, // <-- ADD THIS FIELD
|
||||
...other item fields
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 2. POST /cart - Add Item to Cart
|
||||
|
||||
**Current Request:**
|
||||
```json
|
||||
{
|
||||
"itemID": 123
|
||||
}
|
||||
```
|
||||
|
||||
**NEW Request (with optional quantity):**
|
||||
```json
|
||||
{
|
||||
"itemID": 123,
|
||||
"quantity": 1 // Optional, defaults to 1 if not provided
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- If item already exists in cart, **increment** the quantity by the provided amount
|
||||
- If item doesn't exist, add it with the specified quantity
|
||||
|
||||
### 3. PATCH /cart - Update Item Quantity (NEW ENDPOINT)
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"itemID": 123,
|
||||
"quantity": 5 // New quantity value (not increment, but absolute value)
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "Cart updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- Set the quantity to the exact value provided
|
||||
- If quantity is 0 or negative, remove the item from cart
|
||||
|
||||
### 4. Payment Endpoints - Include Quantity
|
||||
|
||||
**POST /payment/create**
|
||||
|
||||
Update the items array to include quantity:
|
||||
|
||||
**Current:**
|
||||
```json
|
||||
{
|
||||
"amount": 1000,
|
||||
"currency": "RUB",
|
||||
"items": [
|
||||
{
|
||||
"itemID": 123,
|
||||
"price": 500,
|
||||
"name": "Product Name"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**NEW:**
|
||||
```json
|
||||
{
|
||||
"amount": 1000,
|
||||
"currency": "RUB",
|
||||
"items": [
|
||||
{
|
||||
"itemID": 123,
|
||||
"price": 500,
|
||||
"name": "Product Name",
|
||||
"quantity": 2 // <-- ADD THIS FIELD
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Email Purchase Confirmation
|
||||
|
||||
**POST /purchase-email**
|
||||
|
||||
Update items to include quantity:
|
||||
|
||||
**NEW:**
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"telegramUserId": "123456",
|
||||
"items": [
|
||||
{
|
||||
"itemID": 123,
|
||||
"name": "Product Name",
|
||||
"price": 500,
|
||||
"currency": "RUB",
|
||||
"quantity": 2 // <-- ADD THIS FIELD
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Future: Filters & Sorting (To Be Discussed)
|
||||
|
||||
### GET /category/{categoryID}
|
||||
|
||||
Add query parameters for filtering and sorting:
|
||||
|
||||
**Proposed Query Parameters:**
|
||||
- `sort`: Sort order (e.g., `price_asc`, `price_desc`, `rating_desc`, `name_asc`)
|
||||
- `minPrice`: Minimum price filter
|
||||
- `maxPrice`: Maximum price filter
|
||||
- `minRating`: Minimum rating filter (1-5)
|
||||
- `count`: Number of items per page (already exists)
|
||||
- `skip`: Offset for pagination (already exists)
|
||||
|
||||
**Example:**
|
||||
```
|
||||
GET /category/5?sort=price_asc&minPrice=100&maxPrice=500&minRating=4&count=20&skip=0
|
||||
```
|
||||
|
||||
**Response:** Same as current (array of items)
|
||||
Base URLs:
|
||||
- Dexar: `https://api.dexarmarket.ru:445`
|
||||
- Novo: `https://api.novo.market:444`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
## 1. Region / Location Endpoints
|
||||
|
||||
**Required NOW:**
|
||||
1. Add `quantity` field to cart item responses
|
||||
2. Support `quantity` parameter in POST /cart
|
||||
3. Create new PATCH /cart endpoint for updating quantities
|
||||
4. Include `quantity` in payment and email endpoints
|
||||
### 1.1 `GET /regions` — List available regions
|
||||
|
||||
**Future (After Discussion):**
|
||||
- Sorting and filtering query parameters for category items endpoint
|
||||
Returns the list of regions where the marketplace operates.
|
||||
|
||||
**Response** `200 OK`
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "moscow",
|
||||
"city": "Москва",
|
||||
"country": "Россия",
|
||||
"countryCode": "RU",
|
||||
"timezone": "Europe/Moscow"
|
||||
},
|
||||
{
|
||||
"id": "spb",
|
||||
"city": "Санкт-Петербург",
|
||||
"country": "Россия",
|
||||
"countryCode": "RU",
|
||||
"timezone": "Europe/Moscow"
|
||||
},
|
||||
{
|
||||
"id": "yerevan",
|
||||
"city": "Ереван",
|
||||
"country": "Армения",
|
||||
"countryCode": "AM",
|
||||
"timezone": "Asia/Yerevan"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Region object:**
|
||||
| Field | Type | Required | Description |
|
||||
|---------------|--------|----------|------------------------------|
|
||||
| `id` | string | yes | Unique region identifier |
|
||||
| `city` | string | yes | City name (display) |
|
||||
| `country` | string | yes | Country name (display) |
|
||||
| `countryCode` | string | yes | ISO 3166-1 alpha-2 code |
|
||||
| `timezone` | string | no | IANA timezone string |
|
||||
|
||||
> If this endpoint is unavailable, the frontend falls back to 6 hardcoded regions (Moscow, SPB, Yerevan, Minsk, Almaty, Tbilisi).
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Region Query Parameter on Existing Endpoints
|
||||
|
||||
The following **existing** endpoints now accept an optional `?region=` query parameter:
|
||||
|
||||
| Endpoint | Example |
|
||||
|---------------------------------|----------------------------------------------|
|
||||
| `GET /category` | `GET /category?region=moscow` |
|
||||
| `GET /category/:id` | `GET /category/5?count=50&skip=0®ion=spb` |
|
||||
| `GET /item/:id` | `GET /item/123?region=yerevan` |
|
||||
| `GET /searchitems` | `GET /searchitems?search=phone®ion=moscow` |
|
||||
| `GET /randomitems` | `GET /randomitems?count=5®ion=almaty` |
|
||||
|
||||
**Behavior:**
|
||||
- If `region` param is **present** → return only items/categories available in that region
|
||||
- If `region` param is **absent** → return all items globally (current behavior, no change)
|
||||
- The `region` value matches the `id` field from the `/regions` response
|
||||
|
||||
---
|
||||
|
||||
## 2. Auth / Login Endpoints
|
||||
|
||||
Authentication is **Telegram-based** with **cookie sessions** (HttpOnly, Secure, SameSite=None).
|
||||
|
||||
All auth endpoints must support CORS with `credentials: true`.
|
||||
|
||||
### 2.1 `GET /auth/session` — Check current session
|
||||
|
||||
Called on every page load to check if the user has an active session.
|
||||
|
||||
**Request:**
|
||||
- Cookies: session cookie (set by backend)
|
||||
- CORS: `withCredentials: true`
|
||||
|
||||
**Response `200 OK`** (authenticated):
|
||||
```json
|
||||
{
|
||||
"sessionId": "sess_abc123",
|
||||
"telegramUserId": 123456789,
|
||||
"username": "john_doe",
|
||||
"displayName": "John Doe",
|
||||
"active": true,
|
||||
"expiresAt": "2026-03-01T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Response `200 OK`** (expired session):
|
||||
```json
|
||||
{
|
||||
"sessionId": "sess_abc123",
|
||||
"telegramUserId": 123456789,
|
||||
"username": "john_doe",
|
||||
"displayName": "John Doe",
|
||||
"active": false,
|
||||
"expiresAt": "2026-02-27T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Response `401 Unauthorized`** (no session / invalid cookie):
|
||||
```json
|
||||
{
|
||||
"error": "No active session"
|
||||
}
|
||||
```
|
||||
|
||||
**AuthSession object:**
|
||||
| Field | Type | Required | Description |
|
||||
|------------------|---------|----------|------------------------------------------|
|
||||
| `sessionId` | string | yes | Unique session ID |
|
||||
| `telegramUserId` | number | yes | Telegram user ID |
|
||||
| `username` | string? | no | Telegram @username (can be null) |
|
||||
| `displayName` | string | yes | User display name (first_name + last_name) |
|
||||
| `active` | boolean | yes | Whether session is currently valid |
|
||||
| `expiresAt` | string | yes | ISO 8601 expiration datetime |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 `GET /auth/telegram/callback` — Telegram bot auth callback
|
||||
|
||||
This is the URL the Telegram bot redirects to after the user starts the bot.
|
||||
|
||||
**Flow:**
|
||||
1. Frontend generates link: `https://t.me/{botUsername}?start=auth_{encodedCallbackUrl}`
|
||||
2. User clicks → opens Telegram → starts the bot
|
||||
3. Bot sends user data to this callback endpoint
|
||||
4. Backend creates session, sets `Set-Cookie` header
|
||||
5. Frontend polls `GET /auth/session` every 3 seconds to detect when session becomes active
|
||||
|
||||
**Request** (from Telegram bot / webhook):
|
||||
```json
|
||||
{
|
||||
"id": 123456789,
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"username": "john_doe",
|
||||
"photo_url": "https://t.me/i/userpic/...",
|
||||
"auth_date": 1709100000,
|
||||
"hash": "abc123def456..."
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** Should set a session cookie and return:
|
||||
```json
|
||||
{
|
||||
"sessionId": "sess_abc123",
|
||||
"message": "Authenticated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Cookie requirements:**
|
||||
| Attribute | Value | Notes |
|
||||
|------------|----------------|------------------------------------------|
|
||||
| `HttpOnly` | `true` | Not accessible via JS |
|
||||
| `Secure` | `true` | HTTPS only |
|
||||
| `SameSite` | `None` | Required for cross-origin (API ≠ frontend) |
|
||||
| `Path` | `/` | |
|
||||
| `Max-Age` | `86400` (24h) | Or as needed |
|
||||
| `Domain` | API domain | |
|
||||
|
||||
> **Important:** Since the API domain differs from the frontend domain, `SameSite=None` + `Secure=true` is required for the cookie to be sent cross-origin.
|
||||
|
||||
---
|
||||
|
||||
### 2.3 `POST /auth/logout` — End session
|
||||
|
||||
**Request:**
|
||||
- Cookies: session cookie
|
||||
- CORS: `withCredentials: true`
|
||||
- Body: `{}` (empty)
|
||||
|
||||
**Response `200 OK`:**
|
||||
```json
|
||||
{
|
||||
"message": "Logged out"
|
||||
}
|
||||
```
|
||||
|
||||
Should clear/invalidate the session cookie.
|
||||
|
||||
---
|
||||
|
||||
## 3. CORS Configuration
|
||||
|
||||
For auth cookies to work cross-origin, the backend CORS config must include:
|
||||
|
||||
```
|
||||
Access-Control-Allow-Origin: https://dexarmarket.ru (NOT *)
|
||||
Access-Control-Allow-Credentials: true
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization
|
||||
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS
|
||||
```
|
||||
|
||||
> `Access-Control-Allow-Origin` **cannot** be `*` when `Allow-Credentials: true`. Must be the exact frontend origin.
|
||||
|
||||
For Novo, also allow `https://novo.market`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Session Refresh Behavior
|
||||
|
||||
The frontend automatically re-checks the session **60 seconds before `expiresAt`**. If the backend supports session extension (sliding expiration), it can re-set the cookie with a fresh `Max-Age` on every `GET /auth/session` call.
|
||||
|
||||
---
|
||||
|
||||
## 5. Auth Gate — Checkout Flow
|
||||
|
||||
The checkout button (`POST /cart` payment) now requires authentication:
|
||||
- If the user is **not logged in** → frontend shows a Telegram login dialog instead of proceeding
|
||||
- If the user **is logged in** → checkout proceeds normally
|
||||
- The session cookie is sent automatically with the payment request
|
||||
|
||||
No backend changes needed for the payment endpoint itself — just ensure it reads the session cookie if needed for order association.
|
||||
|
||||
---
|
||||
|
||||
## Summary of New Endpoints
|
||||
|
||||
| Method | Path | Purpose | Auth Required |
|
||||
|--------|----------------------------|-----------------------------|---------------|
|
||||
| `GET` | `/regions` | List available regions | No |
|
||||
| `GET` | `/auth/session` | Check current session | Cookie |
|
||||
| `GET` | `/auth/telegram/callback` | Telegram bot auth callback | No (from bot) |
|
||||
| `POST` | `/auth/logout` | End session | Cookie |
|
||||
|
||||
## Summary of Modified Endpoints
|
||||
|
||||
| Method | Path | Change |
|
||||
|--------|-------------------|---------------------------------------|
|
||||
| `GET` | `/category` | Added optional `?region=` param |
|
||||
| `GET` | `/category/:id` | Added optional `?region=` param |
|
||||
| `GET` | `/item/:id` | Added optional `?region=` param |
|
||||
| `GET` | `/searchitems` | Added optional `?region=` param |
|
||||
| `GET` | `/randomitems` | Added optional `?region=` param |
|
||||
|
||||
---
|
||||
|
||||
## Telegram Bot Setup
|
||||
|
||||
Each brand needs its own bot:
|
||||
- **Dexar:** `@dexarmarket_bot`
|
||||
- **Novo:** `@novomarket_bot`
|
||||
|
||||
The bot should:
|
||||
1. Listen for `/start auth_{callbackUrl}` command
|
||||
2. Extract the callback URL
|
||||
3. Send the user's Telegram data (id, first_name, username, etc.) to that callback URL
|
||||
4. The callback URL is `{apiUrl}/auth/telegram/callback`
|
||||
|
||||
Reference in New Issue
Block a user