A modern full-stack donation and fundraising platform that connects verified charities, generous donors, and committed volunteers β with every rupee tracked from donation to impact through secure authentication, role-based dashboards, and an honest, transparent giving experience.
TrustFund is a full-stack donation and fundraising platform that rebuilds trust in online giving. Donors discover verified charities and active campaigns, give securely through the Razorpay payment gateway, download numbered PDF receipts, and follow their money all the way to impact. Charities manage their organization, verification documents, fundraising campaigns, progress updates, and volunteer opportunities from one dedicated workspace. Volunteers find and join real-world opportunities posted by genuine organizations.
The platform solves the persistent trust gap in online giving:
- Donors cannot easily tell which organizations are legitimate or where their money goes.
- Charities lack ready-made tooling for campaigns, donor communication, receipts, and volunteer coordination.
- Volunteers have no central place to discover opportunities posted by verified organizations.
- Everyone lacks visible, verifiable proof of impact β receipts, updates, and transparent financial totals.
TrustFund answers this with a documented charity verification workflow, enforced campaign lifecycles, atomic payment settlement with idempotency, automatic numbered receipts, and dedicated role-gated workspaces for donors, charities, volunteers, and admins. Built with Django, Django REST Framework, React, and PostgreSQL, it demonstrates a production-grade implementation of JWT authentication, RESTful APIs, role-based access control, payment webhooks, PDF generation, and responsive web design β all inside a thoughtfully designed interface.
| Area | Feature | What it does | Why it matters |
|---|---|---|---|
| π Authentication | Email + password sign-up / login | JWT access/refresh tokens with rotation and blacklisting | Secure, stateless, revocable sessions |
| Role-gated registration | Users register explicitly as a DONOR, CHARITY, or VOLUNTEER (ADMIN registration is blocked) | Clear identity from the very first sign-up | |
| ποΈ Charity Verification | Documented verification workflow | Organization profiles move through PENDING β VERIFIED β REJECTED with submit/review/reject/resubmit endpoints |
Charities must prove legitimacy before they can raise funds |
| Full audit trail | Every verification action is recorded in a verification_logs table |
Complete accountability for every approval decision | |
| π― Campaign Management | Validated campaign lifecycle | DRAFT β ACTIVE β COMPLETED / EXPIRED / CANCELLED with enforced transitions |
Terminal states are final β no moving the goalposts |
| Verified-only fundraising | Only owners of verified charities can publish ACTIVE campaigns | The public feed is gated behind real legitimacy | |
| Public discovery feed | Search, category filter, and sort over active campaigns | Donors find causes that match their values | |
| π³ Payments & Donations | Secure Razorpay checkout | Server-side order creation, signature verification, and webhook handling | Payment integrity guaranteed end to end |
| Atomic, idempotent settlement | raised_amount updated with F() + select_for_update inside a transaction; idempotency keys prevent double-counting |
Retries and duplicate webhooks can never inflate totals | |
| π§Ύ Receipts | Automatic numbered PDF receipts | ReportLab generates a unique TRF-YYYYMMDD-XXXXXX receipt for every successful donation |
Donors get an auditable record of each contribution |
| π Volunteers | Opportunity publishing & applications | Charities post opportunities; volunteers apply with a statement; statuses tracked per side | Real-world engagement beyond just money |
| π Notifications | Typed in-app notifications | Donation, milestone, update, volunteer, and receipt notifications with duplicate protection | Donors and charities stay informed without email spam |
| π Dashboards & Analytics | Role-specific dashboards | Donor, charity, and admin dashboards plus a public analytics endpoint | Every role sees the numbers that matter to them |
| π οΈ Admin Console | System-wide management | Admin UI for charity verifications, users, organizations, campaigns, donations, and audit logs | Trust radiation β the verification process is transparent |
All screenshots captured at 1280 Γ 800 from the running application.
| Category | Technology | Purpose |
|---|---|---|
| Frontend | React 19 | Component-based UI |
| TypeScript 5.7 | Type-safe application code | |
| Vite 6 | Build tooling & dev server | |
| React Router 7 | Client-side routing, protected routes & role gates | |
| Motion | Lightweight UI animation | |
| Backend | Django 6 | Web framework & ORM |
| Django REST Framework 3.18 | REST API layer | |
| django-environ | Environment configuration | |
| django-filter | Query filtering for list endpoints | |
| django-cors-headers | Cross-origin resource sharing | |
| Auth | django-rest-framework-simplejwt 5.5 | JWT access/refresh tokens with rotation + blacklist |
| Database | PostgreSQL (via DATABASE_URL) |
Relational database (Neon in production; SQLite out of the box for local dev) |
| Async | Celery 5 + Redis | Background notification dispatch (retrying, broker-aware) |
| Payments | Razorpay Python SDK 1.4 | Orders, checkout, signature verification, webhooks |
| Receipts | ReportLab 4.1 | Numbered PDF donation receipts |
| Media | Cloudinary | Production image storage (optional, disabled by default) |
| Static / Serving | WhiteNoise + gunicorn | Production static files & WSGI serving |
| Testing | pytest + pytest-django (backend) Β· Vitest + Testing Library (frontend) | Automated coverage of both tiers |
React Frontend (Vite + TypeScript)
β
βΌ
React Router (lazy-loaded, role-gated)
β
βΌ
Typed API Clients (fetch / JWT store)
βββ 401 interceptor β token refresh / logout
β
βΌ
Django REST Framework Backend (gunicorn)
βββ DRF permissions + CORS allow-list
β
ββββββββββββββββ¬ββββββββββΌβββββββββββ¬ββββββββββββββββ
βΌ βΌ βΌ βΌ βΌ
Auth Charities Campaigns Donations Volunteers
/auth/* /charities /* /donations/* /volunteers/*
(JWT+rotation) (verification) (lifecycle) (Razorpay) (applications)
β β β β β
β βββββββββββΌβββββββββββΌβββββββββββββββββ
β βΌ βΌ
β PostgreSQL Razorpay API
β (Neon / local) (orders, webhook)
β β β
β βΌ βΌ
ββββββββββββββββΊ Celery + Redis Cloudinary
(background jobs) (prod media)
TrustFund/
β
βββ backend/ # Django REST Framework API
β βββ config/ # settings, URL routing, health check, Celery/WSGI entrypoints
β βββ users/ # Custom User model, roles, JWT auth, permissions
β βββ charities/ # Organizations + verification workflow
β βββ campaigns/ # Campaigns, categories, lifecycle, updates
β βββ donations/ # Donations, Razorpay orders/webhooks, payment services
β βββ receipts/ # Numbered receipts + PDF generation
β βββ volunteers/ # Opportunities + applications
β βββ notifications/ # In-app notifications + Celery tasks
β βββ admin_api/ # Admin-facing endpoints (users, audit logs)
β βββ dashboard/ # Donor / charity / admin dashboards + analytics
β βββ requirements.txt
β βββ build.sh # Render build (install, collectstatic, migrate)
β βββ .env.example
β
βββ frontend/ # React + TypeScript single-page app
β βββ src/
β βββ app/ # Brand config, central route table
β βββ components/ # Design-system primitives (button, dialog, form, toast, β¦)
β βββ layouts/ # Site shell, auth layout, app shell, admin shell
β βββ pages/ # home, auth, campaigns, donor, charity, volunteer, admin, public
β β βββ donor/ # Donation history, details, notifications
β β βββ charity/ # Dashboard, organization, campaigns, updates, volunteers
β β βββ volunteer/ # Dashboard, opportunities, applications
β β βββ admin/ # Dashboard, verifications, users, organizations, campaigns, donations, audit logs
β β βββ campaigns/ # Discovery, detail, donate, donation success
β βββ services/ # Typed API clients (auth, campaigns, donations, receipts, admin, β¦)
β βββ context/ # Auth context
β βββ hooks/ # Reusable UI hooks
β βββ styles/ # Design tokens + base/utilities
β
βββ screenshots/ # README screenshots (1280 Γ 800 page captures)
βββ README.md
βββ LICENSE
βββ .gitignore
- Python 3.12+ and pip
- Node.js 18+ and npm
- (Optional) PostgreSQL and Redis β not required to get started; SQLite and eager Celery work out of the box.
git clone https://github.com/Aby020/TrustFund.git
cd TrustFundcd backend
# Create and activate a virtual environment
python -m venv .venv
# Windows (PowerShell): .venv\Scripts\Activate.ps1
# macOS / Linux: source .venv/bin/activate
pip install -r requirements.txtInside backend, copy the example file:
cd backend
# Windows: copy .env.example .env
# macOS/Linux: cp .env.example .envThe safe defaults (SQLite, eager Celery, no Cloudinary) work without editing. For a full breakdown see Environment Variables.
DEBUG=True
DATABASE_URL=sqlite:///db.sqlite3
REDIS_URL=redis://localhost:6379/0cd backend
python manage.py migrateOptionally seed verified charities and their campaigns:
python manage.py seed_dev_datacd backend
python manage.py runserverBackend runs on:
http://localhost:8000
The API is served under http://localhost:8000/api/v1/ with a health check at GET /healthz/.
Open a new terminal.
cd frontend
npm install
# (Optional) copy the frontend env example; defaults target localhost:8000
# Windows: copy .env.example .env
# macOS/Linux: cp .env.example .envcd frontend
npm run devFrontend runs on:
http://localhost:3000
All secrets are environment variables β never committed. The repository ships safe .env.example files with placeholders; .env itself is git-ignored.
| Variable | Required | Purpose | Dev default |
|---|---|---|---|
DEBUG |
Yes | Django debug mode | True |
SECRET_KEY |
Yes | Django signing key β generate a strong random value for production | placeholder |
ALLOWED_HOSTS |
Yes | Comma-separated allowed hostnames | localhost,127.0.0.1 |
DATABASE_URL |
Yes | PostgreSQL connection string (Neon: append ?sslmode=require) |
sqlite:///db.sqlite3 |
REDIS_URL |
Yes | Redis URL (Celery broker) | redis://localhost:6379/0 |
CELERY_BROKER_URL |
Yes | Celery broker URL | redis://localhost:6379/1 |
CELERY_RESULT_BACKEND |
Yes | Celery result backend URL | redis://localhost:6379/2 |
CELERY_TASK_ALWAYS_EAGER |
No | Run Celery tasks synchronously in dev | True |
CORS_ALLOWED_ORIGINS |
Yes | Frontend origins to allow | http://localhost:3000,... |
CSRF_TRUSTED_ORIGINS |
Yes | Trusted CSRF origins (must match CORS) | http://localhost:3000,... |
EMAIL_URL |
Yes | Email backend (console:// in dev, SMTP in prod) |
console:// |
RAZORPAY_KEY_ID |
Yes | Razorpay key ID (test key in development) | placeholder |
RAZORPAY_KEY_SECRET |
Yes | Razorpay key secret (test secret in development) | placeholder |
RAZORPAY_WEBHOOK_SECRET |
Yes | Razorpay webhook signing secret | placeholder |
CLOUDINARY_STORAGE_ENABLED |
No | Enable Cloudinary for production media | False |
CLOUDINARY_CLOUD_NAME / API_KEY / API_SECRET |
No* | Cloudinary credentials (*required only if enabled) | empty |
SECURE_SSL_REDIRECT |
No | Redirect HTTP to HTTPS (set True in production) |
False |
SESSION_COOKIE_SECURE |
No | Only send session cookies over HTTPS | False |
CSRF_COOKIE_SECURE |
No | Only send CSRF cookies over HTTPS | False |
| Variable | Purpose | Dev default |
|---|---|---|
VITE_API_BASE_URL |
Base URL of the Django API | http://localhost:8000 |
VITE_RAZORPAY_KEY_ID |
Razorpay public key ID used by checkout | rzp_test_xxxxxx |
VITE_RAZORPAY_KEY_ID is a public key and is safe to include in the Vite build output.
- Start the backend:
cd backend && python manage.py runserverβhttp://localhost:8000 - (Optional) Start a Celery worker:
cd backend && celery -A config worker -l info - Start the frontend:
cd frontend && npm run devβhttp://localhost:3000 - Open
http://localhost:3000in your browser.
Log in as a DONOR, CHARITY, or VOLUNTEER through the registration flow to explore the role-specific workspaces. Donations in development use Razorpay test keys β never live credentials outside a protected production environment.
β οΈ Development only.python manage.py seed_dev_datapopulates a local database with realistic demo data using a single test-only placeholder password. It is not real β use it only against your local SQLite/Postgres, never a production database.
| What | Count | Notes |
|---|---|---|
| Verified charity organizations | 8 | Covering water, education, health, food, relief, animals, poverty, and other causes |
| Active campaigns | 11 | Verified charities' campaigns start ACTIVE with goals, locations, and cover images |
| Demo password | 1 | Shared TEST_ONLY_PASSWORD placeholder for every seeded charity account |
There is no default admin credential β the public registration API deliberately blocks ADMIN sign-up (Cannot register as admin. Contact system administrator.). Admin accounts are provisioned by an authorized operator, not through self-registration. If you need admin access locally, create one via the Django shell β never commit the password.
| Capability | π€ Donor | π₯ Charity | π Volunteer | π‘οΈ Admin |
|---|---|---|---|---|
| Browse public campaigns & charities | β | β | β | β |
| Register / log in | β | β | β | β (provisioned) |
| Donate through Razorpay checkout | β | β | β | β |
| Download numbered PDF receipts | β | β | β | β |
| Manage their organization & verification | β | β | β | Review |
| Create & manage campaigns / updates | β | β | β | View |
| Post & review volunteer opportunities | β | β | β | View |
| Apply to volunteer opportunities | β | β | β | β |
| System-wide dashboard & audit logs | β | β | β | β |
Routes are protected by both ProtectedRoute (JWT) and RequireRole (role check) guards β a donor token cannot reach charity or admin endpoints, and vice versa.
Charity creates organization profile ββΊ status = PENDING
β
Admin reviews documents β
βββ Approve ββββββββΊ VERIFIED ββΊ can raise funds
βββ Reject βββββββββΊ REJECTED (reason required)
β
Charity fixes & resubmits ββΊ PENDING
Every action (submit / approve / reject / resubmit) is written to the verification_logs audit trail.
Verified charity creates campaign βββΊ ACTIVE (public feed)
β
Goal reached / deadline passed
β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββ
βΌ βΌ βΌ
COMPLETED EXPIRED CANCELLED
raised_amount is owned by the donations domain β it is never writable through campaign endpoints.
| Step | Action | Result |
|---|---|---|
| 1 | Donor opens a campaign, chooses an amount | Backend validates the campaign is ACTIVE, creates a Razorpay order, stores a PENDING donation with a unique idempotency key |
| 2 | Frontend launches Razorpay Checkout | SDK loaded asynchronously; public key from VITE_RAZORPAY_KEY_ID |
| 3 | Donor completes payment | Client posts payment_id + signature |
| 4 | Signature verified | Donation β SUCCESS; campaign raised_amount incremented atomically |
| 5 | Webhook arrives (payment.captured / order.paid) |
Signature-verified and idempotent β a duplicate simply no-ops |
| 6 | Receipt generated & donor notified | Numbered PDF + in-app notification |
Charity publishes opportunity βββΊ Volunteer applies with a statement
β
Charities review applications
ββββββββββββββββββββΌβββββββββββββββββββ
βΌ βΌ βΌ
APPROVED REJECTED ATTENDED
A volunteer can apply to each opportunity only once. Both sides receive notifications on status changes.
All routes are served under /api/v1/ and return JSON. Mutating routes validate payloads and enforce object-level authorization. Protected endpoints require a Authorization: Bearer <access-token> header.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/auth/register/ |
Register as DONOR, CHARITY, or VOLUNTEER | β |
POST |
/auth/login/ |
Sign in, returns access + refresh JWT pair | β |
POST |
/auth/refresh/ |
Rotate the access token | Refresh token |
POST |
/auth/logout/ |
Revoke the session (blacklist) | β |
GET |
/auth/me/ |
Current user profile & role | β |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/charities/ |
Public list of verified charities | β |
GET |
/charities/<id>/ |
Charity details | β |
POST |
/charities/create/ |
Create an organization profile | CHARITY |
GET |
/charities/me/ |
My organization | CHARITY |
POST |
/charities/<id>/submit/ |
Submit for verification | CHARITY |
POST |
/charities/<id>/approve/ |
Approve verification | Admin |
POST |
/charities/<id>/reject/ |
Reject verification (reason required) | Admin |
POST |
/charities/<id>/resubmit/ |
Resubmit after rejection | CHARITY |
GET |
/charities/<id>/history/ |
Verification audit trail | Admin / Owner |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/campaigns/ |
Discovery feed (search, category, sort) | β |
GET |
/campaigns/<id>/ |
Campaign detail | β |
POST |
/campaigns/ |
Create a campaign (verified charity) | CHARITY |
PATCH |
/campaigns/<id>/ |
Update own campaign | CHARITY |
GET / POST |
/campaigns/updates/ |
Global campaign updates | β / Auth |
GET / POST |
/campaigns/<id>/updates/ |
Updates for one campaign | β / CHARITY |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/donations/ |
Initialize a donation (creates Razorpay order) | β |
GET |
/donations/ |
My donation history | β |
POST |
/donations/<id>/verify_payment/ |
Verify signature & settle donation | β |
POST |
/donations/webhook/ |
Razorpay webhook (idempotent) | Signature |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/receipts/ |
My receipts | β |
GET |
/receipts/<id>/ |
Receipt detail | β |
GET |
/receipts/<id>/download_pdf/ |
Download the numbered PDF receipt | β |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/volunteers/opportunities/ |
Open opportunities | β |
POST |
/volunteers/opportunities/ |
Publish an opportunity | CHARITY |
GET / POST |
/volunteers/applications/ |
List / create applications | β / VOLUNTEER |
PATCH |
/volunteers/applications/<id>/ |
Update application status | CHARITY |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/dashboard/donor/ |
Donor impact & lifetime giving | DONOR |
GET |
/dashboard/charity/ |
Charity funds raised across campaigns | CHARITY |
GET |
/dashboard/admin/ |
System-wide metrics | Admin |
GET |
/dashboard/analytics/ |
Public analytics (donations by category, success rates) | β |
GET |
/admin/users/ |
Admin user directory | Admin |
GET |
/admin/audit-logs/ |
Admin audit log | Admin |
GET |
/notifications/ |
My notifications | β |
Example β login and use a protected endpoint:
# 1. Obtain tokens
curl -X POST https://trustfund-backend-jexv.onrender.com/api/v1/auth/login/ \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"your-password"}'
# 2. Call a protected endpoint with the access token (exported as $TOKEN)
curl https://trustfund-backend-jexv.onrender.com/api/v1/auth/me/ \
-H "Authorization: Bearer $TOKEN"The schema is organized into per-domain Django apps, all reconciled through python manage.py migrate. PostgreSQL is used in production (DATABASE_URL); SQLite works out of the box locally.
| App | Core models | Purpose |
|---|---|---|
| users | User |
Custom user with email login and a mandatory role field (DONOR | CHARITY | VOLUNTEER | ADMIN) |
| charities | CharityOrganization, VerificationLog |
Organization profiles + the PENDING β VERIFIED / REJECTED workflow with full audit history |
| campaigns | Campaign, CampaignCategory, CampaignUpdate |
Fundraising campaigns with enforced lifecycle, goals, locations, and progress updates |
| donations | Donation |
Payments with status, idempotency key, Razorpay references, and atomic settlement |
| receipts | Receipt |
Numbered receipts (TRF-YYYYMMDD-XXXXXX) linked to successful donations |
| volunteers | VolunteerOpportunity, VolunteerApplication |
Opportunities + applications with a per-opportunity uniqueness rule |
| notifications | Notification |
Typed in-app notifications with duplicate protection |
| dashboard / admin_api | β (query/aggregation only) | Role analytics and admin-facing reads |
Key integrity rules enforced at the database layer:
Campaign.goal_amount > 0 (check constraint)
Campaign.raised_amount not user-writable (owned by donations domain)
VolunteerApplication unique (volunteer, opportunity)
Donation.idempotency_key unique (webhook/retry safety)
Charity verification only admins approve/reject (a charity cannot verify itself)
| Feature | Implementation |
|---|---|
| JWT authentication | Access/refresh token pair from SimpleJWT with rotation and blacklisting |
| Deny-by-default authorization | DRF default is IsAuthenticated; public read endpoints opt in explicitly |
| Role-based access control | Frontend RequireRole guards + backend role/permission checks on every endpoint |
| Object-level authorization | Charities manage only their own org/campaigns; verification approve/reject is admin-only |
| Password hashing | Django password hashers β nothing stored in plaintext |
| Payment integrity | Server-side signature verification for checkout and webhooks; idempotency keys; F() + select_for_update atomic raised_amount updates |
| Secure secrets | SECRET_KEY, DATABASE_URL, Razorpay, and Cloudinary credentials read from environment only |
| Secure cookies in production | SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE, SECURE_SSL_REDIRECT, and SECURE_PROXY_SSL_HEADER enabled behind the Render TLS proxy |
| No secrets in code | .env is git-ignored; only placeholder .env.example files are tracked |
- π€ ML / Intelligence Layer β donation-trend analytics, smart campaign recommendations, anomaly detection, and fraud signals (planned, not yet implemented)
- π³ Recurring donations β scheduled giving and one-tap repeat donations
- π§Ύ Region-specific receipts β India / U.K. tax-receipt formatting
- π Richer campaign discovery β location, impact tags, and advanced filters
- π Verified impact reporting β per-campaign impact stories and public transparency pages
- π Email notifications β donation confirmations, milestones, and volunteer updates
- π³ Docker deployment β containerized backend, frontend, worker, and database
- π API documentation β OpenAPI / Swagger specification
- β Penetration testing β a formal security assessment before public launch
- Trust by design β charities must pass a documented verification workflow before they can raise funds; every decision is audited
- Money that can't be faked β
raised_amountis owned by the donations domain and settled atomically with idempotency against webhook replays - Paper trail for every gift β automatic numbered PDF receipts give donors an auditable record
- Four role-gated experiences β dedicated workspaces for donors, charities, volunteers, and admins with strict access separation
- Full-stack, production-shaped β Django 6 + DRF on the backend, React 19 + TypeScript on the frontend, deployed to Render with Neon PostgreSQL
- Tested on both tiers β pytest + pytest-django suites for the backend and Vitest + Testing Library for the frontend, plus strict
tsctypechecking in the build - Honest roadmap β planned features are clearly marked as planned, never presented as shipped
This project is licensed under the MIT License.
See the LICENSE file for more information.
Backend Developer | Python, Django & Node.js Developer
Passionate about building scalable backend systems, RESTful APIs, modern web applications, and production-ready software using Python, Django, Node.js, Express.js, PostgreSQL, and React.
If you found this project helpful, please consider giving it a β on GitHub.
Your support motivates me to continue building and improving high-quality open-source software.
If you have suggestions, feedback, or would like to collaborate, feel free to connect with me on GitHub or LinkedIn.









