# FastCheck Backend Implementation Guide
## QR Code Authentication Flow
### Overview
The frontend displays a QR code that contains a session ID. When a user scans this QR code with the mobile app, the mobile app authenticates and links to that session. The frontend polls the backend every 2 seconds to check if the session has been authenticated.
### Step-by-Step Implementation
---
## 1. Create WebSession (QR Code Generation)
### Frontend Request:
```typescript
GET https://api.fastcheck.store/websession
Headers: {
"Content-Type": "application/json"
}
```
### Backend Response:
```json
{
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "",
"expires": "2026-01-19T10:50:00Z",
"userSessionId": "",
"Status": false
}
```
### Backend Implementation:
```javascript
// Example Node.js/Express
app.get('/websession', (req, res) => {
// Generate unique session ID (UUID or similar)
const sessionId = generateUUID(); // e.g., "1AF3781BF6B94604B771AEA1D44FA63A"
// Set expiration time (e.g., 5 minutes from now)
const expires = new Date(Date.now() + 5 * 60 * 1000).toISOString();
// Store session in database or cache (Redis recommended)
await sessionStore.create({
sessionId: sessionId,
userId: null,
userSessionId: null,
status: false,
expiresAt: expires,
createdAt: new Date()
});
// Return session data
res.json({
sessionId: sessionId,
userId: "",
expires: expires,
userSessionId: "",
Status: false
});
});
```
### What Frontend Does:
```typescript
// Frontend generates QR code data from session ID
const qrData = `fastcheck://login?session=${sessionId}`;
// Example: "fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A"
```
**QR Code Contains:** Deep link URL with session ID
- Format: `fastcheck://login?session={sessionId}`
- Mobile app will parse this URL and extract the sessionId
- Mobile app will then authenticate and update this session
---
## 2. Check WebSession Status (Polling)
### Frontend Request (Every 2 seconds):
```typescript
GET https://api.fastcheck.store/websession/1AF3781BF6B94604B771AEA1D44FA63A
Headers: {
"Content-Type": "application/json"
}
```
### Backend Response (Not Authenticated Yet):
```json
{
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "",
"expires": "2026-01-19T10:50:00Z",
"userSessionId": "",
"Status": false
}
```
### Backend Response (Authenticated):
```json
{
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo",
"expires": "2026-01-19T12:00:00Z",
"userSessionId": "8A94EFEFD003426A9B456C48CAC99BE6",
"Status": true
}
```
### Backend Implementation:
```javascript
app.get('/websession/:sessionId', async (req, res) => {
const { sessionId } = req.params;
// Retrieve session from database/cache
const session = await sessionStore.get(sessionId);
if (!session) {
return res.status(404).json({ message: "Session not found" });
}
// Check if session expired
if (new Date() > new Date(session.expiresAt)) {
await sessionStore.delete(sessionId);
return res.status(404).json({ message: "Session expired" });
}
// Return session status
res.json({
sessionId: session.sessionId,
userId: session.userId || "",
expires: session.expiresAt,
userSessionId: session.userSessionId || "",
Status: session.status || false
});
});
```
---
## 3. Mobile App Authenticates Session
**This is what the MOBILE APP does** (not the web frontend):
### Mobile App Flow:
1. User scans QR code: `fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A`
2. Mobile app extracts sessionId: `1AF3781BF6B94604B771AEA1D44FA63A`
3. Mobile app authenticates user (PIN, biometrics, etc.)
4. Mobile app sends authentication request to backend:
```typescript
POST https://api.fastcheck.store/websession/authenticate
Headers: {
"Authorization": "Bearer {mobile_app_token}",
"Content-Type": "application/json"
}
Body: {
"sessionId": "1AF3781BF6B94604B771AEA1D44FA63A",
"userId": "kHaAe9roaC2uq63AKGE/8+Ti/t/iFro68QhEZ1dRGLo"
}
```
### Backend Implementation:
```javascript
app.post('/websession/authenticate', authenticateMobileApp, async (req, res) => {
const { sessionId, userId } = req.body;
const mobileUserId = req.user.id; // From mobile app authentication
// Verify the mobile user matches
if (userId !== mobileUserId) {
return res.status(403).json({ message: "Unauthorized" });
}
// Update session with user information
const userSessionId = generateUUID();
await sessionStore.update(sessionId, {
userId: userId,
userSessionId: userSessionId,
status: true,
authenticatedAt: new Date()
});
res.json({ message: "Session authenticated" });
});
```
---
## 4. Logout (Delete Session)
### Frontend Request:
```typescript
DELETE https://api.fastcheck.store/websession/1AF3781BF6B94604B771AEA1D44FA63A
Headers: {
"Authorization": "{\"sessionID\": \"1AF3781BF6B94604B771AEA1D44FA63A\"}",
"Content-Type": "application/json"
}
```
### Backend Implementation:
```javascript
app.delete('/websession/:sessionId', async (req, res) => {
const { sessionId } = req.params;
// Delete session from database/cache
await sessionStore.delete(sessionId);
res.json({ message: "Session deleted" });
});
```
---
## 5. Authenticated API Requests
After login, all API requests include the sessionId in the Authorization header:
### Frontend Request:
```typescript
POST https://api.fastcheck.store/fastcheck
Headers: {
"Authorization": "{\"sessionID\": \"1AF3781BF6B94604B771AEA1D44FA63A\"}",
"Content-Type": "application/json"
}
Body: {
"amount": 150000,
"currency": "RUB"
}
```
### Backend Authentication Middleware:
```javascript
// Middleware to verify session
const authenticateSession = async (req, res, next) => {
try {
// Parse Authorization header
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: "not authorized" });
}
// Parse JSON from Authorization header
const { sessionID } = JSON.parse(authHeader);
// Verify session exists and is authenticated
const session = await sessionStore.get(sessionID);
if (!session || !session.status) {
return res.status(401).json({ message: "not authorized" });
}
// Check if session expired
if (new Date() > new Date(session.expiresAt)) {
await sessionStore.delete(sessionID);
return res.status(401).json({ message: "not authorized" });
}
// Attach user info to request
req.user = {
userId: session.userId,
userSessionId: session.userSessionId,
sessionId: sessionID
};
next();
} catch (error) {
return res.status(401).json({ message: "not authorized" });
}
};
// Use middleware on protected routes
app.post('/fastcheck', authenticateSession, async (req, res) => {
const { amount, currency } = req.body;
const userId = req.user.userId;
// Create FastCheck logic...
});
```
---
## QR Code Data Format
### What the QR Code Contains:
```
fastcheck://login?session=1AF3781BF6B94604B771AEA1D44FA63A
```
**Format breakdown:**
- **Scheme**: `fastcheck://` - Deep link scheme for mobile app
- **Path**: `login` - Indicates this is a login QR code
- **Parameter**: `session={sessionId}` - The web session ID
### Frontend QR Code Implementation:
```typescript
// In login.component.ts
const sessionResponse = await createWebSession();
const qrData = `fastcheck://login?session=${sessionResponse.sessionId}`;
// QR code component displays this as a QR image
```
---
## Database Schema Recommendations
### WebSession Table:
```sql
CREATE TABLE web_sessions (
session_id VARCHAR(64) PRIMARY KEY,
user_id VARCHAR(255),
user_session_id VARCHAR(64),
status BOOLEAN DEFAULT FALSE,
expires_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
authenticated_at TIMESTAMP,
INDEX idx_expires (expires_at),
INDEX idx_status (status)
);
-- Auto-delete expired sessions
CREATE EVENT cleanup_expired_sessions
ON SCHEDULE EVERY 1 HOUR
DO
DELETE FROM web_sessions WHERE expires_at < NOW();
```
### Or use Redis (Recommended for sessions):
```javascript
// Redis structure
const sessionKey = `websession:${sessionId}`;
await redis.setex(sessionKey, 300, JSON.stringify({
sessionId: sessionId,
userId: userId,
userSessionId: userSessionId,
status: true
}));
```
---
## Security Considerations
1. **Session Expiration**: Sessions should expire after 5 minutes if not authenticated
2. **HTTPS Only**: All communication must be over HTTPS
3. **CORS Configuration**: Configure CORS to allow frontend domain
4. **Session Cleanup**: Regularly clean up expired sessions
5. **Rate Limiting**: Limit polling requests to prevent abuse
6. **Mobile App Authentication**: Mobile app must authenticate before linking session
---
## Testing the Flow
### 1. Test Session Creation:
```bash
curl -X GET https://api.fastcheck.store/websession
```
Expected: New session with Status: false
### 2. Test Polling:
```bash
curl -X GET https://api.fastcheck.store/websession/{sessionId}
```
Expected: Same session, Status: false (until mobile app authenticates)
### 3. Test Mobile Authentication (simulate):
```bash
curl -X POST https://api.fastcheck.store/websession/authenticate \
-H "Authorization: Bearer {mobile_token}" \
-H "Content-Type: application/json" \
-d '{"sessionId": "{sessionId}", "userId": "{userId}"}'
```
Expected: Session updated with Status: true
### 4. Test Polling After Auth:
```bash
curl -X GET https://api.fastcheck.store/websession/{sessionId}
```
Expected: Session with Status: true, userId populated
---
## Frontend Polling Implementation (Already Done)
```typescript
// In auth.service.ts
startPolling(sessionId: string): Observable {
return interval(2000).pipe( // Poll every 2 seconds
switchMap(() => this.checkWebSessionStatus(sessionId)),
tap(session => {
if (session.Status) {
this.setAuthenticated(session);
}
}),
takeWhile(session => !session.Status, true) // Stop when authenticated
);
}
```
---
## Summary for Backend Team
**Required Endpoints:**
1. ✅ `GET /websession` - Create session for QR
2. ✅ `GET /websession/:id` - Check session status (polled)
3. ⚠️ `POST /websession/authenticate` - Mobile app authenticates session (NEW)
4. ✅ `DELETE /websession/:id` - Logout
**Required Logic:**
- Generate unique session IDs
- Store sessions with expiration
- Mobile app updates session status
- Web frontend polls until Status = true
- All authenticated APIs verify session in Authorization header
**QR Code Data:**
- Format: `fastcheck://login?session={sessionId}`
- Mobile app parses and authenticates
- Web polls until mobile authenticates