Zaptam Services Reference
Complete documentation of all backend services with methods, parameters, and return types.
Table of Contents
- AuthService
- MatchService
- MessageService
- UserService
- WalletService
- VerificationService
- ReportService
- TrustService
- NotificationService
AuthService
File: src/services/auth.service.ts
Handles user registration, authentication, OTP verification, and password management.
Methods
applyForAccount(params: ApplyForAccountParams): Promise<OtpResponse>
Creates a pending user account and sends verification OTP.
interface ApplyForAccountParams {
phoneNumber: string; // International format: +268XXXXXXXX
gender: Gender; // 'MALE' | 'FEMALE'
email?: string;
name?: string;
occupation?: string; // Required for MALE
intro?: string; // For FEMALE only
netWorth?: string; // Required for MALE: '50k-100k' | '100k-500k' | '500k-1m' | '1m-5m' | '5m+'
photos?: Express.Multer.File[]; // Required for FEMALE
}
// Returns
interface OtpResponse {
success: boolean;
message: string;
expiresAt?: Date; // OTP expiration (10 minutes)
}Validation Rules:
- Female applicants must include at least 1 photo
- Male applicants must specify netWorth
- Throws
ConflictErrorif phone already registered and active
sendPhoneOtp(phoneNumber: string, purpose: OtpPurpose): Promise<OtpResponse>
Sends OTP for login, registration, or password reset.
type OtpPurpose = 'REGISTRATION' | 'LOGIN' | 'PASSWORD_RESET';verifyOtp(phoneNumber: string, code: string, purpose: OtpPurpose): Promise<boolean>
Validates OTP code. Marks OTP as used if valid.
completeRegistration(phoneNumber: string, password: string, bio?: string, dateOfBirth?: Date): Promise<TokenPair>
Finalizes registration after OTP verification.
interface TokenPair {
accessToken: string; // JWT, expires in 15m
refreshToken: string; // UUID, stored in DB, expires in 7d
}Actions:
- Hashes password (bcrypt)
- Sets user status to
ACTIVE - Creates default
UserSettingsrecord
login(phoneNumber: string, password: string): Promise<TokenPair>
Authenticates user with password.
Checks:
- User exists and has password
- Status is not BANNED, DELETED, or PENDING
- Password matches
refreshAccessToken(refreshToken: string): Promise<TokenPair>
Rotates tokens. Revokes old refresh token.
logout(userId: string, refreshToken?: string): Promise<void>
Revokes refresh token(s). If no token provided, revokes all user tokens.
resetPassword(phoneNumber: string, code: string, newPassword: string): Promise<void>
Resets password after OTP verification. Revokes all existing tokens.
MatchService
File: src/services/match.service.ts
Handles profile discovery, interest expression, and matching.
Methods
discoverProfiles(userId: string, page: number, limit: number, filters?: DiscoveryFilters): Promise<PaginatedResponse<ProfilePreview>>
Returns paginated list of discoverable profiles.
interface DiscoveryFilters {
gender?: Gender;
verificationLevel?: 'NONE' | 'IDENTITY' | 'FULL';
}
interface ProfilePreview {
id: string;
alias: string | null;
gender: Gender | null;
bio: string | null;
trustScore: number;
verificationLevel: string;
photos: {
id: string;
url: string;
blurLevel: number;
isPrimary: boolean;
}[];
}Ordering (priority):
- Verification level (FULL > IDENTITY > NONE)
- Trust score (highest first)
- Last active (most recent first)
Default filter: Shows opposite gender
getProfileById(userId: string, profileId: string): Promise<ProfileWithMatch>
Returns profile details with match status.
// If mutual match exists:
// - Photos returned with original blur levels
// - lastActiveAt visible if user allows
// If no match:
// - Photos have minimum 80% blur enforced
// - lastActiveAt hiddenexpressInterest(senderId: string, recipientId: string): Promise<{ matched: boolean }>
Expresses interest in another user.
Logic:
// Check for reciprocal interest
const reciprocalInterest = await findExistingInterest(recipientId, senderId);
if (reciprocalInterest?.status === 'PENDING') {
// MUTUAL MATCH!
// 1. Update reciprocal to ACCEPTED
// 2. Create new interest as ACCEPTED
// 3. Create Conversation
return { matched: true };
}
// Create pending interest
await createInterest(senderId, recipientId, 'PENDING');
return { matched: false };getMatches(userId: string, page: number, limit: number): Promise<PaginatedResponse<Match>>
Returns all accepted matches for the user.
declineMatch(userId: string, matchId: string): Promise<void>
Removes a match (deletes interest record).
MessageService
File: src/services/message.service.ts
Manages conversations and messages.
Methods
getConversations(userId: string, page: number, limit: number): Promise<PaginatedResponse<ConversationPreview>>
Returns user's conversations with last message and unread count.
interface ConversationPreview {
id: string;
otherUser: {
id: string;
alias: string | null;
photos: Photo[]; // Primary photo only
};
lastMessage: {
content: string | null;
createdAt: Date;
isFromMe: boolean;
} | null;
unreadCount: number;
updatedAt: Date;
}getMessages(userId: string, conversationId: string, page: number, limit: number): Promise<PaginatedResponse<Message>>
Returns messages for a conversation.
Side effects:
- Marks all unread messages as read for the requesting user
- Filters out expired messages (
expiresAt < now) - Filters out deleted messages (
deletedAt != null)
sendMessage(senderId: string, conversationId: string, content?: string, mediaUrl?: string, expiresInHours?: number): Promise<Message>
Creates a new message.
interface Message {
id: string;
conversationId: string;
senderId: string;
recipientId: string;
content: string | null;
mediaUrl: string | null;
isRead: boolean;
expiresAt: Date | null; // Disappearing messages
deletedAt: Date | null;
createdAt: Date;
}Actions:
- Validates user is part of conversation
- Calculates
expiresAtif disappearing messages enabled - Updates conversation
updatedAt
deleteConversation(userId: string, conversationId: string): Promise<void>
Soft-deletes user's messages in conversation (sets deletedAt).
getOrCreateConversation(userId: string, otherUserId: string): Promise<string>
Returns existing conversation ID or creates new one.
Requirements:
- Users must have a mutual match (accepted interest)
- Throws
ForbiddenErrorif no match exists
UserService
File: src/services/user.service.ts
User profile and settings management.
Methods
getUserById(userId: string): Promise<SafeUser>
Returns user with settings and photos (excludes password).
getUserSettings(userId: string): Promise<UserSettings>
Returns user settings. Creates default if none exist.
interface UserSettings {
aliasMode: boolean; // Show alias vs real name (default: true)
showOnlineStatus: boolean; // Visible online status (default: false)
regionMasking: boolean; // Hide location (default: true)
photoBlurLevel: number; // 0-100 blur percentage (default: 100)
disappearingMsgs: number | null; // Hours until messages expire
}updateProfile(userId: string, data: ProfileUpdate): Promise<SafeUser>
Updates profile fields.
interface ProfileUpdate {
alias?: string;
bio?: string;
email?: string;
dateOfBirth?: Date;
}updateSettings(userId: string, data: SettingsUpdate): Promise<UserSettings>
Updates user settings.
changePassword(userId: string, currentPassword: string, newPassword: string): Promise<void>
Changes password with current password verification.
deleteAccount(userId: string): Promise<void>
Soft delete — sets status to DELETED, marks deletedAt.
panicDelete(userId: string): Promise<void>
HARD DELETE — Permanently removes all user data:
await prisma.$transaction([
prisma.message.deleteMany({ ... }),
prisma.conversation.deleteMany({ ... }),
prisma.interest.deleteMany({ ... }),
prisma.photo.deleteMany({ ... }),
prisma.verification.deleteMany({ ... }),
prisma.walletTransaction.deleteMany({ ... }),
prisma.report.deleteMany({ ... }),
prisma.refreshToken.deleteMany({ ... }),
prisma.otpCode.deleteMany({ ... }),
prisma.userSettings.deleteMany({ ... }),
prisma.user.delete({ ... }),
]);updateLastActive(userId: string): Promise<void>
Updates lastActiveAt timestamp. Called on socket connection.
WalletService
File: src/services/wallet.service.ts
Handles credits, earnings, and transactions.
Methods
getWalletBalance(userId: string): Promise<WalletBalance>
Calculates balance from transaction history.
interface WalletBalance {
balance: number;
currency: string; // 'USD'
userType: 'earner' | 'spender'; // Female = earner, Male = spender
}Balance calculation:
let balance = 0;
for (const tx of completedTransactions) {
if (tx.type === 'EARNING' || tx.type === 'CREDIT_PURCHASE') {
balance += tx.amount;
} else if (tx.type === 'WITHDRAWAL' || tx.type === 'BOOST' || tx.type === 'MEMBERSHIP') {
balance -= tx.amount;
}
}getTransactionHistory(userId: string, page: number, limit: number): Promise<PaginatedResponse<Transaction>>
Returns paginated transaction history.
purchaseCredits(userId: string, amount: number, paymentReference: string): Promise<void>
Records credit purchase. Male users only.
purchaseMembership(userId: string, tierId: string, paymentReference: string): Promise<void>
Records membership purchase based on tier price.
requestWithdrawal(userId: string, amount: number, payoutDetails: string): Promise<void>
Creates pending withdrawal request. Female users only.
Requirements:
- Minimum withdrawal: $10
- Must have sufficient balance
processWithdrawal(transactionId: string, approved: boolean, adminId: string): Promise<void>
Admin approves/rejects withdrawal. Updates status to COMPLETED or FAILED.
getMembershipTiers(): Promise<MembershipTier[]>
Returns active membership tiers.
VerificationService
File: src/services/verification.service.ts
Manages identity and worth/value verification.
Methods
getVerificationStatus(userId: string): Promise<VerificationStatus>
Returns current verification level and history.
interface VerificationStatus {
currentLevel: 'NONE' | 'IDENTITY' | 'FULL';
verifications: Verification[];
}submitVerification(userId: string, type: VerificationType, documents: string[]): Promise<void>
Submits verification request.
type VerificationType = 'IDENTITY' | 'WORTH' | 'VALUE';Rules:
WORTH— Male users onlyVALUE— Female users only- Cannot submit if pending verification of same type exists
reviewVerification(verificationId: string, reviewerId: string, approved: boolean, notes?: string): Promise<void>
Admin reviews verification.
On approval:
- Updates verification status to
APPROVED - Updates user
verificationLevel:IDENTITYapproval: NONE → IDENTITYWORTH/VALUEapproval: IDENTITY → FULL
getPendingVerifications(page: number, limit: number): Promise<PaginatedResponse<Verification>>
Returns pending verifications for admin review.
ReportService
File: src/services/report.service.ts
User reporting and moderation.
Methods
createReport(data: CreateReportData): Promise<Report>
Creates a report against another user.
interface CreateReportData {
reporterId: string;
reportedId: string;
type: ReportType; // 'BLACKMAIL' | 'HARASSMENT' | 'FAKE_PROFILE' | 'INAPPROPRIATE_CONTENT' | 'OTHER'
description: string;
evidence?: any; // JSON evidence
}Validation: Cannot have duplicate active report (PENDING/INVESTIGATING) for same user+type.
createBlackmailReport(reporterId: string, reportedId: string, description: string, evidence?: any): Promise<Report>
High-priority blackmail report.
Automatic actions:
- Status set to
INVESTIGATING(not PENDING) - Immediate -20 trust score penalty to reported user
getReports(filters: ReportFilters): Promise<PaginatedReports>
Returns reports with optional filters.
interface ReportFilters {
status?: ReportStatus;
type?: ReportType;
page?: number;
limit?: number;
}getReportById(reportId: string): Promise<Report>
Returns detailed report with reporter and reported user info.
getUserReports(userId: string): Promise<Report[]>
Returns reports submitted by a user.
TrustService
File: src/services/trust.service.ts
Trust score calculation and management.
Methods
calculateTrustScore(userId: string): Promise<number>
Calculates trust score (0-100) from multiple factors.
See Trust System Documentation for full algorithm.
updateTrustScore(userId: string): Promise<number>
Recalculates and saves trust score.
applyTrustPenalty(userId: string, penalty: number, reason: string): Promise<number>
Directly reduces trust score.
const newScore = Math.max(0, currentScore - penalty);reportScreenshotDetected(userId: string): Promise<void>
Applies -10 penalty for screenshot detection.
NotificationService
File: src/services/notification.service.ts
WhatsApp/SMS OTP delivery.
Methods
sendOtpNotification(phoneNumber: string, code: string, purpose: string): Promise<NotificationResponse>
Sends OTP via Eneza notification service.
interface NotificationPayload {
messageType: 'authentication';
phoneNumber: string;
authCode: string;
buttonUrl: 'verify-code' | 'login-code' | 'reset-code';
channel: 'whatsapp' | 'sms';
}
interface NotificationResponse {
success: boolean;
message?: string;
error?: string;
}Configuration:
const NOTIFICATION_SERVICE_URL = 'https://notifications.eneza.app';
const DEFAULT_CHANNEL = 'whatsapp';