Skip to content

smart-programers/kijumbe-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Kijumbe App - Backend Development Guide

Overview

Kijumbe is a mobile application built with Flutter to help users manage group savings systems known as "Mchezo" in Tanzania. This guide is intended for backend developers who will be implementing the Node.js API to support this application. This document outlines the key features, data models, API requirements, and integration points for the application.

Architecture Overview

The Kijumbe app follows a feature-based architecture on the frontend with a clear separation of concerns:

lib/
├── core/                  # Core utilities, constants, and widgets
├── features/              # Main features of the app
│   ├── auth/              # Authentication feature
│   ├── groups/            # Group management feature
│   ├── home/              # Home screen feature
│   ├── navigation/        # Navigation components
│   ├── payments/          # Payment processing feature
│   └── profile/           # User profile feature
└── main.dart              # App entry point

The backend should be structured to support this architecture with RESTful endpoints organized by feature.

Key Data Models and Requirements

Authentication

The app uses phone-based authentication with OTP verification. The backend should implement:

// User Model example
const userSchema = new mongoose.Schema({
  userId: String,
  phoneNumber: String,
  firstName: String,
  lastName: String,
  email: String,
  photoUrl: String,
  createdAt: Date,
  updatedAt: Date
});

Required endpoints:

  • POST /api/auth/request-otp: Send OTP to a phone number
  • POST /api/auth/verify-otp: Verify OTP and authenticate user
  • POST /api/auth/profile: Create or update user profile
  • GET /api/auth/profile: Get user profile
  • POST /api/auth/logout: End user session

Groups

Groups are central to the application. Each group has members, contributions, payouts, and rules.

// Group Model example
const groupSchema = new mongoose.Schema({
  id: String,
  name: String,
  purpose: String,
  description: String,
  contributionAmount: Number,
  frequency: {
    type: String,
    enum: ['daily', 'weekly', 'biweekly', 'monthly']
  },
  startDate: Date,
  endDate: Date,
  memberLimit: Number,
  payoutMethod: {
    type: String,
    enum: ['sequential', 'random', 'byNeed', 'custom']
  },
  status: {
    type: String,
    enum: ['active', 'completed', 'cancelled']
  },
  members: [memberSchema],
  contributions: [contributionSchema],
  payouts: [payoutSchema],
  rules: [ruleSchema],
  createdBy: String,
  createdAt: Date,
  updatedAt: Date
});

// Group Member Model
const memberSchema = new mongoose.Schema({
  id: String,
  userId: String,
  name: String,
  photoUrl: String,
  phoneNumber: String,
  role: {
    type: String,
    enum: ['admin', 'treasurer', 'member']
  },
  joinedDate: Date,
  hasReceivedPayout: Boolean,
  isRemoved: Boolean,
  removedDate: Date,
  joinStatus: {
    type: String,
    enum: ['original', 'lateJoiner', 'replacement']
  },
  catchUpContributionIds: [String],
  hasPaidCatchUp: Boolean
});

// Contribution Model
const contributionSchema = new mongoose.Schema({
  id: String,
  groupId: String,
  memberId: String,
  amount: Number,
  dueDate: Date,
  paidDate: Date,
  status: {
    type: String,
    enum: ['pending', 'completed', 'late', 'missed']
  },
  receiptId: String,
  paymentMethod: String,
  penaltyAmount: Number
});

// Payout Model
const payoutSchema = new mongoose.Schema({
  id: String,
  groupId: String,
  memberId: String,
  amount: Number,
  scheduledDate: Date,
  processedDate: Date,
  status: {
    type: String,
    enum: ['scheduled', 'processing', 'completed', 'cancelled']
  },
  transactionId: String,
  paymentMethod: String
});

// Group Rule Model
const ruleSchema = new mongoose.Schema({
  id: String,
  groupId: String,
  title: String,
  description: String,
  type: {
    type: String,
    enum: ['latePenalty', 'missedPaymentPenalty', 'attendance', 'other']
  },
  penaltyAmount: Number,
  isActive: Boolean
});

Required endpoints:

  • GET /api/groups: Get all available groups
  • GET /api/groups/user/:userId: Get groups for a specific user
  • GET /api/groups/:id: Get a specific group by ID
  • POST /api/groups: Create a new group
  • PUT /api/groups/:id: Update a group
  • DELETE /api/groups/:id: Delete a group
  • POST /api/groups/:id/join: Join an existing group
  • POST /api/groups/:id/members: Add a new member to a group
  • POST /api/groups/:id/members/late: Add a late-joining member with catch-up calculations

Payments

The app handles payments for contributions with a focus on mobile money in Tanzania:

// Payment Model
const paymentSchema = new mongoose.Schema({
  id: String,
  userId: String,
  groupId: String,
  contributionId: String,
  amount: Number,
  feeAmount: Number,
  dateInitiated: Date,
  dateCompleted: Date,
  status: {
    type: String,
    enum: ['pending', 'processing', 'completed', 'failed', 'cancelled']
  },
  method: {
    type: String,
    enum: ['mobileMoney', 'bankTransfer', 'cash', 'other']
  },
  transactionId: String,
  receiptUrl: String,
  notes: String,
  metadata: Object
});

// Receipt Model
const receiptSchema = new mongoose.Schema({
  id: String,
  paymentId: String,
  groupId: String,
  userId: String,
  userName: String,
  groupName: String,
  amount: Number,
  feeAmount: Number,
  date: Date,
  transactionId: String,
  receiptNumber: String,
  paymentMethod: String,
  additionalInfo: Object
});

// Upcoming Payment Model
const upcomingPaymentSchema = new mongoose.Schema({
  id: String,
  groupId: String,
  groupName: String,
  amount: Number,
  dueDate: Date,
  isPaid: Boolean
});

Required endpoints:

  • GET /api/payments/user/:userId: Get a user's payment history
  • GET /api/payments/group/:groupId: Get a group's payment history
  • GET /api/payments/:id: Get a specific payment
  • POST /api/payments: Make a payment
  • GET /api/payments/upcoming/:userId: Get upcoming payments for a user
  • GET /api/payments/:id/receipt: Get a receipt for a payment
  • POST /api/payments/mobile-money/init: Initialize a mobile money payment
  • POST /api/payments/mobile-money/verify: Verify a mobile money payment

Mobile Money Integration

The app supports multiple mobile money providers in Tanzania. The backend should implement:

  • Integration with M-Pesa API
  • Integration with Tigo Pesa API
  • Integration with Airtel Money API
  • Webhook endpoints for payment notifications

Example mobile money initialization endpoint:

/**
 * Initialize a mobile money payment
 * @param {string} provider - The mobile money provider (e.g., 'mpesa', 'tigopesa', 'airtelmoney')
 * @param {string} phoneNumber - The phone number for the transaction
 * @param {number} amount - The amount to pay
 * @param {string} description - Payment description
 * @returns {object} Payment initialization details with a reference number
 */
app.post('/api/payments/mobile-money/init', async (req, res) => {
  try {
    const { provider, phoneNumber, amount, description } = req.body;
    
    // Call the appropriate provider API
    const result = await mobileMoneyService.initiatePayment(provider, phoneNumber, amount, description);
    
    res.status(200).json({
      success: true,
      referenceNumber: result.referenceNumber,
      message: `Payment request sent to ${phoneNumber}. Please check your phone to confirm.`
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: `Payment initialization failed: ${error.message}`
    });
  }
});

Key Backend Implementation Details

Late Joiner Calculations

The app includes a feature for adding members who join late to a group. The backend needs to implement the logic for calculating catch-up contributions:

/**
 * Calculate missed contributions for a late-joining member
 * @param {Object} group - The group object
 * @param {Date} joinDate - The date the member is joining
 * @returns {number} Number of missed contributions
 */
function calculateMissedContributions(group, joinDate) {
  // If member joined before the group started, they haven't missed any
  if (joinDate <= group.startDate) {
    return 0;
  }

  // Calculate time since group started
  const duration = joinDate - group.startDate;
  const durationDays = Math.floor(duration / (1000 * 60 * 60 * 24));

  // Calculate based on contribution frequency
  switch (group.frequency) {
    case 'daily':
      return durationDays;
    case 'weekly':
      return Math.ceil(durationDays / 7);
    case 'biweekly':
      return Math.ceil(durationDays / 14);
    case 'monthly':
      // Calculate approximate months
      return Math.floor((durationDays + 15) / 30);
    default:
      return 0;
  }
}

Transaction Fee Calculation

The app applies transaction fees to payments. The backend should implement this logic:

/**
 * Calculate transaction fee
 * @param {number} amount - The transaction amount
 * @returns {number} The fee amount
 */
function calculateTransactionFee(amount) {
  const feePercentage = 2.0; // 2% fee
  const minimumFee = 500.0; // 500 TZS minimum
  const maximumFee = 10000.0; // 10,000 TZS maximum

  // Calculate fee as percentage of transaction amount
  let fee = (amount * feePercentage) / 100.0;

  // Apply minimum fee
  if (fee < minimumFee && amount > minimumFee) {
    fee = minimumFee;
  }

  // Apply maximum fee
  if (fee > maximumFee) {
    fee = maximumFee;
  }

  // Round to nearest whole number
  return Math.round(fee);
}

Authentication and Security

The backend should implement:

  1. JWT-based authentication
  2. Phone verification using SMS services (e.g., Twilio, Africa's Talking)
  3. Rate limiting for OTP requests
  4. Secure storage of user data
  5. Input validation and sanitization

Example JWT implementation:

const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET;

// Generate JWT token
function generateToken(userId) {
  return jwt.sign({ userId }, JWT_SECRET, { expiresIn: '7d' });
}

// Verify JWT token middleware
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) return res.status(401).json({ message: 'Authentication required' });

  jwt.verify(token, JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: 'Invalid or expired token' });
    req.user = user;
    next();
  });
}

Recommended Project Structure

For the Node.js backend, we recommend this structure:

backend/
├── config/                 # Configuration files
├── controllers/            # Route handlers
│   ├── authController.js
│   ├── groupController.js
│   ├── paymentController.js
│   └── ...
├── middleware/             # Custom middleware
│   ├── auth.js             # Authentication middleware
│   └── ...
├── models/                 # Database models
│   ├── User.js
│   ├── Group.js
│   ├── Payment.js
│   └── ...
├── routes/                 # Route definitions
│   ├── auth.js
│   ├── groups.js
│   ├── payments.js
│   └── ...
├── services/               # Business logic
│   ├── authService.js
│   ├── groupService.js
│   ├── paymentService.js
│   ├── mobileMoneyService.js
│   └── ...
├── utils/                  # Utility functions
│   ├── dateUtils.js
│   ├── currencyUtils.js
│   └── ...
├── app.js                  # Express application setup
└── server.js               # Server entry point

API Response Format

We recommend a consistent API response format:

// Success response
{
  "success": true,
  "data": {
    // Response data here
  },
  "message": "Operation successful"
}

// Error response
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Error message"
  }
}

Environment Variables

Required environment variables:

# Server configuration
PORT=3000
NODE_ENV=development
API_URL=http://localhost:3000

# Database
MONGO_URI=mongodb://localhost:27017/kijumbe

# Authentication
JWT_SECRET=your_jwt_secret
OTP_EXPIRY_TIME=300 # seconds

# SMS Service (for OTP)
SMS_API_KEY=your_sms_api_key
SMS_SENDER_ID=KIJUMBE

# Mobile Money APIs
MPESA_API_KEY=your_mpesa_api_key
MPESA_API_SECRET=your_mpesa_api_secret
TIGO_API_KEY=your_tigo_api_key
AIRTEL_API_KEY=your_airtel_api_key

Testing

We recommend implementing comprehensive tests for the API:

  • Unit tests for utility functions and services
  • Integration tests for API endpoints
  • Load testing for payment processing

Deployment Considerations

  • Use environment variables for configuration
  • Implement proper error logging
  • Set up monitoring for the API
  • Configure CORS for the Flutter app
  • Implement rate limiting
  • Set up database backups

Communication Protocol with Frontend

The Flutter app expects:

  1. RESTful endpoints with JSON responses
  2. JWT authentication via Authorization header
  3. Proper HTTP status codes (200, 201, 400, 401, 403, 404, 500)
  4. Consistent error messages

Conclusion

This guide should provide a solid foundation for implementing the backend services required by the Kijumbe app. The key focus areas are authentication, group management, and payment processing with special attention to mobile money integration and late joiner calculations.

For any questions or clarifications, please refer to the frontend code repository or contact the development team.

About

No description, website, or topics provided.

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages