Building **PlacePal**, your POS agent registration, verification, location-
based service request, and real-time tracking app, using Visual Studio Code
(VS Code) is a cost-effective approach to avoid hiring developers. Since
you’re aiming to develop both the front-end and back-end yourself, I’ll guide
you step-by-step through the process, tailoring it to a beginner-friendly level
while ensuring the app is functional for Nigeria’s POS ecosystem. This guide
assumes you have basic computer skills but little to no coding experience.
We’ll use **React Native** for the front-end (mobile app for iOS/Android),
**Node.js with Express** for the back-end, **MongoDB** for the database,
and **Socket.io** for real-time tracking, as these are cost-effective and
widely supported tools.
The steps below include setting up your development environment, writing
the front-end and back-end code, integrating APIs (e.g., for KYC and
payments), and testing the app. I’ll provide simplified code snippets,
explanations, and commands to run in VS Code’s terminal. This approach
minimizes costs by using free/open-source tools and leveraging Nigeria-
specific APIs like Flutterwave and YouVerify.
---
### **Step-by-Step Guide to Build PlacePal in Visual Studio Code**
#### **Step 1: Set Up Your Development Environment**
1. **Install Visual Studio Code**:
- Download and install VS Code from
[code.visualstudio.com](https://code.visualstudio.com) (free).
- Open VS Code and install the following extensions (via the Extensions
panel, Ctrl+Shift+X):
- **ESLint**: For JavaScript code quality.
- **Prettier**: For code formatting.
- **React Native Tools**: For React Native debugging.
- **MongoDB for VS Code**: For database management.
2. **Install Node.js**:
- Download and install Node.js (LTS version) from
[nodejs.org](https://nodejs.org) (includes npm, the package manager).
- Verify installation in VS Code’s terminal (Ctrl+` to open):
```bash
node -v
npm -v
```
Expect versions like `v20.x.x` and `v10.x.x`.
3. **Install MongoDB**:
- Download MongoDB Community Server from
[mongodb.com](https://www.mongodb.com/try/download/community) or use
**MongoDB Atlas** (free cloud database).
- For local setup: Install and run MongoDB as a service.
- For Atlas: Sign up, create a free cluster, and note the connection string
(e.g.,
`mongodb+srv://<username>:<password>@cluster0.mongodb.net/placepal
`).
- Verify MongoDB is running:
```bash
mongod --version
```
4. **Install Expo CLI** (for React Native):
- In VS Code’s terminal:
```bash
npm install -g expo-cli
```
- Verify:
```bash
expo --version
```
5. **Set Up Android/iOS Emulator**:
- For Android:
- Install **Android Studio** from
[developer.android.com](https://developer.android.com/studio).
- Set up an Android emulator (e.g., Pixel 6) via Android Studio’s Device
Manager.
- For iOS (Mac only):
- Install **Xcode** from the Mac App Store and set up an iOS simulator.
- Alternatively, use Expo Go (a mobile app) to test on your physical phone.
6. **Create Project Folder**:
- In VS Code, click “File” > “Open Folder” and create a new folder named
`PlacePal`.
- Open the terminal in VS Code (Ctrl+`) and navigate to the folder:
```bash
cd ~/path/to/PlacePal
```
---
#### **Step 2: Build the Front-End (React Native)**
We’ll create a mobile app with a registration form, map-based dashboard,
and service request modal.
1. **Initialize React Native Project**:
- In the VS Code terminal:
```bash
npx create-expo-app placepal-frontend
cd placepal-frontend
```
- Install dependencies for maps, API calls, and notifications:
```bash
npm install react-native-maps axios react-native-push-notification @react-
navigation/native @react-navigation/stack
```
2. **Create Front-End Structure**:
- In the `placepal-frontend` folder, create the following files in VS Code:
- `App.js`: Main app entry point.
- `screens/RegisterScreen.js`: Agent registration form.
- `screens/DashboardScreen.js`: Customer dashboard with map.
- `screens/RequestScreen.js`: Service request modal.
3. **Code the Front-End**:
- **App.js** (Navigation setup):
```javascript
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import RegisterScreen from './screens/RegisterScreen';
import DashboardScreen from './screens/DashboardScreen';
import RequestScreen from './screens/RequestScreen';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Register" component={RegisterScreen}
options={{ title: 'PlacePal - Agent Register' }} />
<Stack.Screen name="Dashboard" component={DashboardScreen}
options={{ title: 'PlacePal - Find Agents' }} />
<Stack.Screen name="Request" component={RequestScreen}
options={{ title: 'PlacePal - Request Service' }} />
</Stack.Navigator>
</NavigationContainer>
);
```
- **screens/RegisterScreen.js** (Agent registration form):
```javascript
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, Alert } from 'react-
native';
import axios from 'axios';
export default function RegisterScreen({ navigation }) {
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [bvn, setBvn] = useState('');
const [address, setAddress] = useState('');
const handleRegister = async () => {
try {
const response = await axios.post('http://localhost:3000/api/register',
{ name, phone, bvn, address });
Alert.alert('Success', response.data.message);
navigation.navigate('Dashboard');
} catch (error) {
Alert.alert('Error', error.response?.data?.error || 'Registration failed');
};
return (
<View style={styles.container}>
<Text style={styles.title}>Agent Registration</Text>
<TextInput style={styles.input} placeholder="Full Name"
value={name} onChangeText={setName} />
<TextInput style={styles.input} placeholder="Phone Number"
value={phone} onChangeText={setPhone} keyboardType="phone-pad" />
<TextInput style={styles.input} placeholder="BVN" value={bvn}
onChangeText={setBvn} keyboardType="numeric" />
<TextInput style={styles.input} placeholder="Business Address"
value={address} onChangeText={setAddress} />
<Button title="Register" onPress={handleRegister} color="#1a73e8"
/>
<Button title="Go to Dashboard" onPress={() =>
navigation.navigate('Dashboard')} />
</View>
);
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, backgroundColor: '#f4f4f4' },
title: { fontSize: 24, fontWeight: 'bold', color: '#1a73e8', marginBottom:
20 },
input: { borderWidth: 1, borderColor: '#ccc', padding: 10, marginBottom:
10, borderRadius: 5 },
});
```
- **screens/DashboardScreen.js** (Map-based dashboard):
```javascript
import React, { useEffect, useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import axios from 'axios';
export default function DashboardScreen({ navigation }) {
const [agents, setAgents] = useState([]);
useEffect(() => {
// Fetch nearby agents (mock for now)
const mockAgents = [
{ id: 1, name: 'Agent John', latitude: 6.5244, longitude: 3.3792,
services: ['Withdrawal', 'Transfer'] },
{ id: 2, name: 'Agent Sarah', latitude: 6.5344, longitude: 3.3892,
services: ['Withdrawal', 'Bill Payment'] },
];
setAgents(mockAgents);
// Replace with real API call later
// axios.get('http://localhost:3000/api/agents/nearby?
lat=6.5244&lng=3.3792').then(res => setAgents(res.data));
}, []);
return (
<View style={styles.container}>
<Text style={styles.title}>Find Nearby POS Agents</Text>
<MapView
style={styles.map}
initialRegion={{
latitude: 6.5244, // Lagos
longitude: 3.3792,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
{agents.map(agent => (
<Marker
key={agent.id}
coordinate={{ latitude: agent.latitude, longitude:
agent.longitude }}
title={agent.name}
description={`Services: ${agent.services.join(', ')}`}
onPress={() => navigation.navigate('Request', { agentId:
agent.id })}
/>
))}
</MapView>
<Button title="Request Service" onPress={() =>
navigation.navigate('Request')} color="#1a73e8" />
</View>
);
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, backgroundColor: '#f4f4f4' },
title: { fontSize: 24, fontWeight: 'bold', color: '#1a73e8', marginBottom:
20 },
map: { flex: 1, borderRadius: 5 },
});
```
- **screens/RequestScreen.js** (Service request modal):
```javascript
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, Alert } from 'react-
native';
import axios from 'axios';
export default function RequestScreen({ route }) {
const { agentId } = route.params || {};
const [serviceType, setServiceType] = useState('withdrawal');
const [amount, setAmount] = useState('');
const handleRequest = async () => {
try {
const response = await axios.post('http://localhost:3000/api/request',
{ agentId, serviceType, amount });
Alert.alert('Success', response.data.message);
} catch (error) {
Alert.alert('Error', error.response?.data?.error || 'Request failed');
};
return (
<View style={styles.container}>
<Text style={styles.title}>Request Service</Text>
<Text style={styles.label}>Service Type</Text>
<TextInput
style={styles.input}
value={serviceType}
onChangeText={setServiceType}
placeholder="e.g., withdrawal, transfer, bill"
/>
<Text style={styles.label}>Amount (₦)</Text>
<TextInput
style={styles.input}
value={amount}
onChangeText={setAmount}
keyboardType="numeric"
placeholder="Enter amount"
/>
<Button title="Submit Request" onPress={handleRequest}
color="#1a73e8" />
</View>
);
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, backgroundColor: '#f4f4f4' },
title: { fontSize: 24, fontWeight: 'bold', color: '#1a73e8', marginBottom:
20 },
label: { fontSize: 16, marginBottom: 5 },
input: { borderWidth: 1, borderColor: '#ccc', padding: 10, marginBottom:
10, borderRadius: 5 },
});
```
4. **Test the Front-End**:
- In the `placepal-frontend` folder, run:
```bash
npx expo start
```
- Scan the QR code with the **Expo Go** app on your phone or open the
Android/iOS emulator.
- You’ll see a basic app with:
- A registration screen (mock submission).
- A dashboard with a map showing two mock agents in Lagos.
- A request screen for selecting services and amounts.
---
#### **Step 3: Build the Back-End (Node.js + Express)**
The back-end handles agent registration, verification, nearby agent
searches, and service requests.
1. **Initialize Node.js Project**:
- In the `PlacePal` folder, create a new folder for the back-end:
```bash
mkdir placepal-backend
cd placepal-backend
npm init -y
```
- Install dependencies:
```bash
npm install express mongoose socket.io node-geocoder
```
2. **Create Back-End Code**:
- In the `placepal-backend` folder, create `server.js`:
```javascript
const express = require('express');
const mongoose = require('mongoose');
const socketIo = require('socket.io');
const http = require('http');
const cors = require('cors');
const NodeGeocoder = require('node-geocoder');
const app = express();
const server = http.createServer(app);
const io = socketIo(server, { cors: { origin: '*' } });
app.use(cors());
app.use(express.json());
// Connect to MongoDB
mongoose.connect('mongodb://localhost/placepal', { useNewUrlParser:
true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err));
// Geocoder for address-to-coordinates
const geocoder = NodeGeocoder({ provider: 'openstreetmap' });
// Agent Schema
const agentSchema = new mongoose.Schema({
name: String,
phone: String,
bvn: String,
address: String,
location: { type: { type: String, enum: ['Point'] }, coordinates:
[Number] },
verified: Boolean,
});
agentSchema.index({ location: '2dsphere' });
const Agent = mongoose.model('Agent', agentSchema);
// Register Agent
app.post('/api/register', async (req, res) => {
const { name, phone, bvn, address } = req.body;
try {
// Mock KYC verification (replace with YouVerify/Smile ID API)
if (bvn.length !== 11) throw new Error('Invalid BVN');
const geoData = await geocoder.geocode(address);
if (!geoData[0]) throw new Error('Invalid address');
const agent = new Agent({
name,
phone,
bvn,
address,
location: { type: 'Point', coordinates: [geoData[0].longitude,
geoData[0].latitude] },
verified: true,
});
await agent.save();
res.json({ message: 'Agent registered' });
} catch (error) {
res.status(400).json({ error: error.message });
});
// Find Nearby Agents
app.get('/api/agents/nearby', async (req, res) => {
const { lat, lng } = req.query;
try {
const agents = await Agent.find({
location: {
$near: { $geometry: { type: 'Point', coordinates: [parseFloat(lng),
parseFloat(lat)] }, $maxDistance: 5000 },
},
});
res.json(agents);
} catch (error) {
res.status(400).json({ error: 'Error fetching agents' });
});
// Service Request
app.post('/api/request', async (req, res) => {
const { agentId, serviceType, amount } = req.body;
try {
// Mock payment (replace with Flutterwave/Paystack API)
if (!agentId || !serviceType || !amount) throw new Error('Invalid
request');
io.emit('newRequest', { agentId, serviceType, amount });
res.json({ message: 'Request submitted' });
} catch (error) {
res.status(400).json({ error: error.message });
});
// Real-Time Tracking
io.on('connection', (socket) => {
console.log('Client connected');
socket.on('updateLocation', async ({ agentId, lat, lng }) => {
await Agent.updateOne(
{ _id: agentId },
{ location: { type: 'Point', coordinates: [parseFloat(lng),
parseFloat(lat)] } }
);
io.emit('locationUpdate', { agentId, lat, lng });
});
});
server.listen(3000, () => console.log('PlacePal server running on
http://localhost:3000'));
```
3. **Test the Back-End**:
- Ensure MongoDB is running (locally or via Atlas).
- In the `placepal-backend` folder, run:
```bash
node server.js
```
- You should see “MongoDB connected” and “PlacePal server running on
http://localhost:3000”.
4. **Test API Endpoints** (using Postman or VS Code REST Client):
- **Register Agent**:
```http
POST http://localhost:3000/api/register
Content-Type: application/json
"name": "John Doe",
"phone": "08012345678",
"bvn": "12345678901",
"address": "Ikeja, Lagos, Nigeria"
```
- **Get Nearby Agents**:
```http
GET http://localhost:3000/api/agents/nearby?lat=6.5244&lng=3.3792
```
- **Submit Request**:
```http
POST http://localhost:3000/api/request
Content-Type: application/json
"agentId": "replace_with_mongodb_id",
"serviceType": "withdrawal",
"amount": 5000
```
---
#### **Step 4: Integrate Real-Time Tracking**
1. **Add Socket.io to Front-End**:
- Install Socket.io client:
```bash
cd placepal-frontend
npm install socket.io-client
```
- Update `DashboardScreen.js` to listen for location updates:
```javascript
import io from 'socket.io-client';
// Inside DashboardScreen component
useEffect(() => {
const socket = io('http://localhost:3000');
socket.on('locationUpdate', ({ agentId, lat, lng }) => {
setAgents(prev =>
prev.map(agent =>
agent.id === agentId ? { ...agent, latitude: lat, longitude: lng } :
agent
);
});
return () => socket.disconnect();
}, []);
```
2. **Test Real-Time Tracking**:
- Simulate location updates by sending a WebSocket message (e.g., via a
test script or Postman WebSocket):
```javascript
"agentId": "replace_with_mongodb_id",
"lat": 6.5250,
"lng": 3.3800
```
- The map should update marker positions (requires real agent IDs from
MongoDB).
---
#### **Step 5: Integrate APIs**
1. **KYC Verification (YouVerify/Smile ID)**:
- Sign up for YouVerify or Smile ID (expect $0.1–$1 per verification).
- Update `server.js` to verify BVN:
```javascript
// Install: npm install axios
const axios = require('axios');
app.post('/api/register', async (req, res) => {
const { name, phone, bvn, address } = req.body;
try {
const kycResponse = await
axios.post('https://api.youverify.co/v2/verifications/bvn', {
id: bvn,
apiKey: 'YOUR_YOUVERIFY_API_KEY',
});
if (!kycResponse.data.success) throw new Error('Invalid BVN');
// Continue with geocoder and agent saving...
} catch (error) {
res.status(400).json({ error: error.message });
});
```
2. **Payments (Flutterwave/Paystack)**:
- Sign up for Flutterwave or Paystack (sandbox mode for testing).
- Update `server.js` for payments:
```javascript
const Flutterwave = require('flutterwave-node-v3');
const flw = new Flutterwave('YOUR_PUBLIC_KEY', 'YOUR_SECRET_KEY');
app.post('/api/request', async (req, res) => {
const { agentId, serviceType, amount } = req.body;
try {
const payment = await flw.Charge.card({
card_number: 'TEST_CARD_NUMBER',
cvv: '123',
expiry_month: '09',
expiry_year: '25',
amount,
email: '[email protected]',
tx_ref: `placepal_${Date.now()}`,
});
if (payment.status !== 'success') throw new Error('Payment failed');
io.emit('newRequest', { agentId, serviceType, amount });
res.json({ message: 'Request submitted' });
} catch (error) {
res.status(400).json({ error: error.message });
});
```
3. **Geolocation**:
- The `node-geocoder` package is already included in `server.js` for
address-to-coordinates conversion.
- Replace with Google Maps API if needed (requires API key, ~$5/month).
---
#### **Step 6: Test the Full App**
1. **Run Back-End**:
- In the `placepal-backend` folder:
```bash
node server.js
```
2. **Run Front-End**:
- In the `placepal-frontend` folder:
```bash
npx expo start
```
- Test on your phone (via Expo Go) or emulator.
3. **Test Features**:
- Register an agent (submit form, check MongoDB for stored data).
- View nearby agents on the map (mock data initially).
- Submit a service request and verify the alert.
- Simulate location updates via WebSocket (requires manual testing until
real devices are used).
---
#### **Step 7: Deploy the App**
1. **Back-End Deployment (Heroku)**:
- Sign up for Heroku (free tier available).
- Install Heroku CLI:
```bash
npm install -g heroku
```
- Deploy:
```bash
cd placepal-backend
heroku create placepal-backend
git init
git add .
git commit -m "Initial commit"
git push heroku main
```
- Set up MongoDB Atlas and update `server.js` with the Atlas connection
string.
2. **Front-End Deployment (Expo)**:
- Build the app for production:
```bash
cd placepal-frontend
npx expo export
```
- Publish to Google Play/App Store (requires developer accounts: $25 one-
time for Google, $99/year for Apple).
- Alternatively, share the app via Expo’s QR code for testing.
---
#### **Cost Breakdown (DIY Approach)**
- **Software**: Free (VS Code, Node.js, MongoDB, Expo).
- **APIs**:
- YouVerify: ~$0.1–$1 per KYC check (budget $100 for initial 100–1,000
verifications).
- Flutterwave/Paystack: Free sandbox, 1–2% transaction fees in production.
- Google Maps API: ~$5/month (optional, Node-Geocoder is free).
- **Hosting**:
- Heroku: Free tier or $7–$50/month for scaling.
- MongoDB Atlas: Free tier (512MB) or $9/month for 2GB.
- **Total Initial Cost**: $100–$200 (excluding Play Store/App Store fees).
---
#### **Learning Resources**
Since you’re building this yourself, here are beginner-friendly resources:
- **React Native**: [Expo Docs](https://docs.expo.dev), [React Native
Tutorial](https://www.youtube.com/watch?v=0-S5a0eKiOc).
- **Node.js/Express**: [freeCodeCamp Node.js
Course](https://www.youtube.com/watch?v=TNV0_7QRDwY).
- **MongoDB**: [MongoDB University](https://university.mongodb.com).
- **APIs**: Flutterwave/Paystack developer docs, YouVerify API guides.
---
#### **Tips for Success**
- **Start Small**: Focus on the MVP (registration, map, requests) and test in
a small area (e.g., Port Harcourt, based on your interest in the region).
- **Learn Incrementally**: Spend 1–2 hours daily on tutorials. Start with
React Native, then Node.js, and finally APIs.
- **Test Locally**: Use mock data initially to save on API costs.
- **Leverage Community**: Join Nigerian tech communities (e.g., Andela
Learning Community, Devcenter) for free mentorship.
- **Regulatory Compliance**: Consult a fintech lawyer (e.g., via Upwork,
~$50/hour) to ensure CBN compliance for KYC and transactions.
---
#### **Next Steps**
1. **Set Up Environment**: Follow Step 1 to install VS Code, Node.js,
MongoDB, and Expo.
2. **Build Front-End**: Create and test the React Native app (Steps 2–3).
3. **Build Back-End**: Set up and test the Node.js server (Step 4).
4. **Integrate APIs**: Add KYC and payment APIs (Step 5).
5. **Test and Iterate**: Run the app locally, fix bugs, and add features like
offline mode.
6. **Deploy**: Launch on Heroku and Expo (Step 7).
If you hit any roadblocks (e.g., errors in VS Code, API setup issues), share the
specific problem, and I’ll provide tailored code or commands to fix it. I can
also break down any step further or provide additional resources for learning.
Want to start with a specific part (e.g., front-end or back-end) or need help
with a particular feature? Let me know!