Skip to content

Latest commit

Β 

History

History
954 lines (693 loc) Β· 27.2 KB

CONTRIBUTING.md

File metadata and controls

954 lines (693 loc) Β· 27.2 KB

Contributing to StellarEduPay

Thank you for your interest in contributing to StellarEduPay! This guide will help you get started with development, understand our conventions, and successfully submit your first pull request.


πŸ“‹ Table of Contents


πŸš€ Getting Started

Prerequisites

Before you begin, ensure you have the following installed:

  • Node.js (v18 or higher)
  • npm (v9 or higher)
  • MongoDB (v7 or higher) OR Docker (for containerized setup)
  • Redis (v6 or higher) - required for BullMQ retry queue
  • Git for version control

First-Time Setup

  1. Fork and Clone the Repository

    git clone https://github.com/YOUR_USERNAME/stellaredupay.git
    cd stellaredupay
  2. Install Dependencies

    # Root dependencies (for testing)
    npm install
    
    # Backend dependencies
    cd backend
    npm install
    
    # Frontend dependencies
    cd ../frontend
    npm install
    cd ..
  3. Configure Environment Variables

    Copy the example environment files and configure them:

    # Root environment
    cp .env.example .env
    
    # Backend environment
    cp backend/.env.example backend/.env
    
    # Frontend environment
    cp frontend/.env.example frontend/.env.local
  4. Set Up Stellar Testnet Wallet

    • Visit Stellar Laboratory
    • Generate a new keypair for testing
    • Fund your testnet wallet using Friendbot
    • Add the public key to SCHOOL_WALLET_ADDRESS in your .env files
  5. Start Required Services

    Option A: Using Docker (Recommended)

    docker-compose up -d mongo redis

    Option B: Local Installation

    • Start MongoDB on port 27017
    • Start Redis on port 6379
  6. Run the Application

    # Terminal 1: Start backend
    cd backend
    npm run dev
    
    # Terminal 2: Start frontend
    cd frontend
    npm run dev
  7. Verify Setup


πŸ’» Development Setup

Running with Docker

For a complete containerized environment:

# Build and start all services
docker-compose up --build

# Run in detached mode
docker-compose up -d

# View logs
docker-compose logs -f backend

# Stop all services
docker-compose down

Database Migrations

If you need to run database migrations:

cd backend
node migrations/001_backfill_remaining_balance.js

🌿 Branch Naming Convention

Use the following prefixes for your branches:

  • feature/ - New features or enhancements

    • Example: feature/add-payment-reminders
  • fix/ - Bug fixes

    • Example: fix/transaction-polling-race-condition
  • docs/ - Documentation updates

    • Example: docs/update-api-spec
  • refactor/ - Code refactoring without behavior changes

    • Example: refactor/extract-stellar-client
  • test/ - Adding or updating tests

    • Example: test/add-fee-adjustment-tests
  • chore/ - Maintenance tasks, dependency updates

    • Example: chore/update-stellar-sdk

Branch Naming Rules:

  • Use lowercase with hyphens
  • Be descriptive but concise
  • Reference issue number when applicable: fix/123-payment-validation

πŸ“ Commit Message Guidelines

We follow Conventional Commits specification:

Format

<type>(<scope>): <subject>

<body>

<footer>

Types

  • feat: - New feature
  • fix: - Bug fix
  • docs: - Documentation changes
  • style: - Code style changes (formatting, no logic change)
  • refactor: - Code refactoring
  • test: - Adding or updating tests
  • chore: - Maintenance tasks

Examples

feat(payments): add payment reminder scheduler

Implements automated email reminders for overdue payments.
Includes configurable cooldown period and max reminder count.

Closes #45

---

fix(stellar): handle rate limit errors from Horizon API

Added exponential backoff retry logic when Horizon returns 429.

Fixes #67

---

docs(api): update payment endpoints documentation

Added examples for bulk payment verification endpoint.

Commit Rules

  • Use present tense ("add feature" not "added feature")
  • Use imperative mood ("move cursor to..." not "moves cursor to...")
  • First line should be 50 characters or less
  • Reference issues and PRs in the footer

πŸ“ Coding Standards

JavaScript/Node.js Style

  • Use ES6+ syntax (async/await, arrow functions, destructuring)
  • Use 2 spaces for indentation
  • Use single quotes for strings
  • Add semicolons at the end of statements
  • Use camelCase for variables and functions
  • Use PascalCase for classes and React components
  • Use UPPER_SNAKE_CASE for constants

Code Organization

Backend Structure:

backend/src/
β”œβ”€β”€ routes/          # Express route definitions only
β”œβ”€β”€ controllers/     # Request/response handling, no business logic
β”œβ”€β”€ services/        # Business logic and external API calls
β”œβ”€β”€ models/          # Mongoose schemas and database models
β”œβ”€β”€ middleware/      # Express middleware (auth, validation, etc.)
β”œβ”€β”€ utils/           # Helper functions and utilities
└── config/          # Configuration files

Frontend Structure:

frontend/src/
β”œβ”€β”€ pages/           # Next.js pages (routing)
β”œβ”€β”€ components/      # Reusable React components
β”œβ”€β”€ services/        # API client and external services
└── styles/          # CSS and styling

Best Practices

  • Keep functions small and focused - Each function should do one thing well
  • Use meaningful variable names - Avoid abbreviations unless widely understood
  • Add JSDoc comments for public functions and complex logic
  • Handle errors gracefully - Always catch and log errors appropriately
  • Avoid hardcoding values - Use environment variables or configuration files
  • Validate inputs - Use Joi schemas for API validation
  • Use async/await - Prefer async/await over raw promises

Example Code Style

// Good
const calculateTotalFees = async (studentId, feeStructureId) => {
  try {
    const student = await Student.findById(studentId);
    if (!student) {
      throw new Error('Student not found');
    }
    
    const feeStructure = await FeeStructure.findById(feeStructureId);
    return feeStructure.calculateTotal(student);
  } catch (error) {
    logger.error('Failed to calculate fees', { studentId, error });
    throw error;
  }
};

// Bad
const calc = (sid, fid) => {
  return Student.findById(sid).then(s => {
    return FeeStructure.findById(fid).then(f => f.calculateTotal(s));
  });
};

πŸ§ͺ Testing Requirements

Where tests live

Location Purpose
backend/tests/ Backend unit / service / controller tests
tests/ Root cross-cutting suites only (e2e, integration, helpers)
frontend/ Frontend tests (own package scripts)

Rule: never put the same filename under both tests/ and backend/tests/.

Running tests

# Root suite (tests/ only β€” not backend/tests)
npm test

All tests mock both the Stellar SDK and MongoDB β€” no real network connection or running database is required.

Expected output:

PASS tests/stellar.test.js
PASS tests/payment.test.js
PASS tests/payment-limits.test.js
...

Test Suites: X passed, X total
Tests:       X passed, X total
Time:        ~5s

3. Run a single test file

npm test -- tests/stellar.test.js
npm test -- tests/payment.test.js
npm test -- tests/payment-limits.test.js

4. Check code coverage

# Backend (root Jest config β†’ backend/src)
npm run coverage

# Frontend (frontend/jest.config.js β†’ frontend/src)
npm run coverage:frontend

# Equivalent one-off form (works for either package):
npm test -- --coverage

Jest will print a per-file coverage table (lines, statements, functions and branches) and write a full report to:

  • coverage/backend/ β€” backend source (backend/src)
  • coverage/frontend/ β€” frontend source (frontend/src)

Each directory contains lcov-report/index.html (browsable), lcov.info, coverage-summary.json and cobertura-coverage.xml. See Test Coverage for the measured baseline, thresholds, and the expectation for new code.

Test files and what they cover

File Coverage
tests/stellar.test.js Stellar service: asset detection, fee validation, amount normalisation, transaction verification, ledger sync
tests/payment.test.js Payment API: full payment flow, all endpoints, edge cases, error handling
tests/payment-limits.test.js Payment limits: validation, boundary cases, error codes

Environment variables for tests

Tests use mocks and do not require a real .env file. If you want to run the backend's own test suite separately:

cd backend
npm test

The backend tests also use mocks; no live MongoDB or Stellar Horizon connection is needed.

Test Coverage Requirements

  • New features must include unit tests
  • Bug fixes should include regression tests
  • API endpoints require integration tests
  • Critical services (Stellar integration, payment processing) require comprehensive test coverage

Writing Tests

Backend suite only

npm run test:backend

  • Be isolated and not depend on external services
  • Mock Stellar SDK calls to avoid hitting the network
  • Use descriptive test names that explain the scenario
  • Follow the Arrange-Act-Assert pattern

Example Test:

describe('Payment Validation', () => {
  it('should reject payments below minimum amount', async () => {
    // Arrange
    const payment = { amount: 0.001, studentId: 'STU001' };
    
    // Act
    const result = await validatePayment(payment);
    
    // Assert
    expect(result.valid).toBe(false);
    expect(result.error).toContain('below minimum');
  });
});

πŸ“Š Test Coverage

StellarEduPay measures test coverage on every pull request (see issue #1288). Coverage is collected with Jest's instrumentation for backend/src (root Jest config) and frontend/src (frontend Jest config). The CI coverage job runs both and uploads the reports as downloadable artefacts on every PR, so reviewers can inspect exactly which code paths a change exercises.

What is measured

Coverage is reported for four metrics β€” lines, statements, functions, and branches β€” and branch coverage is tracked and reported separately from line coverage. Every defect enumerated in #1288 is an unexecuted branch inside a function whose other branches are tested, so branch coverage is the signal that would have surfaced them; line coverage alone would have partially masked them.

Running it locally

Command Scope
npm run coverage backend (backend/src)
npm run coverage:frontend frontend (frontend/src)
npm test -- --coverage whichever package you run it in

Reports land in coverage/backend and coverage/frontend (lcov-report/index.html, lcov.info, coverage-summary.json, cobertura-coverage.xml).

Measured baseline

The baseline below was captured from main and is recorded here and in issue #1288. It is preliminary: ~64 of 219 suites are currently failing (the blocked prerequisite in #1288), so the numbers understate real coverage. They will be re-measured and the floors raised once the suite is green.

Area Lines Branches Functions Statements
backend/src 49.45% 43.12% 44.34% 48.77%
frontend/src 39.37% 47.27% 23% 38.2%

The no-regression floors configured in the Jest configs (set at the measured baseline, with a small margin for run-to-run variance) are:

Scope Lines Branches Functions Statements
global β€” all of backend/src except services/, middleware/, controllers/ 51% 46% 37% 51%
backend/src/services/ 53% 45% 51% 52%
backend/src/middleware/ 52% 45% 42% 50%
backend/src/controllers/ 37% 32% 32% 36%
frontend/src (global) 39% 47% 22% 38%

Thresholds (no-regression floor)

A coverageThreshold is set in each Jest config at (or slightly below) the measured baseline, so coverage cannot regress from the numbers above:

  • Global floor β€” applies repo-wide. A PR that drops overall coverage below the baseline fails the coverage check.
  • Per-directory floors for backend/src/services/, backend/src/middleware/ and backend/src/controllers/ β€” the money, auth and tenant-isolation code. These carry the same starting floor today, but are the areas intended to be held to a much higher bar (target β‰₯ 70%) once the suite is green, because error-handling and failover paths there are exactly what a payments platform depends on.

The CI coverage job is intentionally non-gating for now: with the suite red, a low baseline must not block merges. The thresholds still enforce "no further regression." When the suite is green, promote the job to a required check and raise the per-directory floors.

Expectation for new code

  • New features and bug fixes must ship with tests (regression tests for fixes).
  • Cover the error-handling and failover branches, not just the happy path β€” that is where the highest-risk, least-tested code lives.
  • A PR should not reduce coverage below the global floor, and should aim to meet or exceed the threshold for the directory it touches.
  • Coverage on changed lines is the primary review signal; treat a drop there as a request to add tests.

Follow-up backlog

The ten lowest-covered modules under backend/src/services/ are tracked in follow-up issue #1303 β€” the concrete backlog this measurement produces.


πŸ”„ Pull Request Process

Before Submitting

  1. Sync with main branch

    git checkout main
    git pull origin main
    git checkout your-branch
    git rebase main
  2. Run all checks locally

    # Run tests (from project root)
    npm test
    
    # Check backend syntax
    node -c backend/src/app.js
  3. Update documentation

    • Update docs/api-spec.md if you changed API endpoints
    • Update README.md if you added new features or changed setup
    • Add inline code comments for complex logic

Submitting Your PR

  1. Push your branch

    git push origin your-branch-name
  2. Create Pull Request on GitHub

    • Use a clear, descriptive title
    • Reference the issue: "Closes #123" or "Fixes #456"
    • Describe what changed and why
    • Include screenshots for UI changes
    • List any breaking changes
  3. PR Template

    ## Description
    Brief description of changes
    
    ## Related Issue
    Closes #123
    
    ## Changes Made
    - Added payment reminder scheduler
    - Updated email notification service
    - Added tests for reminder logic
    
    ## Testing
    - [ ] Unit tests added/updated
    - [ ] Manual testing completed
    - [ ] No console errors
    
    ## Screenshots (if applicable)
    [Add screenshots for UI changes]

PR Review Requirements

Your PR must meet these criteria before merging:

  • Tests passing - All existing and new tests must pass
  • No merge conflicts - Rebase on latest main if needed
  • Code review approved - At least one maintainer approval required
  • Documentation updated - Relevant docs reflect your changes
  • Follows conventions - Branch naming, commit messages, code style
  • No sensitive data - No secrets, API keys, or PII in code

Review Process

  1. Maintainers will review your PR within 2-3 business days
  2. Address any requested changes by pushing new commits
  3. Once approved, a maintainer will merge your PR
  4. Your branch will be automatically deleted after merge

πŸ— Architecture Guidelines

Service-Oriented Architecture

We follow a strict Service-Oriented Architecture. To maintain the codebase:

Backend Layer Responsibilities

Routes (backend/src/routes/)

  • Define endpoints only
  • Apply middleware (auth, validation, rate limiting)
  • Delegate to controllers

Controllers (backend/src/controllers/)

  • Extract request data
  • Call service methods
  • Format responses
  • Do not put business logic here

Services (backend/src/services/)

  • Contains all business logic
  • Handles external API calls (Stellar Horizon, webhooks)
  • Manages database transactions
  • All Stellar-specific logic must go into stellarService.js

Models (backend/src/models/)

  • Mongoose schemas
  • Database validation rules
  • Instance methods for model-specific operations

Middleware (backend/src/middleware/)

  • Authentication and authorization
  • Request validation (Joi schemas)
  • Error handling
  • Rate limiting and idempotency

Frontend Architecture

  • Use Functional Components with React Hooks
  • Style with Tailwind CSS (via globals.css)
  • UI components in frontend/src/components/
  • Page-level logic in frontend/src/pages/
  • All backend communication through frontend/src/services/api.js

Adding New Features

When adding a new feature:

  1. Update API spec first - Document in docs/api-spec.md
  2. Create service layer - Add business logic to appropriate service
  3. Add controller - Create controller to handle HTTP requests
  4. Define routes - Add routes with proper middleware
  5. Add validation - Create Joi schema in middleware/schemas/
  6. Write tests - Add unit and integration tests
  7. Update docs - Document in relevant markdown files

πŸ” Security Best Practices

As a blockchain-based payment system, security is paramount:

Environment Variables

  • Never hardcode sensitive values (wallet addresses, secret keys, API keys)
  • Refer to .env.example files for required configuration
  • Use process.env to access environment variables
  • Never commit .env files to version control

Stellar Protocol Security

  • Memo Validation: Always ensure studentId is passed correctly in the Stellar Memo field
  • Secret Keys: Never log or store secret keys. Only use public keys for ledger lookups
  • Amount Validation: Use amountNormalizer.js for safe decimal handling
  • Transaction Verification: Always verify transactions against the blockchain

API Security

  • Use JWT authentication for protected endpoints
  • Implement rate limiting on all public endpoints
  • Validate all inputs with Joi schemas
  • Use idempotency keys for payment operations
  • Sanitize user inputs to prevent injection attacks

Data Privacy

  • Never log sensitive information (passwords, secret keys, PII)
  • Follow GDPR principles for data handling

πŸ§ͺ Testing Standards

We maintain a high bar for reliability with comprehensive test coverage.

Test Types

Unit Tests

  • Test individual functions and utilities
  • Mock external dependencies (Stellar SDK, database)
  • Focus on edge cases and error handling

Integration Tests

  • Test complete API flows
  • Verify database operations
  • Test middleware chains

Example Test Structure:

describe('Fee Adjustment Engine', () => {
  beforeEach(() => {
    // Setup test data
  });
  
  afterEach(() => {
    // Cleanup
  });
  
  it('should apply early payment discount', async () => {
    // Arrange
    const payment = { amount: 100, dueDate: '2026-04-30' };
    
    // Act
    const adjusted = await feeAdjustmentEngine.apply(payment);
    
    // Assert
    expect(adjusted.finalAmount).toBe(95);
    expect(adjusted.discountApplied).toBe(5);
  });
});

Running Tests

# Run all backend tests
cd backend
npm test

# Run specific test file
npm test -- tests/verify_fee_validation.js

# Run with coverage
npm test -- --coverage

Test Requirements for PRs

  • All new features must include tests
  • Bug fixes should include regression tests
  • Tests must pass before PR can be merged
  • Aim for meaningful test coverage, not just high percentages

πŸ“ Coding Standards

JavaScript Style Guide

General Rules:

  • Use ES6+ syntax (async/await, arrow functions, destructuring)
  • 2 spaces for indentation
  • Single quotes for strings
  • Semicolons at end of statements
  • Max line length: 100 characters

Naming Conventions:

  • camelCase for variables and functions
  • PascalCase for classes and React components
  • UPPER_SNAKE_CASE for constants
  • Prefix private methods with underscore: _privateMethod

File Naming:

  • camelCase.js for utilities and services
  • PascalCase.jsx for React components
  • kebab-case.js for routes and middleware

Code Quality

Functions:

  • Keep functions small and focused (single responsibility)
  • Use descriptive names that explain what the function does
  • Limit parameters to 3-4; use options object for more
  • Add JSDoc comments for public APIs

Error Handling:

  • Always use try-catch for async operations
  • Log errors with context using the logger utility
  • Throw meaningful error messages
  • Use custom error classes when appropriate

Comments:

  • Write self-documenting code first
  • Add comments for complex business logic
  • Explain "why" not "what"
  • Keep comments up-to-date with code changes

Example Code Style

/**
 * Processes a payment transaction and updates student balance
 * @param {string} studentId - The student's unique identifier
 * @param {Object} transaction - Stellar transaction object
 * @returns {Promise<Object>} Updated payment record
 */
const processPayment = async (studentId, transaction) => {
  try {
    const student = await Student.findOne({ studentId });
    if (!student) {
      throw new Error(`Student not found: ${studentId}`);
    }
    
    const amount = amountNormalizer.fromStroops(transaction.amount);
    const payment = await Payment.create({
      studentId,
      amount,
      transactionHash: transaction.hash,
      status: 'confirmed'
    });
    
    await student.updateBalance(amount);
    logger.info('Payment processed', { studentId, amount, hash: transaction.hash });
    
    return payment;
  } catch (error) {
    logger.error('Payment processing failed', { studentId, error });
    throw error;
  }
};

πŸ”„ Pull Request Process

1. Before Creating PR

  • Sync with latest main branch
  • All tests pass locally
  • Code follows style guidelines
  • Documentation is updated
  • No console.log or debug code remains
  • Environment variables are documented in .env.example

2. Creating the PR

Title Format:

<type>(<scope>): <description>

Examples:
feat(payments): add automated reminder system
fix(stellar): resolve transaction polling race condition
docs(api): update webhook integration guide

Description Template:

## Description
Clear description of what this PR does and why.

## Related Issue
Closes #123

## Changes Made
- Added payment reminder scheduler service
- Updated notification service with email templates
- Added reminder configuration to .env.example

## Testing Done
- [ ] Unit tests added for reminder logic
- [ ] Integration tests for email sending
- [ ] Manual testing with testnet wallet
- [ ] Verified no regression in existing features

## Screenshots
[If UI changes, add before/after screenshots]

## Breaking Changes
[List any breaking changes or migration steps needed]

## Checklist
- [ ] Tests passing
- [ ] Documentation updated
- [ ] No merge conflicts
- [ ] Follows coding standards
- [ ] Environment variables documented

3. PR Review Checklist

Your PR will be reviewed for:

  • Functionality - Does it solve the problem correctly?
  • Code Quality - Is it readable, maintainable, and well-structured?
  • Tests - Are there adequate tests with good coverage?
  • Documentation - Are changes documented appropriately?
  • Security - Are there any security concerns?
  • Performance - Does it introduce performance issues?
  • Breaking Changes - Are breaking changes clearly documented?

4. Addressing Review Feedback

  • Respond to all review comments
  • Push additional commits to address feedback
  • Mark conversations as resolved once addressed
  • Request re-review when ready

5. Merge Requirements

Before your PR can be merged:

  • At least one maintainer approval
  • All CI checks passing (when CI is configured)
  • All review comments addressed
  • No merge conflicts with main
  • Documentation complete

πŸ› Architecture Guidelines

Service Layer Principles

Stellar Service (stellarService.js)

  • All Horizon API calls go here
  • Transaction parsing and validation
  • Memo extraction and decryption
  • Use stellarRateLimitedClient.js for rate-limited calls

Payment Service (paymentService.js)

  • Payment creation and verification
  • Student balance updates
  • Payment status management

Transaction Service (transactionService.js)

  • Transaction polling and syncing
  • Retry logic for failed transactions
  • Idempotency handling

Database Best Practices

  • Use Mongoose models for all database operations
  • Implement soft deletes (see utils/softDelete.js)
  • Use transactions for multi-document updates
  • Index frequently queried fields
  • Validate data at schema level

API Design

  • Follow RESTful conventions
  • Use proper HTTP status codes
  • Return consistent response format (see utils/responseHelper.js)
  • Version APIs when making breaking changes
  • Document all endpoints in docs/api-spec.md

Error Handling

  • Use centralized error handler middleware
  • Return user-friendly error messages
  • Log detailed error context for debugging
  • Use appropriate HTTP status codes

πŸ” Code Review Guidelines

When reviewing others' PRs:

  • Be respectful and constructive
  • Ask questions to understand the approach
  • Suggest improvements, don't demand them
  • Approve when code meets standards, even if not perfect
  • Focus on correctness, security, and maintainability

πŸ“š Additional Resources


πŸ†˜ Getting Help


πŸ“„ License

By contributing to StellarEduPay, you agree that your contributions will be licensed under the MIT License.


Thank you for helping us eliminate manual reconciliation and fraud in school payments!