A self-hosted PHP tool for developers building payment integrations in Kenya. Generate realistic synthetic identity data, trigger live M-Pesa STK Push requests, create hosted checkout sessions, and inspect wallet balances — all from a single authenticated web interface.
The name means "to test, to try" in Swahili. That's exactly what this is for.
Built on the IntaSend PHP SDK with a production-grade structure — proper auth, CSRF protection, PDO, webhook signature verification — so the patterns you see here translate directly into real applications.
| Feature | Description |
|---|---|
| ID Generator | Generates realistic Kenyan (and global) identity profiles — names, national IDs, phone numbers, addresses, credit cards, work history. Premium-gated. |
| Hosted Checkout | Creates a secure, branded payment page via the IntaSend Checkout API. Customer is redirected to complete payment. |
| M-Pesa STK Push | Sends a payment prompt directly to any Kenyan mobile number. Customer approves on their phone. |
| Wallet Viewer | Displays IntaSend wallet balances and transaction history in real time. |
| Transaction Log | Full audit trail of every payment initiated — stored in MySQL, filterable by status and type. |
| Premium Subscription | One-time KES 500 payment unlocks the ID Generator. Handled through the same checkout flow. |
| Admin Dashboard | Manage users, view payments, toggle premium status, and monitor platform activity. |
- PHP 7.4+ — no framework, structured MVC-style
- MySQL 5.7+ — PDO with prepared statements throughout
- IntaSend PHP SDK — Checkout, STK Push, Wallet, Transfer
- Tailwind CSS + DaisyUI — utility-first styling
- Guzzle — HTTP client used by the SDK
├── admin/ # Admin-only pages (dashboard, users, settings)
├── assets/
│ └── css/style.css # Custom design system (Wasiliana brand)
├── auth/ # Login, register, logout
├── config/
│ ├── config.php # Loads .env, exposes Config::get()
│ ├── database.php # PDO singleton
│ └── session.php # SessionManager with CSRF support
├── database/
│ └── schema.sql # Full MySQL schema — run once to set up
├── includes/
│ └── functions.php # Shared helpers (validation, logging, URLs)
├── middleware/
│ ├── auth.php # require_auth(), get_logged_in_user()
│ └── admin.php # require_admin()
├── models/
│ ├── Payment.php # Payment CRUD + analytics queries
│ ├── User.php # User CRUD + password verification
│ └── Settings.php # Key-value site settings
├── pages/ # Page partials included by app.php
│ ├── home.php # Dashboard (stats, tools, activity feed)
│ ├── checkout_new.php # Hosted checkout form
│ ├── stk_new.php # STK Push form
│ ├── wallet.php # Wallet viewer
│ ├── services.php # ID Generator (premium)
│ ├── upgrade_new.php # Premium upgrade flow
│ ├── payment_history.php # Transaction log
│ └── profile.php # Account settings
├── services/
│ ├── PaymentService.php # Wraps IntaSend SDK — checkout, STK, verify
│ └── MockDataService.php # Generates fake identity profile data
├── webhook/
│ └── callback.php # Payment callback handler (signature verified)
├── app.php # Main authenticated router
├── index.php # Redirects to app.php
├── .env.example # Environment variable template
└── composer.json
- PHP 7.4+
- MySQL 5.7+
- Composer
- Laragon (or any local server — Apache/Nginx)
- An IntaSend account with API keys
composer installcp .env.example .envEdit .env:
# IntaSend — use sandbox keys for development
INTASEND_PUBLISHABLE_KEY=ISPubKey_test_xxxxxxxxxxxxxxxxxxxx
INTASEND_SECRET_KEY=ISSecretKey_test_xxxxxxxxxxxxxxxxxxxx
# Database
DB_HOST=localhost
DB_NAME=wasiliana
DB_USER=root
DB_PASS=
# App
APP_ENV=development
APP_URL=http://wasiliana.test
APP_NAME=WasilianaSandbox vs Live: The SDK auto-detects the environment from your key prefix (
ISPubKey_test_→ sandbox,ISPubKey_live_→ production). Always use sandbox keys during development.
mysql -u root < database/schema.sqlOr using Laragon's MySQL path:
"C:\laragon\bin\mysql\mysql-8.0.40-winx64\bin\mysql.exe" -u root < database/schema.sqlIf using Laragon, the virtual host is auto-created:
http://Intasend-Tutorial-Series-master.test
Register an account at /auth/register.php, then log in.
After registering, run this SQL to promote a user:
UPDATE users SET is_admin = 1 WHERE email = 'your@email.com';Then visit /admin/dashboard.php.
User clicks "Upgrade"
→ pages/upgrade_new.php
→ PaymentService::createCheckout()
→ IntaSend Checkout API
→ User redirected to hosted payment page
→ User pays
→ IntaSend POSTs to /webhook/callback.php
→ Signature verified
→ User premium status updated in DB
| Key | Description |
|---|---|
INTASEND_PUBLISHABLE_KEY |
Public key from IntaSend dashboard |
INTASEND_SECRET_KEY |
Secret key — never expose client-side |
DB_HOST |
Database host (default: localhost) |
DB_NAME |
Database name |
DB_USER |
Database user |
DB_PASS |
Database password |
APP_ENV |
development or production |
APP_URL |
Full base URL of the app |
SESSION_LIFETIME |
Session TTL in seconds (default: 7200) |
- Passwords hashed with
bcryptviapassword_hash() - All DB queries use PDO prepared statements — no raw interpolation
- CSRF tokens on every POST form
- Session regeneration on login
- Webhook callbacks verify IntaSend's HMAC signature
.envandcredentials.phpare gitignored — never committed- Input sanitized with
htmlspecialcharsbefore output
Ordered by ease of implementation. Items at the top can be shipped in an afternoon; items at the bottom are multi-day efforts.
- User authentication (register, login, logout)
- Premium subscription via hosted checkout (KES 500 one-time)
- M-Pesa STK Push
- Wallet balance viewer
- Transaction history log
- ID Generator with free/premium field gating
- Admin dashboard (users, payments, settings)
- Webhook callback with signature verification
- CSRF protection, bcrypt passwords, PDO prepared statements
- Copy profile to clipboard — single button copies the full generated profile as JSON. Currently you copy field by field.
- Profile history in session — keep the last 5 generated profiles in session so a page refresh doesn't wipe everything.
- Email on premium activation — SMTP config is already in
.env, just never wired up. Send a confirmation email whenis_premiumflips to 1 in the webhook callback. - Webhook event log in UI — the
webhook_eventstable already exists and is populated. Surface it as a readable feed in the admin dashboard so developers can see raw payloads without hitting MySQL directly. - Unify ID Generator UI with the rest of the app — the matrix/hacker theme on
services.phpis jarring against the Wasiliana design system. Rebuild it in the same surface-card style. - Dark/light mode toggle — the CSS tokens are already set up; just wire a
<html data-theme>toggle to localStorage.
- Copy-to-clipboard + JSON/CSV export — extend the profile copy button to offer structured download formats. Prerequisite for the bulk generation feature below.
- Deeper Kenya-specific data — expand
MockDataService:- Kenyan ID numbers with correct format (8-digit, year-range validated)
- M-Pesa number prefixes by carrier: Safaricom
07[0-9]{2}, Airtel0730–0739, Telkom0770–0779 - Real Kenyan counties, constituencies, and ward names instead of a 5-item street list
- Real Kenyan university names, company names, and employer sectors
- Saved profiles — add a
saved_profilestable, let users bookmark up to 10 generated identities. Free limit: 3 saved. Premium: unlimited. No page refresh anxiety. - Payment link builder — form-based no-code tool: enter amount, description, optional expiry → generates a shareable IntaSend checkout URL the user can send to anyone. No integration code required. Natural extension of the existing checkout page.
- STK Push status polling — after initiating an STK Push, poll the IntaSend invoice status every 3 seconds via a lightweight AJAX endpoint and update the UI without a full page reload. Currently the user has to refresh manually.
- Bulk profile generation — generate 10–500 profiles at once, download as CSV or JSON. Free tier: max 10. Premium tier: up to 500. This is the highest-value addition — anyone seeding a test database or building a demo app needs volume.
- REST API with API keys — expose
GET /api/v1/generatewith query params (country,gender,count,fields). Each user gets an API key from their profile page. Rate-limit: 10 req/day free, 1000 req/day premium. This is what turns the platform from a website into a SaaS product developers actually integrate. - Webhook testing tool — assign each user a unique inbound URL (e.g.
/hook/{token}). Any HTTP POST to that URL is captured and displayed in a live feed. Like a mini RequestBin baked into the platform. Directly useful for anyone building payment integrations. - Team accounts — one owner account with multiple member seats sharing a profile library and API quota. This is the path from individual developers to agencies and QA teams — where real recurring revenue comes from.
- Subscription model — replace the one-time KES 500 with monthly/annual tiers. Free → Pro (KES 500/month) → Team (KES 2,000/month). The current model sells access once and has nothing left to offer.
Suggested first three: copy-to-clipboard, saved profiles, and the REST API. Together they make the platform genuinely useful for a developer's daily workflow rather than just a demo.
MIT — free to use, modify, and deploy.