# 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