A Node.js backend that powers MonieEstate, an imaginary housing estate that generates its own power and allows residents to purchase electricity plans offline via Monnify agents across Nigeria.
Resident walks to Moniepoint Agent
│
▼
Agent opens POS / Web
│
▼
Enters: HouseNumber:PlanCode
e.g. "ME001A:HOME"
│
▼
[Monnify] ──POST──▶ /monnify/verify-payer ──▶ [MonieEstate Backend]
│
Local JSON DB
(Residents, Plans)
│
Returns resident name + plan amount
│
▼
Agent collects cash, confirms amount
│
▼
[Monnify] ──POST──▶ /monnify/payment-request ──▶ [MonieEstate Backend]
│
Generates power token
Credits resident units
Logs to local DB
│
Returns power token
│
▼
Agent prints receipt with power token
Resident enters token on estate meter
Data is stored in data/db.json — a plain JSON file created automatically on first run. It contains three collections:
| Field | Type | Description |
|---|---|---|
| houseNumber | String | Alphanumeric ID, e.g. ME001A, A12B |
| fullName | String | Resident's full name |
| phone | String | Contact phone number |
| String | Email address | |
| status | String | ACTIVE or SUSPENDED |
| currentUnits | Number | Current power unit balance |
| createdAt | String | ISO timestamp |
| Field | Type | Description |
|---|---|---|
| planCode | String | e.g. STARTER, HOME, PREMIUM, BUSINESS |
| planName | String | Human-readable plan name |
| amount | Number | Price in Naira |
| units | Number | Power units included |
| description | String | Plan description |
| status | String | ACTIVE or INACTIVE |
| Field | Type | Description |
|---|---|---|
| transactionRef | String | Monnify transaction reference |
| houseNumber | String | Resident's house number |
| planCode | String | Plan code purchased |
| amount | Number | Amount paid in Naira |
| unitsPurchased | Number | Power units purchased |
| powerToken | String | Generated token, e.g. 1234-5678-9012-3456 |
| status | String | PENDING, SUCCESS, or FAILED |
| createdAt | String | ISO timestamp |
| updatedAt | String | ISO timestamp |
Called by Monnify to verify customer at agent
Request (from Monnify):
{
"productCode": "P10101",
"paymentRecipientId": "ME001A:HOME"
}Format:
HOUSE_NUMBER:PLAN_CODE
The colon separator lets the agent capture both house number and desired plan in a single ID field.
Success Response:
{
"responseCode": "00",
"responseMessage": "User verified successfully.",
"paymentRecipientId": "ME001A:HOME",
"paymentRecipientDescription": "Adaeze Okonkwo | Home Plan (150 units) | Current Balance: 120 units",
"amount": 5000
}User Not Found:
{
"responseCode": "02",
"responseMessage": "House number ME999X does not exist in MonieEstate."
}Called by Monnify after successful cash collection
Request (from Monnify):
{
"amount": 5000,
"transactionReference": "MNFY|66|20210825115615|000002",
"productCode": "P10101",
"paymentRecipientId": "ME001A:HOME"
}Success Response:
{
"responseCode": "00",
"productCode": "HOME",
"paymentRecipientId": "ME001A:HOME",
"transactionReference": "MNFY|66|20210825115615|000002",
"paymentToken": "1234-5678-9012-3456"
}This token is printed on the agent receipt and entered by the resident on their estate meter.
Called by Monnify to check status of a payment
Request:
GET /monnify/payment-requery?transactionReference=MNFY%7C66%7C20210825115615%7C000002
Success Response:
{
"responseCode": "00",
"productCode": "HOME",
"paymentRecipientId": "ME001A:HOME",
"transactionReference": "MNFY|66|20210825115615|000002",
"paymentToken": "1234-5678-9012-3456"
}git clone <your-repo>
cd monie-estate
npm installcp .env.example .envEdit .env:
PORT=3000
MONNIFY_PRODUCT_CODE=your_product_code
# Optional: override DB file location (default: ./data/db.json)
# DB_PATH=./data/db.jsonnpm start
# or for development:
npm run devOn first run the server will:
- Create
data/db.jsonwith seed residents and plans - Start listening on port 3000
Monnify needs to call your endpoints from the internet:
# Install ngrok from https://ngrok.com
ngrok http 3000Copy the HTTPS URL (e.g. https://abc123.ngrok.io)
- Log into Monnify Dashboard
- Go to Developer Settings → Offline Payment Setup
- Set your endpoints:
- Payer Verification URL:
https://abc123.ngrok.io/monnify/verify-payer - Payment Request URL:
https://abc123.ngrok.io/monnify/payment-request - Payment Requery URL:
https://abc123.ngrok.io/monnify/payment-requery
- Payer Verification URL:
- Create an Offline Product:
- Product Type: Merchant Invoice (allows dynamic amounts per plan)
- This gives you a
productCodeto put in your.env
curl -X POST http://localhost:3000/monnify/verify-payer \
-H "Content-Type: application/json" \
-d '{"productCode": "P10101", "paymentRecipientId": "ME001A:HOME"}'curl -X POST http://localhost:3000/monnify/payment-request \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"transactionReference": "MNFY|66|20241201120000|000001",
"productCode": "P10101",
"paymentRecipientId": "ME001A:HOME"
}'curl "http://localhost:3000/monnify/payment-requery?transactionReference=MNFY%7C66%7C20241201120000%7C000001"curl http://localhost:3000/api/residents/ME001Acurl http://localhost:3000/api/plans| House Number | Name | Status | Initial Units |
|---|---|---|---|
| ME001A | Adaeze Okonkwo | ACTIVE | 120 |
| ME002B | Babatunde Lawal | ACTIVE | 45 |
| ME003C | Chioma Eze | ACTIVE | 0 |
| ME004D | Danladi Musa | SUSPENDED | 200 |
| ME005E | Emeka Obiora | ACTIVE | 75 |
| Code | Name | Price | Units |
|---|---|---|---|
| STARTER | Starter Plan | ₦2,000 | 50 units |
| HOME | Home Plan | ₦5,000 | 150 units |
| PREMIUM | Premium Plan | ₦10,000 | 350 units |
| BUSINESS | Business Plan | ₦25,000 | 1,000 units |
When a resident pays at a Moniepoint agent:
- Agent enters
ME001A:HOMEon the POS - Monnify calls
/monnify/verify-payer→ backend confirms resident + returns ₦5,000 - Resident pays ₦5,000 cash
- Monnify calls
/monnify/payment-request - Backend generates token:
3847-2910-5634-8821 - Token is printed on agent receipt
- Resident enters token on their estate meter → 150 units loaded
- Resident's balance updated in
data/db.json: 120 + 150 = 270 units
monie-estate/
├── src/
│ ├── index.js # Express app entry point
│ ├── routes/
│ │ ├── monnify.js # Monnify offline pay-in endpoints
│ │ └── api.js # Internal management API
│ ├── services/
│ │ └── db.js # Local JSON file DB layer
│ └── utils/
│ └── tokenGenerator.js # Power token generation
├── data/
│ └── db.json # Local database (auto-created, gitignored)
├── .env # Environment variables (gitignored)
├── .env.example # Environment template
├── .gitignore
└── package.json