Edge360
Edge360 is lite CRM software. It is a tool to use by businesses to find sales leads and to keep track of their sales prospects.

- PERN (React js
- Node js
- Express js and Postgres)













How I framed it.
Edge360 is a multi-tenant CRM and sales-network platform built for B2B service organizations (consulting, finance, agencies) that consolidates 4–6 disconnected tools (spreadsheets, email, Slack, separate CRM/billing/support) into a single workspace. The platform pairs a React 18 SPA with a Node.js/Express + PostgreSQL backend, layering Socket.IO realtime chat, M-Pesa STK Push billing for the East African market, a public tender marketplace, and a sales-agent reseller channel on top of a configurable lead pipeline. Multi-tenancy is enforced at the application layer through organization_id scoping, keeping infrastructure simple while serving every tenant from a single Postgres database.
What it does.
- Configurable lead pipeline with custom milestone stages, per-prospect notes with happiness ratings, scheduled tasks, drop/won outcomes, and full transfer history between owners
- Separate Relationships module for long-horizon contacts (referrers, partners, ex-clients) with social-media JSON and qualitative narrative fields like what_i_know and why_they_important
- Public Notice Board / Tender marketplace where users post and browse opportunities with attachments, finder fees, commission terms, and admin moderation
- Real-time per-lead and per-relationship chat threads plus direct messages between team members, delivered live over Socket.IO
- M-Pesa STK Push subscription billing for the Kenyan / East African market alongside the standard credit-card flow
- Sales-agent reseller channel with subscription attribution, commission tracking, and dedicated agent pricing tiers
Inside the build.
Multi-Tenant Application Isolation
Every prospect, relationship, message, target, and support ticket is scoped by organization_id. A single Postgres database serves every tenant, with isolation enforced through JWT-resolved user filters at the controller layer — inexpensive infrastructure with strict logical separation, no per-tenant schemas or row-level security required.
Realtime Chat with Room Targeting
Socket.IO is mounted on the same Express HTTP server. addUser registers a (userId, socketId, prospectId | relationshipId) tuple. sendMessage dispatches either to all users in the matching prospect/relationship room minus the sender (broadcast) or to a specific receiver_id (DM). Presence is held in-memory — fine for single-instance, would need a Redis adapter for horizontal scaling.
M-Pesa STK Push Pipeline
Server fetches a Daraja OAuth token, builds the base64 STK password (Shortcode + Passkey + Timestamp), POSTs to mpesa/stkpush/v1/processrequest, and inserts an mpesa_transactions row in 'Waiting User Action' state. Daraja later invokes the callback URL — the server flips the user to premium, updates users_subscriptions, and persists the raw callback JSON to disk for replay. The client polls a status endpoint until terminal state.
Encrypted-Transit Authentication
Clients AES-encrypt the password before transit using a shared CRYPTO_JS_KEY; the server decrypts then bcrypt-hashes for storage. JWTs are issued in the auth-token header, stored in localStorage, and validated by the fetchuser middleware. A per-user user_logged_in flag enforces a single active session (admin exempt) to prevent silent account sharing.
Per-Tenant Pipeline Configuration
Each organization stores its own milestones[] (default 5 stages, editable), happiness_rating_choices[] (5 emotional descriptors), lead sources, services, industries, and currency as Postgres text/jsonb arrays. Tenants customize the entire CRM vocabulary without schema migrations or backend deployments.
Under the hood.
- Multi-tenant isolation enforced at the controller layer via organization_id scoping — single Postgres database serves every tenant without per-tenant schemas or row-level security
- Stateless JWT auth with per-user single-active-session lock (user_logged_in flag) preventing parallel logins from a second browser
- AES-encrypted passwords in transit (CRYPTO_JS_KEY) layered on top of TLS, bcrypt-hashed at rest — protects against plaintext leaks in client-side console logs
- Socket.IO server attached to the same Express HTTP server, keyed off (userId, prospectId | relationshipId) tuples for room-scoped chat
- M-Pesa Daraja integration: OAuth token → STK push → callback persistence → client polling, with raw callback JSON written to mpesa_callbacks/ for forensic replay
- Role-based menu rendering (admin / employee / agent + super-admin gate by email) driving the entire dashboard layout from a single CRM container
The toolkit.
Lead Pipeline
Configurable milestones, notes with happiness rating, scheduled tasks, drop/won outcomes, and full owner-transfer history
Relationships
Parallel module for long-horizon contacts with social JSON and qualitative narrative fields (what_i_know, why_they_important, what_they_value)
Notice Board
Public tender / opportunity marketplace with attachments, finder fees, commission terms, and admin authorization gate
Realtime Chat
Socket.IO per-lead and per-relationship threads plus DMs with in-memory presence tracking
M-Pesa Billing
Daraja STK Push integration for the East African market with raw callback JSON persisted to disk for forensic replay
Agent Channel
Reseller users with subscription attribution via agent_id FK and dedicated agent-tier pricing
What I learned.
- Layered application-level multi-tenant isolation (organization_id scoping) over a single Postgres database to ship a working multi-tenant CRM without per-tenant schemas or row-level security overhead
- Modeled prospects and relationships as two distinct entities because most CRMs incorrectly conflate active sales opportunities with long-horizon network contacts
- Implemented M-Pesa STK Push end-to-end (OAuth → push → callback → client polling) to serve a market underserved by Stripe-centric SaaS billing
- Persisted every Daraja callback as raw JSON to disk before processing — invaluable for debugging payment edge cases and reconciling disputed transactions
- Layered AES-in-transit on top of TLS for password fields specifically to prevent plaintext leaks via client-side console logs and shoulder-surfed devtools
- Made the pipeline vocabulary (milestones, happiness ratings, lead sources, services, currency) editable per organization via Postgres text/jsonb arrays — tenant customization without schema migrations