mypay is a QR-based mobile payment platform designed to simplify everyday payments for small businesses and their customers. It removes the need for customers to manually enter business numbers or account references by enabling a fast scan → enter amount → confirm payment flow using STK push.
Merchants create and manage their businesses through a Flutter mobile app, where they generate a secure QR code to display at their point of sale. Customers scan the QR code with their phone camera, which opens a lightweight web payment page in their browser, allowing them to enter an amount and complete payment with a single confirmation on their phone.
mypay focuses on reducing payment errors, protecting merchant privacy, and delivering a faster, more intuitive payment experience on top of existing mobile money infrastructure.
- ✅ Merchant Onboarding – Merchants register and create one or more businesses from a Flutter app
- ✅ Secure QR Generation – Businesses generate signed, server-validated QR codes
- ✅ QR Validation – Scanned QR links are verified server-side before payments are allowed
- ✅ Customer Web Checkout – QR scan opens a React web UI with business details and amount entry
- ✅ STK Push Flow (Mocked) – Payment request triggers a simulated STK push confirmation
- ✅ Session Management – Short-lived payment sessions (e.g. 5 minutes) to prevent abuse
- ✅ Payment Confirmation – Customers receive clear success or failure feedback after payment
qr-payments-mvp/
├── backend/ # Node.js + Express server
│ ├── index.js # Main server with all endpoints
│ ├── package.json
│ └── node_modules/
├── merchant_app/ # Flutter mobile app
│ ├── lib/main.dart # Complete Flutter implementation
│ ├── pubspec.yaml
│ └── ...
├── customer_web/ # React web app
│ ├── src/
│ │ ├── main.jsx
│ │ ├── App.jsx
│ │ ├── App.css
│ │ └── pages/
│ │ ├── PaymentPage.jsx
│ │ └── SuccessPage.jsx
│ ├── index.html
│ ├── vite.config.js
│ ├── package.json
│ └── node_modules/
└── README.md
- Node.js (v14+)
- Flutter (latest stable)
- npm or yarn
git clone https://github.com/Philip38-hub/mypay.git
cd mypaycd backend
npm install
npm startExpected output:
Backend running on http://localhost:4000
cd customer_web
npm install
npm run devExpected output:
VITE v5.0.8 ready in 123 ms
➜ Local: http://localhost:3000/
cd merchant_app
flutter pub get
flutter runSelect your target (Android emulator, iOS simulator, or web).
- Open Flutter app
- Tap Home tab (default)
- Tap ➕ Add Business FAB
- Step 1 - Business Details:
- Name:
Mary's Mangoes - Message:
Fresh mangoes daily - Tap Continue
- Name:
- Step 2 - Payment Type:
- Select:
Pochi la Biashara - Tap Continue
- Select:
- Step 3 - Payment Details:
- Phone:
+254712345678 - Tap Create Business
- Phone:
- QR code displays with business name and payment type
- QR URL is shown (copyable)
- Tap Open Customer Page to test
- QR opens in browser →
http://localhost:3000/pay/{businessId}?v=1&exp=...&sig=... - Backend validates QR signature and expiry
- Customer sees:
- Business name:
Mary's Mangoes - Message:
Fresh mangoes daily - Payment type badge
- Business name:
- Enter amount:
150 - Tap Pay Now
- Loading spinner (2 seconds)
- Success page shows:
- ✓ Payment Successful!
- Business: Mary's Mangoes
- Amount: KES 150.00
- Payment ID:
pay_xxx
- Tap Back to Businesses
- Business appears in list
- Tap to view QR again
http://localhost:3000/pay/{businessId}
?v={qrVersion}
&exp={timestamp}
&sig={HMAC-SHA256}
- Signature Validation - HMAC-SHA256 with secret key
- Expiry Check - QR valid for 60 minutes
- Business Verification - Business must exist and be active
- Session Creation - 5-minute session for payment
POST /api/business
{
"displayName": "Mary's Mangoes",
"message": "Fresh mangoes daily",
"paymentType": "pochi",
"paymentDetails": { "phone": "+254712345678" }
}
→ Returns: { id, qrUrl, qrImage, expiresAt }
POST /api/qr/validate
{
"businessId": "biz_xxx",
"v": 1,
"exp": 1712345678,
"sig": "abc123..."
}
→ Returns: { sessionId, business }
POST /api/payments
{
"sessionId": "sess_xxx",
"amount": 150
}
→ Returns: { paymentId, status: "pending", amount }
→ After 2s: status becomes "success"
GET /api/payments/{paymentId}
→ Returns: { id, status, amount }
{
id: "biz_xxx",
displayName: "Mary's Mangoes",
message: "Fresh mangoes daily",
paymentType: "pochi",
paymentDetails: { phone: "+254712345678" },
qrVersion: 1,
isActive: true,
createdAt: timestamp
}{
id: "sess_xxx",
businessId: "biz_xxx",
expiresAt: timestamp,
createdAt: timestamp
}{
id: "pay_xxx",
businessId: "biz_xxx",
amount: 150,
status: "pending" | "success",
createdAt: timestamp,
completedAt: timestamp (if success)
}- Bottom Navigation - Home, Analytics, Profile tabs
- Business List - View all created businesses
- Multi-step Form - Business details → Payment type → Payment details
- QR Display - Shows QR image, URL, and open button
- Analytics Placeholder - Future feature
- Profile Placeholder - Merchant info
- Minimal Design - Focus on payment flow
- QR Validation - Automatic on page load
- Amount Input - Simple number field
- Loading State - Spinner during payment
- Success Screen - Payment confirmation with details
- ❌ No real M-Pesa integration
- ❌ No authentication (anyone can create businesses)
- ❌ No persistence (data lost on restart)
- ❌ No error handling (happy path only)
- ❌ No rate limiting
- ❌ No logging
- ❌ QR expires after 60 minutes (hardcoded)
- ❌ Session expires after 5 minutes (hardcoded)
If you can complete this flow, the MVP is working:
- ✅ Create business in Flutter app
- ✅ Generate QR code
- ✅ Open QR in browser
- ✅ See customer payment page
- ✅ Enter amount and pay
- ✅ See success confirmation
# Check if port 4000 is in use
lsof -i :4000
# Kill process if needed
kill -9 <PID># Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
npm run dev- Ensure backend is running on
localhost:4000 - On Android emulator, use
10.0.2.2:4000instead oflocalhost:4000 - Check firewall settings
- Ensure React app is running on
localhost:3000 - Check browser console for errors
- Verify QR URL format in Flutter app
- Add Database - PostgreSQL with Prisma ORM
- Add Auth - JWT for merchants, QR for customers
- Integrate M-Pesa - Real STK Push via Safaricom API
- Add Logging - Winston or Pino for debugging
- Add Tests - Jest for backend, Flutter tests
- Deploy - Heroku for backend, Vercel for React
- Mobile Optimization - Responsive design, PWA
- Analytics - Track transactions, revenue per merchant
MIT
Built with ❤️ for the MVP