Complete doc
Table📋 of Contents
OverviewArchitectureEnvironment SetupProject StructureDatabase SchemaAuthentication SystemAPI EndpointsMiddlewareUtilitiesSocket.io EventsScheduled JobsError HandlingDevelopment GuideDeployment
Overview
UserStateOnMart APIPOS Backend is a robustfull-featured Node.jsRESTful backend framework designedAPI for managing userretail authentication,operations. user profiles, payments, and referral systems. Built with Express.js, itIt provides a comprehensive set of features for modernproduct webmanagement, applications.transaction processing, inventory tracking, cashier shift management, and customer relationship management.
✨ Key CapabilitiesFeatures
Multi-strategy🔐authenticationJWT(Local,AuthenticationJWT,&Basic)Authorization - Secure role-based access control- 👥 Multi-Role User
registrationManagement - MasterAdmin, SuperAdmin, Admin, Cashier - 🏪 Multi-Outlet Support - Manage multiple store locations
- 📦 Product Management - Categories, brands, barcodes, and inventory
- 💰 Transaction Processing - Complete POS functionality with
OTP/Email verificationvoid/refund Payment🕐processingShiftviaManagementXendit- Track cashier shifts and salesReal-time🎁paymentPromotionalstatusSystemupdates- ReferralFlexibleand point systemAddressdiscount managementMarketing👤offersCustomer Management - Customer profiles andvouchertransactionredemptionhistoryPush📊notificationsDashboardvia&FirebaseReportingCloud-MessagingSales analytics and insights- 📄 Invoice Generation - PDF receipt printing
- 📥 Import/Export - CSV and Excel support
🚀 ArchitectureQuick Start
Technology StackPrerequisites
Application Flow
Request → Express Middleware → Passport Auth → Route Handler → Controller → Model → Database
↓
Response/Error Handler
Environment Setup
Environment Variables
Create a .env file in the root directory with the following variables:
# Server Configuration
API_PORT=33010
ORIGIN_CORS=http://localhost:3000,http://example.com
# PostgreSQL Configuration
PS_HOST=localhost
PS_USER=postgres
PS_PASSWORD=your_password
PS_DB=userstate
PS_DIALECT=postgres
PS_PORT=5432
PS_POOL_MAX=50
# MongoDB Configuration
MONGO_DB=mongodb://localhost:27017/userstate
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password
# Authentication
SECRET_AUTH_CONFIG=your_jwt_secret_key
SANDBOX_AUTH_CONFIG=your_sandbox_key
# Xendit Configuration
XENDIT_SECRET_KEY=your_xendit_secret_key
XENDIT_PUBLIC_KEY=your_xendit_public_key
XENDIT_CALLBACK_TOKEN=your_callback_token
# External APIs
API_URL_SUNSHINE=https://sunshine-api-url
API_URL_SMSVIRO=https://smsviro-api-url
SMSVIROTOKEN=your_sms_token
# Email Configuration (Nodemailer)
EMAIL_HOST=smtp.hostinger.com
EMAIL_PORT=465
EMAIL_USER=noreply@yourdomain.com
EMAIL_PASS=your_email_password
# Firebase Configuration
# Place your Firebase service account JSON in keystore/onmapss-main-key.json
Installation Steps
-
Clone the repository
git clone <repository-url> cd onmart-pos-backend -
Install
Node.js Dependenciesdependenciesnpm install -
Setup PostgreSQL Databasecreatedb userstate Setup MongoDB(if using payment logs)# Ensure MongoDB is running mongod --dbpath /path/to/dataSetup Redis# Ensure Redis is running redis-serverConfigure
environmentFirebaseDownload your Firebase service account key JSONPlace it inkeystore/onmapss-main-key.json
Run Database MigrationsThe application uses auto-sync for Sequelize modelsUncomment the sync lines inindex.jsif needed:
await db.user_login.sync({ alter: true }); await db.user_info.sync({ alter: true }); // ... etc
Project Structure
Directory Overview
userstate-api/
│
├── config/ # Configuration files
│ ├── api.js # External API configurations
│ ├── auth.config.js # JWT and auth secrets
│ ├── db.js # Database connection settings
│ ├── multer.js # File upload configuration
│ ├── pg.js # PostgreSQL initialization
│ ├── redis.js # Redis client setup
│ └── voucher-ext.json # Voucher extensions data
│
├── controllers/ # Business logic handlers
│ ├── address.controller.js # Address CRUD operations
│ ├── auth.controller.js # Authentication logic
│ ├── faq.controller.js # FAQ management
│ ├── fcm.controller.js # Firebase Cloud Messaging
│ ├── marketing_offer.controller.js # Marketing offers
│ ├── passport.controller.js # Passport strategy handlers
│ ├── payment.controller.js # Payment processing
│ ├── payout.controller.js # Payout operations
│ ├── point_mutation.controller.js # Points system
│ ├── referral.controller.js # Referral system
│ └── user_info.controller.js # User profile management
│
├── middleware/ # Custom middleware
│ ├── auth.js # Authentication middleware
│ ├── authJwt.js # JWT verification
│ ├── error_param_handler.js # Error handling
│ ├── express_validator.js # Express validator wrapper
│ ├── json_validator.js # AJV JSON schema validator
│ ├── other_validator.js # Custom validators
│ ├── passport.js # Passport strategies
│ ├── upload_update_profile.js # File upload middleware
│ └── verify.js # Token verification
│
├── models/ # Database models
│ ├── index.js # Model loader and relationships
│ ├── user_login.model.js # User credentials
│ ├── user_info.model.js # User profile information
│ ├── user_balance.model.js # User wallet balance
│ ├── address_list.model.js # User addresses
│ ├── point_mutation.model.js # Point transactions
│ ├── referral_history.model.js # Referral tracking
│ ├── marketing_offer.model.js # Offers
│ ├── fcm.model.js # FCM tokens
│ ├── api_key.model.js # API keys for services
│ ├── otp_tracker.model.js # OTP tracking
│ ├── bank_account.model.js # Bank account info
│ ├── bank_channel.model.js # Payment channels
│ ├── payment_type.model.js # Payment methods
│ ├── user_transaction.model.js # Transaction history
│ ├── voucher_redemption.model.js # Voucher usage
│ ├── deactivate_reason.model.js # Account deactivation
│ ├── xendit_payout_logs.model.js # Payout logs (MongoDB)
│ └── xendit_checkout_logs.model.js # Checkout logs (MongoDB)
│
├── routers/ # Route definitions
│ ├── address.route.js # Address endpoints
│ ├── auth.route.js # Authentication endpoints
│ ├── faq.route.js # FAQ endpoints
│ ├── fcm.route.js # FCM endpoints
│ ├── marketing_offer.route.js # Marketing endpoints
│ ├── payment.route.js # Payment endpoints
│ ├── point_mutation.route.js # Points endpoints
│ ├── referral.route.js # Referral endpoints
│ └── user_info.route.js # User profile endpoints
│
├── schemas/ # Request validation schemas
│ ├── index.js # Schema exporter
│ ├── address.schema.js # Address validation
│ ├── auth.schema.js # Auth validation
│ ├── fcm.schema.js # FCM validation
│ ├── marketing_offer.schema.js # Marketing validation
│ ├── payment.schema.js # Payment validation
│ ├── point_mutation.schema.js # Points validation
│ ├── referral.schema.js # Referral validation
│ └── user_info.schema.js # User profile validation
│
├── socket/ # Socket.io implementations
│ └── payment_status.socket.js # Real-time payment updates
│
├── scheduler/ # Cron job definitions
│ └── xendit.scheduler.js # Payment status sync
│
├── utilities/ # Utility functions
│ ├── encrypt.util.js # Encryption helpers
│ ├── general.util.js # General utilities
│ ├── hashing_center.util.js # Hashing functions
│ └── qrcode.util.js # QR code generation
│
├── libraries/ # Static data files
│ ├── dir_day.json # Day mappings
│ ├── dir_digits.json # Digit mappings
│ ├── dir_month.json # Month mappings
│ └── dir_year.json # Year mappings
│
├── keystore/ # Private keys and certificates
│ └── onmapss-main-key.json # Firebase service account key
│
├── resources/ # Generated files (gitignored)
│ └── user_profile_img/ # User profile images
│
├── index.js # Application entry point
├── utility.js # Shared utility functions
├── package.json # Dependencies and scripts
└── .gitignore # Git ignore rules
Database Schema
PostgreSQL Tables (via Sequelize)
1. user_login
Stores user authentication credentials.
2. user_info
Stores user profile information.
3. user_balance
Tracks user wallet balance.
4. address_list
Stores user addresses.
5. point_mutation
Tracks point transactions.
6. referral_history
Tracks referral relationships.
7. marketing_offer
Stores promotional offers.
8. fcm
Stores Firebase Cloud Messaging tokens.
9. user_transaction
Tracks payment transactions.
10. voucher_redemption
Tracks voucher usage.
11. api_key
Stores API keys for service authentication.
12. otp_tracker
Tracks OTP requests for rate limiting.
MongoDB Collections (via Mongoose)
xendit_payout_logs
Stores Xendit payout transaction logs.
{
payout_id: String,
username: String,
amount: Number,
status: String,
bank_code: String,
account_number: String,
account_holder_name: String,
description: String,
reference_id: String,
created_at: Date,
updated_at: Date
}
xendit_checkout_logs
Stores Xendit checkout/invoice logs.
{
invoice_id: String,
external_id: String,
username: String,
amount: Number,
status: String,
payment_method: String,
payment_channel: String,
description: String,
invoice_url: String,
expiry_date: Date,
created_at: Date,
updated_at: Date
}
Model Relationships
user_login
├── hasOne → user_info
├── hasOne → user_balance
├── hasMany → address_list
├── hasMany → marketing_offer
├── hasMany → fcm
├── hasMany → user_transaction
└── hasMany → voucher_redemption
user_info
├── belongsTo → user_login
├── belongsTo → user_balance
└── hasMany → referral_history (as referrer)
referral_history
├── belongsTo → user_info (username)
└── belongsTo → user_info (submitted_by as referrer)
Authentication System
The application uses Passport.js with three authentication strategies:
1. Local Strategy (Username/Password)
Used for: User login with username/email and password
Endpoint: POST /auth
Flow:
User submits credentialsSystem checks database for username or emailIf user not confirmed, checks Redis for temporary dataPassword is verified using bcryptJWT token generated on success
Middleware: passport.PassportLocal
2. JWT Strategy (Bearer Token)
Used for: Protected route access
Endpoints: Most authenticated routes
Flow:
Client sends JWT in Authorization header:Bearer <token>Token is decoded and validatedUser's password hash is used as secret key (dynamic per user)User information extracted from token payload
Middleware: passport.PassportJwt
Token Structure:
{
user: "username",
agent: true/false,
iat: 1234567890,
exp: 1234567890
}
3. Basic Strategy (API Key)
Used for: Service-to-service authentication
Endpoints: Internal/system endpoints (/internal/*, /sys/*)
Flow:
Client sends Basic Auth header:Basic base64(holder:apiKey)System looks up API key in databaseAPI key verified using bcrypt
Middleware: passport.PassportBasic
Token Management
JWT Secret Key: Dynamically generated from user's password hash
Formula:pwd.slice(32, 48) + pwd.slice(14, 30)Ensures tokens invalidate on password change
Token Expiration: Configurable via environment
Session tokens can be extended via/extendendpoint
Redis Storage: Used for temporary user data during registration
API Endpoints
Authentication Endpoints
POST /auth
Description: User login
Auth: Local Strategy
Body:
{
"username": "john_doe",
"password": "securePassword123"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"agent": false
}
POST /signup
Description: User registration with email/OTP verification
Auth: None
Body:
{
"username": "john_doe",
"password": "securePassword123",
"email": "john@example.com",
"full_name": "John Doe",
"phone_number": "628123456789",
"referral_code": "REF123",
"mode": "LINK"
}
Modes:
LINK: Email verification linkOTP: SMS OTP verification
POST /check/email
Description: Check if email is already registered
Auth: None
Body:
{
"email": "john@example.com"
}
POST /verify-sign-up
Description: Verify OTP or email verification
Auth: JWT Token
Body:
{
"otp": "123456"
}
POST /request-reset-pwd
Description: Request password reset
Auth: None
Body:
{
"email": "john@example.com"
}
POST /reset-pwd
Description: Reset password with token
Auth: Token from email
Body:
{
"password": "newSecurePassword123"
}
POST /update-pwd
Description: Update password (logged in)
Auth: JWT
Body:
{
"old_password": "oldPassword123",
"new_password": "newPassword123"
}
GET /extend
Description: Extend JWT token expiration
Auth: JWT
Response:
{
"token": "new_jwt_token..."
}
GET /verify
Description: Verify if JWT token is valid
Auth: JWT
Response: 200 OK or 401 Unauthorized
GET /logoff
Description: Logout user
Auth: JWT
Response: 200 OK
POST /deactivate
Description: Deactivate user account
Auth: JWT
Body:
{
"reason": "No longer needed"
}
User Profile Endpoints
GET /user/profile
Description: Get user profile information
Auth: JWT
Response:
{
"username": "john_doe",
"full_name": "John Doe",
"email": "john@example.com",
"phone_number": "628123456789",
"profile_img": "/resources/user_profile_img/john_doe.jpg",
"referral_code": "JOHN123",
"balance": 50000,
"points": 150
}
PUT /user/profile
Description: Update user profile
Auth: JWT
Body:
{
"full_name": "John Updated",
"phone_number": "628987654321"
}
POST /user/update-profile-img
Description: Upload profile image
Auth: JWT
Content-Type: multipart/form-data
Body: Form data with profile_img file
GET /user/referral-code
Description: Get user's referral code
Auth: JWT
Response:
{
"referral_code": "JOHN123"
}
Address Endpoints
GET /address/list
Description: Get all user addresses
Auth: JWT
Response:
[
{
"id": 1,
"label": "Home",
"recipient_name": "John Doe",
"phone_number": "628123456789",
"address": "Jl. Example No. 123",
"is_default": true
}
]
POST /address
Description: Create new address
Auth: JWT
Body:
{
"label": "Office",
"recipient_name": "John Doe",
"phone_number": "628123456789",
"address": "Jl. Office No. 456",
"is_default": false
}
PUT /address/:id
Description: Update address
Auth: JWT
Body: Same as create
DELETE /address/:id
Description: Delete address
Auth: JWT
POST /address/set-default
Description: Set default address
Auth: JWT
Body:
{
"id": 1
}
Payment Endpoints
POST /payment-checkout
Description: Create payment checkout/invoice
Auth: JWT
Body:
{
"amount": 100000,
"description": "Order #12345",
"payment_method": "EWALLET",
"ewallet_type": "OVO"
}
POST /sys/payment-checkout
Description: System checkout (for internal services)
Auth: Basic
Body:
{
"username": "john_doe",
"amount": 100000,
"description": "Order #12345"
}
POST /payment-callback
Description: Xendit payment callback
Auth: Callback Token
Body: Xendit callback payload
GET /balance
Description: Get user balance
Auth: JWT
Response:
{
"balance": 50000,
"points": 150
}
POST /balance/decrease
Description: Decrease user balance
Auth: JWT
Body:
{
"amount": 10000,
"description": "Purchase payment"
}
GET /invoice/detail
Description: Get invoice details
Auth: None (query param based)
Query: ?invoice_id=inv_123
GET /invoice/detail/xendit
Description: Get Xendit invoice details
Auth: JWT
Query: ?invoice_id=inv_123
GET /invoice/list/xendit
Description: List user's Xendit invoices
Auth: JWT
POST /invoice/cancel/xendit
Description: Cancel Xendit invoice
Auth: JWT
Body:
{
"invoice_id": "inv_123"
}
POST /redeem-voucher
Description: Redeem voucher code
Auth: Basic
Body:
{
"username": "john_doe",
"voucher_code": "PROMO123"
}
GET /check-voucher-redemption
Description: Check if user has redeemed voucher
Auth: JWT
Query: ?voucher_code=PROMO123
POST /bank/list
Description: Get list of available bank codes
Auth: None
Body:
{
"type": "DISBURSEMENT"
}
GET /payment/list
Description: Get available payment types
Auth: None
Point & Referral Endpoints
GET /points/history
Description: Get point transaction history
Auth: JWT
Response:
[
{
"id": 1,
"type": "CREDIT",
"amount": 50,
"description": "Referral bonus",
"created_at": "2024-01-01T00:00:00.000Z"
}
]
POST /points/redeem
Description: Redeem points for balance
Auth: JWT
Body:
{
"points": 100
}
GET /referral/history
Description: Get referral history
Auth: JWT
Response:
[
{
"username": "referred_user",
"referral_code": "JOHN123",
"status": "completed",
"created_at": "2024-01-01T00:00:00.000Z"
}
]
GET /referral/stats
Description: Get referral statistics
Auth: JWT
Response:
{
"total_referrals": 10,
"completed_referrals": 8,
"pending_referrals": 2,
"total_earned_points": 400
}
FCM Endpoints
POST /fcm/register
Description: Register FCM device token
Auth: JWT
Body:
{
"token": "fcm_device_token...",
"device_type": "android"
}
DELETE /fcm/token
Description: Remove FCM token
Auth: JWT
Query: ?token=fcm_device_token...
POST /fcm/send
Description: Send push notification (admin)
Auth: Basic
Body:
{
"username": "john_doe",
"title": "New Offer",
"body": "Check out our latest promotion!",
"data": {
"offer_id": "123"
}
}
Marketing Offer Endpoints
GET /marketing-offer/list
Description: Get available marketing offers
Auth: JWT
Response:
[
{
"id": 1,
"title": "Summer Sale",
"description": "Get 50% off!",
"image_url": "/images/offer1.jpg",
"valid_until": "2024-12-31T23:59:59.000Z",
"is_active": true
}
]
POST /marketing-offer
Description: Create marketing offer (admin)
Auth: Basic
Body:
{
"title": "New Year Sale",
"description": "Special discount",
"image_url": "/images/offer.jpg",
"valid_until": "2024-12-31"
}
Middleware
Authentication Middleware
passport.PassportLocal
Validates username and passwordReturns JWT token on successUsed in:/auth
passport.PassportJwt
Validates Bearer tokenExtracts user from tokenDynamic secret key per userUsed in: Most protected routes
passport.PassportBasic
Validates HTTP Basic AuthChecks API key against databaseUsed in:/internal/*,/sys/*routes
authJwt.verifyToken
Custom JWT verificationUsed for specific token validation scenariosChecks token from Authorization header or query params
Validation Middleware
validator.validate({ body: schema })
Uses AJV to validate request body against JSON schemaAutomatically returns 400 with validation errorsSchemas defined inschemas/directory
contentTypeValid(type)
Ensures request Content-Type matches expected typeExample:contentTypeValid("application/json")
verifyToken(type)
Validates special tokens (REGISTER, RESET_PWD)Checks token from query params or bodyValidates against Redis storage
verifyCallbackToken
Validates Xendit callback tokensEnsures callbacks are from legitimate source
Error Handling Middleware
errorHandlerParam.JsonHandler
Catches JSON parsing errorsReturns proper error response
errorHandlerParam.otherHandler
Global error handlerFormats error responsesLogs errors for debugging
Upload Middleware
upload_update_profile
Handles profile image uploadsUses multer for multipart/form-dataValidates file type and sizeStores in/resources/user_profile_img/
Utilities
utility.js
provideKey(req, rawJwtToken, done)
Provides secret key for JWT verificationExtracts key from user's password hashEnsures tokens invalidate on password change
admin()
Initializes Firebase Admin SDKReturns admin instance for FCM
sendMail(to, subject, text, html)
Sends email via NodemailerUses configured SMTP settingsSupports HTML templates
generateRandomString(length, numberOnly)
Generates random strings or numbersUsed for OTP generation
syncInitBalance()
Initializes user balance recordsUtility function for data migration
Encryption Utilities (utilities/encrypt.util.js)
Encryption and decryption helpersUsed for sensitive data
Hashing Utilities (utilities/hashing_center.util.js)
Centralized hashing functionsbcrypt and argon2 wrappers
QR Code Utilities (utilities/qrcode.util.js)
QR code generationUsed for payment or referral codes
General Utilities (utilities/general.util.js)
Common helper functionsData formatting and transformation
Socket.io Events
Payment Status Socket
Namespace: Default namespace
File: socket/payment_status.socket.js
Server Events
payment:status:update
Description: Broadcast payment status update to specific user
Payload:
{
invoice_id: "inv_123",
status: "PAID",
amount: 100000,
paid_at: "2024-01-01T00:00:00.000Z"
}
Client Events
subscribe:payment
Description: Subscribe to payment updates for user
Payload:
{
username: "john_doe"
}
unsubscribe:payment
Description: Unsubscribe from payment updates
Payload:
{
username: "john_doe"
}
Usage Example
Client-side (JavaScript):
const socket = io('http://localhost:33010');
// Subscribe to payment updates
socket.emit('subscribe:payment', { username: 'john_doe' });
// Listen for payment status updates
socket.on('payment:status:update', (data) => {
console.log('Payment updated:', data);
// Update UI accordingly
});
// Unsubscribe when done
socket.emit('unsubscribe:payment', { username: 'john_doe' });
Scheduled Jobs
Xendit Payment Status Sync
File: scheduler/xendit.scheduler.js
Schedule: Every 2 hours (0 */2 * * *)
Function: expireStatusUpdater
Purpose:
Synchronizes payment status with XenditUpdates expired invoicesNotifies users of status changes via Socket.io
Manual Trigger: Uncomment endpoint in index.js
app.get("/scheduler", xenditScheduler.expireStatusUpdater);
Implementation Details:
Fetches pending/processing invoices from databaseQueries Xendit API for current statusUpdates local database with latest statusEmits Socket.io events for status changesLogs results to MongoDB
Error Handling
Error Response Format
All errors follow a consistent format:
{
"status": false,
"message": "Error description",
"errorCode": 400,
"errors": [] // Optional validation errors
}
HTTP Status Codes
Common Error Messages
Authentication Errors
"Invalid Credential"(422): Wrong username/password"Account does not exist"(500): User not found"Account is Disabled"(403): Account deactivated"OTP Not Confirmed"(403): Registration pending OTP"Account is Not Confirmed"(403): Registration pending email
Validation Errors
"Email is already registered"(409): Duplicate email"PhoneRequired": Phone number required for OTP mode"PhoneFormatErr": Invalid phone number format
Payment Errors
"Insufficient Balance": Not enough funds"Invoice Not Found": Invalid invoice ID"Invoice Already Paid": Cannot modify paid invoice
Error Handler Middleware
JsonHandler: Catches JSON parsing errors
app.use(errorHandlerParam.JsonHandler);
otherHandler: Global error handler
app.use(errorHandlerParam.otherHandler);
Logging
The application uses morgan with custom colored format for request logging:
Method: BlueURL: GreenStatus: YellowResponse Time: Cyan
Errors are logged to console with stack traces in development.
Development Guide
Getting Started
Clone and Installgit clone <repo-url> cd userstate-api npm installConfigure Environmentvariablescp .env.example .env # Edit .env with your configuration-
Set up database
# Create database createdb onmart_pos_dev # Run migrations npx sequelize-cli db:migrate # Seed initial data npx sequelize-cli db:seed:all -
Start
DevelopmenttheServerservernpm startUses nodemon for auto-reload on file changes.
The server will be running at http://localhost:3000 🎉
ProjectDefault ConventionsLogin Credentials
Code
After Styleseeding,
- you
UsecanES6+logfeaturesAsync/await preferred over callbacksUse destructuring for cleaner codeFollow existing file naming patterns
File Naming
Models:*.model.jsControllers:*.controller.jsRoutes:*.route.jsSchemas:*.schema.jsMiddleware: descriptive namesin withcontextthese
Database
| Role | Password | |
|---|---|---|
| MasterAdmin | masteradmin@mail.com | password |
| SuperAdmin | superadmin@mail.com | password |
| Admin | admin@mail.com | password |
| Cashier | cashier@mail.com | password |
⚠️ Important: Change these passwords in production!models/index.js
📚
Security Best Practices
Always hash passwords with bcrypt/argon2
Validate all user input
Use parameterized queries (ORM handles this)
Sanitize file uploads
Implement rate limiting for sensitive endpoints
Never log sensitive data (passwords, tokens, keys)
Adding New Features
1. Adding a New Endpoint
Documentation
StepComprehensive 1:documentation Createis Controller (controllers/feature.controller.js)
const db = require("../models");
exports.myEndpoint = async (req, res) => {
try {
// Your logic here
res.status(200).json({ success: true });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
Step 2: Create Schema (schemas/feature.schema.js)
exports.mySchema = {
type: "object",
properties: {
field1: { type: "string" },
field2: { type: "number" }
},
required: ["field1"]
};
Step 3: Create Route (routers/feature.route.js)
const controller = require("../controllers/feature.controller");
const passport = require("../controllers/passport.controller");
const { validator, feature } = require("../schemas");
module.exports = (app) => {
app.post(
"/feature/endpoint",
[
passport.PassportJwt,
validator.validate({ body: feature.mySchema })
],
controller.myEndpoint
);
};
Step 4: Load Routeavailable in index.js
require("./routers/feature.route")(app);
2. Adding a New Model
Step 1: Create Model File (models/my_table.model.js)
module.exports = (sequelize, DataTypes) => {
const MyTable = sequelize.define("my_table", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
field1: {
type: DataTypes.STRING,
allowNull: false
},
field2: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
timestamps: true,
underscored: true
});
return MyTable;
};
Step 2: Load Model inthe models/index.js/docs
db.my_table = require("./my_table.model")(
sequalize_config,
sequelize.DataTypes
);
Step 3: Define Relationships (if any)
db.user_login.hasMany(db.my_table, {
foreignKey: "username",
sourceKey: "username"
});
Testing
Manual Testing
Use tools like:folder:
-
PostmanBackend Documentation:For- Complete APItestingreference, authentication, and development guide -
curlDatabase Documentation:For-command-lineDatabasetestingschema, - relationships,
Socket.ioandClient:queryFor WebSocket testingexamples
Example curl Commands
Login:
curl -X POST http://localhost:33010/auth \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"password123"}'
Get Profile (with JWT):
curl -X GET http://localhost:33010/user/profile \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Basic Auth (Internal endpoints):
curl -X POST http://localhost:33010/sys/payment-checkout \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'holder:apikey' | base64)" \
-d '{"username":"test","amount":10000}'
Debugging
Enable Sequelize Query Logging
In config/db.js:
const sequalize_config = new sequelize(
userstate.DB,
userstate.USER,
userstate.PASSWORD,
{
host: userstate.HOST,
dialect: userstate.DIALECT,
logging: console.log, // Add this line
// ...
}
);
Database Connection Issues
Check:
PostgreSQL is running:pg_isreadyCredentials in.envare correctDatabase exists:psql -lFirewall allows connection
Redis Connection Issues
Check:
Redis is running:redis-cli pingRedis configuration inconfig/redis.jsPassword authentication if enabled
Common Issues
"Cannot find module": Run npm install
"Port already in use": Change API_PORT in .env or kill process:
lsof -ti:33010 | xargs kill -9
"Connection refused": Database or Redis not running
Deployment
Production Checklist
SetNODE_ENV=productionin environmentUse strong, unique values forSECRET_AUTH_CONFIGConfigure production database credentialsSet up proper CORS originsEnable HTTPS/SSLConfigure firewall rulesSet up database backupsConfigure monitoring and loggingSet up PM2 or similar process managerRemove or protect debug endpoints
Using PM2
Install PM2:
npm install -g pm2
Start Application:
pm2 start index.js --name userstate-api
PM2 Configuration (ecosystem.config.js):
module.exports = {
apps: [{
name: 'userstate-api',
script: './index.js',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z'
}]
};
Start with config:
pm2 start ecosystem.config.js
Useful PM2 Commands:
pm2 list # List all processes
pm2 logs userstate-api # View logs
pm2 restart userstate-api # Restart app
pm2 stop userstate-api # Stop app
pm2 delete userstate-api # Remove from PM2
pm2 monit # Monitor resources
Nginx Reverse Proxy
Example Nginx Configuration:
server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://localhost:33010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# WebSocket support for Socket.io
location /socket.io/ {
proxy_pass http://localhost:33010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Docker Deployment
Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 33010
CMD ["node", "index.js"]
docker-compose.yml:
version: '3.8'
services:
api:
build: .
ports:
- "33010:33010"
environment:
- NODE_ENV=production
env_file:
- .env
depends_on:
- postgres
- redis
- mongo
restart: unless-stopped
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: userstate
POSTGRES_USER: postgres
POSTGRES_PASSWORD: your_password
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --requirepass your_redis_password
volumes:
- redis_data:/data
restart: unless-stopped
mongo:
image: mongo:6
volumes:
- mongo_data:/data/db
restart: unless-stopped
volumes:
postgres_data:
redis_data:
mongo_data:
Environment Variables for Production
# Production Settings
NODE_ENV=production
API_PORT=33010
# Use production database
PS_HOST=your-prod-db-host
PS_USER=prod_user
PS_PASSWORD=strong_password
PS_DB=userstate_prod
PS_DIALECT=postgres
PS_PORT=5432
PS_POOL_MAX=50
# Strong JWT secret
SECRET_AUTH_CONFIG=long_random_string_min_32_chars
# Production CORS
ORIGIN_CORS=https://app.yourdomain.com,https://www.yourdomain.com
# External services
XENDIT_SECRET_KEY=xnd_production_...
Monitoring
Health Check Endpoint:
Add to index.js:
app.get("/health", (req, res) => {
res.json({
status: "OK",
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
PM2 Monitoring:
pm2 install pm2-logrotate # Log rotation
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
Backup Strategy
Database Backup (PostgreSQL):
# Daily backup script
pg_dump -U postgres -h localhost userstate_prod > backup_$(date +%Y%m%d).sql
# Automated daily backup with cron
0 2 * * * /path/to/backup_script.sh
Redis Backup:
Redis automatically saves RDB snapshots. Configure in redis.conf:
save 900 1
save 300 10
save 60 10000
Conclusion
This documentation provides a comprehensive guide to the UserState API. For questions or issues, please contact the development team or create an issue in the repository.
Quick Links
-
Repository:Installation[YourGuiderepository URL] - API Endpoints
- Database Schema
- Environment Variables
🏗️ Project Structure
onmart-pos-backend/
├── config/ # Database configuration
├── controllers/ # Business logic
├── docs/ # Documentation
│ ├── BACKEND.md # Backend & API documentation
│ └── DATABASE.md # Database documentation
├── helpers/ # Utility functions
├── middlewares/ # Express middlewares
├── migrations/ # Database migrations
├── models/ # Sequelize models
├── routes/ # API routes
├── seeders/ # Database seeders
├── uploads/ # File uploads
├── utils/ # Utilities
├── index.js # Entry point
└── package.json # Dependencies
🔑 API Endpoints
Base URL:URL: http://localhost:330103000/api
Core Endpoints
| Category | Endpoint | Description |
|---|---|---|
| Auth | POST /auth/login |
User login |
| Auth | GET /auth/me |
Get current user |
| Products | GET /products |
List products |
| Products | POST /products |
Create product |
| Transactions | POST /transactions |
Create transaction |
| Cashier | POST /cashier/shifts/open |
Open shift |
| Dashboard | GET /dashboard/summary |
Dashboard stats |
For complete API documentation, see BACKEND.md.
🛠️ Tech Stack
- Runtime: Node.js
-
Socket.ioFramework::SameExpressasv5.1.0 -
baseDatabase:URLPostgreSQL - ORM: Sequelize v6.37.7
- Authentication: JWT (jsonwebtoken)
- Security: Helmet, CORS, bcrypt
- File Processing: Multer, Sharp
- PDF Generation: PDFKit
- Data Processing: fast-csv, xlsx
- Logging: Morgan
🔐 Security Features
- ✅ JWT-based authentication
- ✅ Password hashing with bcrypt
- ✅ Role-based authorization
- ✅ Security headers (Helmet)
- ✅ CORS protection
- ✅ Input validation
- ✅ SQL injection prevention (Sequelize ORM)
💻 Development
SupportDatabase Commands
# Run migrations
npx sequelize-cli db:migrate
# Undo last migration
npx sequelize-cli db:migrate:undo
# Run seeders
npx sequelize-cli db:seed:all
# Database reset
npx sequelize-cli db:migrate:undo:all
npx sequelize-cli db:migrate
npx sequelize-cli db:seed:all
Environment Variables
ForKey supportenvironment andvariables questions:(see .env.example for all options):
PORT=3000
NODE_ENV=development
DB_USERNAME=postgres
DB_PASSWORD=your_password
DB_NAME=onmart_pos_dev
DB_HOST=localhost
JWT_SECRET=your_jwt_secret
ORIGIN_CORS=http://localhost:3711
📊 Database Schema
The system uses 16 tables with proper relationships:
Email:support@yourdomain.comUsers & Roles - Authentication and authorizationDocumentationupdates:Products - Inventory with categories and brands- Transactions - Sales records with items
- Shifts - Cashier working periods
- Customers - Customer information
- Promos - Promotional campaigns
- Outlets - Store locations
For detailed schema information, see DATABASE.md.
🤝 Contributing
- Fork the repository
- Create a
pullfeaturerequestbranch (git checkout -b feature/amazing-feature) BugCommitreports:your changes (git commit -m 'Add amazing feature')- Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📝 License
This project is licensed under the ISC License.
📧 Support
For questions or support:
- Open an issue in the repository
- Contact the development team
🗂️ For New Developers
If you're taking over this project, start with these documents:
- Backend Documentation - Understand the API architecture and endpoints
- Database Documentation - Learn the database structure and relationships
- Environment Setup - Follow the Quick Start guide above
- Test the API - Use the default credentials to explore the system
Recommended Reading Order
- Installation & Setup
- Database Schema Overview
- Authentication Flow
- API Endpoints Reference
- Code Structure & Organization
Last UpdatedVersion:: February 2024
Version: 1.0.0
AuthorLast Updated:: DevelopmentFebruary Team9, 2026