3.1 Introduction
3.1.1 Background
Me2You is a consumer-to-consumer (C2C) marketplace built for South Africa. The platform connects individual buyers and sellers in one unified account model, with escrow payment protection, three delivery options, a community discovery layer and a unified driver capability. The brief is mobile-first, low-bandwidth and POPIA-compliant.
3.1.2 About this website
The stack is the permitted set only: HTML5, CSS3, JavaScript (vanilla, no jQuery in the deployed build), PHP 8.3 (procedural, file-per-route), MySQL 8.0 (62 InnoDB tables, utf8mb4), Bootstrap 5.3.2 from a CDN and Leaflet 1.9.4 for maps. Payments are taken through PayFast (the South African gateway, sandbox in submission and live after merchant KYC). Transactional email runs through Titan SMTP. The pickup-point delivery option calls the Paxi API with a graceful fallback when credentials are absent. Hosting is on AWS EC2 behind an Application Load Balancer with Auto Scaling, RDS MySQL Multi-AZ, ElastiCache Redis and S3 for uploads. The whole stack is defined as Terraform in deploy/terraform/. No CMS is used.
3.2 Technical stack
Me2You is built strictly on the permitted technologies for the module, with no framework code generators and no CMS. The whole application is hand-written and version-controlled. The layers are as follows.
3.2.1 Application and front end
- PHP 8.3 (procedural, PDO): the server language. The application is file-per-route procedural PHP; every page is a PHP file under app/. All database access goes through PDO prepared statements with emulation switched off.
- MySQL 8.0: the relational database. 62 InnoDB tables in utf8mb4, with foreign keys, transactions and FULLTEXT search indexes.
- Vanilla JavaScript: all interactivity (cart badge, live filters, the discovery feed, map widgets) is written in plain JavaScript. No jQuery ships in the deployed build.
- HTML5 and CSS3: semantic, accessible markup and hand-written CSS. Per-page CSS files load only on their matching page to keep payloads small.
- Bootstrap 5.3.2 (CDN): the responsive grid and a small set of components, loaded from a content delivery network.
- Leaflet 1.9.4 maps: the mapping library for delivery tracking and address selection, with routing from OSRM and address lookup from Nominatim (OpenStreetMap services).
3.2.2 Integrations and infrastructure
- PayFast payment gateway: South Africa's main online card gateway, handling 3D Secure and settling in rands. The instant transaction notification (ITN) webhook confirms each payment server-side.
- Apache on AWS (af-south-1): the web server, running on EC2 instances in the Cape Town region behind an Application Load Balancer with Auto Scaling. Provisioned as Terraform.
- Redis (sessions and cache): ElastiCache Redis Multi-AZ holds PHP sessions and cached query results, so any instance behind the load balancer can serve any request.
- Amazon S3 (media): uploaded listing photos and short videos are stored in an S3 bucket rather than on the instance, so media survives instance replacement.
The live platform that this manual describes is reachable below. Each operational section that follows links directly to the running page it explains.
3.3 Basics: accessing the website and the admin area
3.3.1 Accessing the website
Open any modern browser (Chrome, Firefox, Safari, Samsung Internet) on a phone, tablet or computer. Navigate to https://m2y.online. The site is fully responsive and works at 375 pixels, 768 pixels and 1280 pixels. No app download is needed.
3.3.2 The admin area
Admins sign in via the same login form. After login, the navigation reveals an "Admin" entry that opens the admin dashboard. The admin area is RBAC-gated: every admin route calls require_role('admin') server-side, so the navigation hint alone is not a security gate; the server check is. Admins can view the dashboard, manage users, moderate listings, manage orders, resolve disputes, approve drivers, run driver payouts, view the escrow ledger and the audit log.
3.4 Registration and authentication
3.4.1 Creating an account
- Click "Sign up" in the top navigation.
- Fill in full name, email, optional phone, and a password of at least 8 characters.
- Tick the POPIA consent box: "I agree to the Privacy Policy and consent to Me2You processing my personal data for marketplace services."
- Click "Create account". The account is created with the buyer role and you are signed in immediately.
3.4.2 Logging in
- Click "Sign in" in the navigation.
- Enter your email and password. The session cookie is HttpOnly and SameSite=Lax. The session id is regenerated on login, which prevents session-fixation attacks.
- If you have forgotten your password, click "Forgot password" and follow the email link (valid for one hour).
3.4.3 User roles and the unified account
Me2You uses a unified account model. A single user record can buy and sell from the same profile. The driver capability is an applied-for, admin-approved extension of a regular account, stored in a separate couriers table joined by user_id. The four roles are:
- Buyer: browse, search, buy, track, leave reviews, open disputes, watch listings, propose swaps, place bids, set up lay-bye, tip drivers, join communities. The default role on registration.
- Seller: all buyer actions plus create and edit listings, manage orders, dispatch, set vacation mode, view earnings.
- Driver: view available jobs by radius, accept and complete deliveries, capture proof of delivery, view earnings and ratings, request cashout. Granted after onboarding and admin approval.
- Admin: full RBAC scope. Manages users, listings, orders, disputes, escrow, driver applications, payout runs and the audit log.
3.4.4 Editing your profile
Open the account menu (top right) and choose "Settings". Update display name, phone, password, avatar, cover image, notification preferences and bank details for payouts. The settings area also holds the POPIA data export and the account-deletion request.
3.5 Buyer guide
3.5.1 Browsing and searching
- From the homepage, tap a category pill or type into the search bar. Search uses MySQL FULLTEXT with prefix matching, so "dri" finds "drill" and "driver".
- Filter by category, price, condition (new, like-new, good, fair), province and pickup proximity. Sort by newest, price low-to-high, price high-to-low or relevance.
- Property and vehicle categories route to specialised browse pages with their own filters.
3.5.2 Product detail page
Each listing renders photos (and optional short video), title, price in rands, condition, full description, seller card with rating, and the action panel. The action panel adapts to the listing kind: a Buy or Lay-bye or Bid tab switcher appears on eligible listings. Open any listing from the browse page above to see a live product detail page.
3.5.3 Cart and checkout
- Tap "Add to cart" on any product. The cart icon in the navigation shows a badge count.
- Open the cart, adjust quantities and remove items as needed. The subtotal updates live.
- Tap "Checkout". Choose a delivery method (see 3.8). Choose a payment method (see 3.13). Optionally add a driver tip.
- Review the order summary. Tap "Pay safely". You are redirected to PayFast to enter card details.
- On successful payment, you receive a tracking reference (for example, M2Y-9F4K).
3.5.4 Order tracking
Open "My orders" from the account menu to see your order list. Each order shows a status timeline: placed, paid, dispatched, received, completed. The escrow badge sits prominently at the top, showing whether funds are held, released, frozen or refunded. For driver deliveries, the tracking page shows the courier's last known location on a Leaflet map.
3.5.5 Confirming receipt and leaving a review
When the parcel arrives, open the order and tap "Confirm receipt". This releases the escrow to the seller (or starts the 48-hour auto-release if you do nothing). You may then leave a review of the seller. Sellers can also leave a review of buyers; the two-sided review is rendered on each member's profile.
3.5.6 Opening a dispute
- On the order page tap "Something not right?".
- Choose a reason: not received, wrong item, damaged, not as described, other.
- Upload up to four evidence photos (maximum 4 MB each) and describe the problem.
- Tap "Submit dispute". The escrow row is frozen immediately, so the seller cannot be paid until an admin resolves.
- An admin reviews both sides and resolves within 48 hours: full refund, partial refund or release to seller.
3.6 Seller guide: products (adding, removing, updating)
3.6.1 Becoming a seller
Every member can list. Click "Sell" in the navigation or open the seller onboarding page to verify a phone number, add bank details for payouts and accept the seller agreement.
3.6.2 Adding a product (creating a listing)
- Open "Sell" in the navigation. The unified sell wizard asks for kind (item, property, vehicle), category, condition, price (rands), weight, description and photos.
- Upload one to six photos (JPEG, PNG or WebP, maximum 2 MB each). MIME is validated server-side using getimagesize so a file extension cannot be spoofed.
- Tap "Publish for moderation". The listing enters the pending queue. An admin reviews it within hours. If approved, it goes live and appears in search.
3.6.3 Updating a product
From the seller dashboard open any of your listings and tap "Edit". Change the title, description, price, condition or photos. Saving sends the listing back through moderation if a sensitive field changed; otherwise the change is live immediately.
3.6.4 Removing a product
From the seller dashboard tap the actions menu on a listing and choose "Take down". The listing is hidden from public browse but the historical orders that referenced it stay intact. To re-list, choose "Republish".
3.6.5 Adding a short video
Listings can carry a short MP4 (maximum 15 MB, maximum 30 seconds) alongside photos. Open the listing edit page and choose "Add video". The video appears in the listing gallery and in the discovery feed.
3.6.6 Importing from Facebook
Sellers can paste a Facebook Marketplace URL into the import page. The Open Graph parser pre-fills title, description, price, photos and location, which the seller reviews and confirms.
The site has three menu surfaces:
- Top navigation: Browse, Feed, Communities, Sell (for sellers), the cart and the account menu. Rendered from app/includes/header.php and adapts to the signed-in role.
- Footer: About, safety centre, legal pages (POPIA, terms, cookies, seller and driver agreements), help, contact. Rendered from app/includes/footer.php.
- Mobile bottom navigation: Home, Browse, Sell, Cart, Account. Always visible on phone widths for one-thumb reach.
Categories are stored in the categories table and rendered through the same include on every category surface; adding a new category is a single INSERT and it appears everywhere.
3.8 Shipping and delivery options
Three delivery methods, all with escrow protection, set up in the shipping_methods table:
Me2You driver (door-to-door)
An approved Me2You driver picks up from the seller and delivers to the buyer's address. The delivery fee is distance-based (typically R35 to R90) and is calculated server-side using haversine distance against the courier's last ping. The order is published to the driver job board the moment the buyer pays.
Pickup point (Paxi, Pargo, PEP)
The seller drops the parcel at the nearest store. The parcel moves through the network in two to five business days. The buyer collects from their chosen pickup point using a 6-digit collection code. Network fees apply (typically R40 to R60).
Self-collection
The buyer and seller meet in person. A 6-digit OTP confirms the handover: the buyer shows the code only after inspecting the item, the seller enters it on their phone, and the escrow releases at that moment. Free.
3.9 The front page: adding and changing images
The homepage hero image is served from app/shared/hero-logo.png (a 520 by 520, 39 KB optimised asset with explicit width and height and fetchpriority high, so the largest contentful paint stays under target). To change it, replace the file with a similarly-sized image and rebuild the cache. Featured listings under the hero are pulled from the listings table by recency and trust score; the homepage code does not hard-code any image, so adding listings to the demo seed automatically updates the homepage.
3.10 Changing the logo
The Me2You peace-hand logo is in app/shared/ in three sizes (192, 512 and 1024 pixels) plus an Apple touch icon. Replace these four files with new artwork at the same dimensions; the includes pick them up automatically. The hub logo at /hub/shared/logo-peace.png is the same artwork, served by the hub independently so the hub can be rebranded without changing the marketplace.
3.11 Orders
3.11.1 What happens when someone places an order
A single database transaction wraps the whole checkout: the order row is created in "placed" status, the cart items are snapshotted into order_items (so subsequent price changes on the listing do not affect the order), and the cart is cleared. The buyer is then redirected to PayFast.
When PayFast settles, the ITN handler at /webhooks/payfast_itn.php runs four security checks (IP whitelist, signature, server-side validate, amount match against the database). On success it transitions the order to "paid", writes an escrow row in "held" status, and branches on the fulfilment type: for driver delivery it publishes a job to delivery_assignments; for self-collection it mints a 6-digit OTP into collection_otps; for pickup network it stubs a Paxi waybill into pickup_shipments.
3.11.2 What the seller needs to do when an order is received
- Watch the email notification from team@m2y.online or the seller dashboard.
- Pack the item. For driver delivery, wait for the courier to make contact. For pickup network, drop the parcel at the chosen store and enter the waybill code on the order. For self-collection, arrange a meeting time.
- Mark "Dispatched" on the order. The order moves to "dispatched" status.
- When the buyer confirms receipt (or 48 hours pass without dispute) the escrow releases. Earnings queue for the Friday payout batch.
3.12 Updating a page on the site
Me2You is a file-per-route procedural PHP application. Each user-facing URL maps to a file under app/, for example /listings/browse.php is app/listings/browse.php. To update a page, edit that PHP file directly. Static legal pages live under app/legal/. Shared chrome (header, footer, navigation, security headers and the Content-Security-Policy) lives in app/includes/header.php and app/includes/footer.php; changes there affect every page. Per-page CSS lives under app/assets/css/pages/ and is loaded only on its matching page, which keeps the per-page CSS small.
To add a new page, create the PHP file in the right directory, require the auth helpers and the header at the top, write the page body with output escaped through e(), and require the footer at the bottom. Add a link to the new page in app/includes/header.php or app/includes/footer.php so it is reachable.
3.13 Payments
Me2You uses a provider-agnostic payment layer (app/config/payments.php). One provider is active today and others are registered as "coming soon" so the buyer sees the roadmap and the operator can activate a new rail by flipping its status, without rewriting checkout:
- Card via PayFast (active): South Africa's main online card gateway. 3D Secure (PASA-mandated) is handled by PayFast. Settles in rands to a South African bank. Sandbox in the submission build; live after the PayFast merchant KYC clears (one to two weeks).
- Instant EFT via Ozow (coming soon): pay-from-bank-app, no card. Reachable through PayFast aggregation.
- Payflex, PayJustNow (coming soon): pay in three or four interest-free instalments.
- Mobicred (coming soon): revolving credit, monthly repayments.
- International card via Stripe (coming soon): for non-SA cards. Requires Stripe Connect plus a UK-entity / FX / tax resolution.
- Bank deposit / EFT (coming soon): manual transfer with proof-of-payment verification.
Money is always handled as an integer number of cents in the database (price_cents, total_cents). The rands() helper is the only place currency is formatted for display, so the rand sign is consistent across the whole site. Payments never bypass escrow: every paid order writes an escrow_transactions row in "held" status that only releases when the buyer confirms or the 48-hour auto-release fires.
3.14 Driver guide
3.14.1 Onboarding
Apply on the driver registration page with SA ID, driver's licence, vehicle type and service area. Submit and you are redirected to the onboarding page which tracks application status. An admin reviews and approves the application within 48 hours. Once approved the "Driver" navigation entry appears in the account menu.
3.14.2 Going on duty and accepting jobs
- Open the driver job board. Toggle "On duty" at the top. Only on-duty drivers see open jobs.
- Jobs inside your service radius show normally. Jobs outside are greyed out with the actual distance.
- Each job card shows pickup to drop-off, distance, estimated time, payout, parcel weight and an escrow badge confirming the buyer has paid.
- Tap "Accept job". The system takes a row-level lock on the delivery_assignments row so two drivers cannot both win the same job.
3.14.3 Active delivery
- Navigate to the seller. Tap "Arrived at pickup", scan the parcel barcode (or type the order reference) and tap "Picked up".
- Drive to the buyer. Send periodic GPS pings in the background; the buyer's tracking map updates live.
- On arrival, capture proof of delivery: photo of the parcel at the address, buyer's signature on screen, and tap "Submit POD".
- The order flips to "received" and the 48-hour escrow countdown begins.
3.14.4 Earnings and payout
The driver earnings page shows this week, pending payout, month total, average per job, recent payout batches and full job history. Drivers are paid every Friday via bank EFT in one batch. Tips are 100 percent to the driver (no platform cut) and ride alongside the same EFT.
3.14.5 Ratings
The driver ratings page shows the star rating from buyers plus acceptance, on-time and cancellation KPIs. Drivers in the top 15 percent get "Gold driver" status, which gives priority visibility and a small per-delivery bonus.
3.15.1 Discovery feed
The discovery feed is a vertical short-video feed with tabs For You, Following and Near me. Only the on-screen video plays, to keep prepaid data use low. Tap to unmute. Swipe up adds to cart, swipe in the opposite direction adds to the wishlist (Watch). Inline social bar lets the buyer like, comment, share, follow and shop without leaving the feed.
3.15.2 Community hubs
Community hubs group people by town, suburb, security estate or interest. Each hub has its own "what's happening" feed (updates, events, safety alerts) and a roster of members. A per-community manager (a different role from the site-wide admin) can approve members, pin posts and publish events without touching the global RBAC.
3.15.3 Following and saved searches
The following page shows the people and communities you follow. The saved-searches page stores named search queries and notifies you when a new listing matches. The check_saved_searches cron runs every 15 minutes.
3.16 Transaction depth: lay-bye, auctions, swap, watch
3.16.1 Reserve with deposit (lay-bye)
On eligible listings the action panel offers a "Lay-bye" tab. The buyer pays a deposit into escrow, the seller reserves the item and approves within the chosen window, and the buyer pays the remainder in instalments. A timestamped risk acknowledgement is recorded for both parties.
3.16.2 Auctions
Time-boxed listings show a countdown, current bid, minimum next bid (price-scaled increments: R1 for cheap items, R100 for expensive ones), and a bid form. The bid endpoint is transaction-guarded so two bids cannot both win.
3.16.3 Swap offers
The "Propose a swap" button on the product detail page opens a flow where the buyer picks one of their own listings to offer in trade, writes a message, and sends. The seller sees both items side by side and accepts, declines or counters. A message thread is opened automatically.
3.16.4 Watch and price-drop alerts
Tap the heart on any listing to "Watch" it. A row is written to wishlists. When the seller drops the price, the price_drop_alerts cron sends a notification: "Price dropped from R349 to R299 on Nike Air Max."
3.16.5 Seller vacation mode
Sellers can pause their shopfront without delisting. The vacation page carries a toggle; when on, all of the seller's listings display a "back on" date and new orders are paused.
3.17 Privacy, POPIA and account control
The account settings page holds POPIA-related controls:
- Data export: downloads a JSON file of every row that references your user id (orders, listings, messages, reviews) so you can take your data with you.
- Account deletion: soft-deletes your account (status flips to "deleted", login disabled, data retained for the POPIA audit trail).
- Notification preferences: per-channel toggles (email, in-app) for each event type.
Passwords are hashed with bcrypt and automatically rehashed on login if the cost factor increases. Every database query uses PDO prepared statements with EMULATE_PREPARES off, so prepared parameters are bound at the protocol layer. Output is escaped through e() everywhere. CSRF tokens are session-bound and validated with hash_equals.
3.18 Admin guide
Every page below is RBAC-gated and reachable only by a signed-in administrator. Each opens the live admin tool it describes.
3.18.1 Dashboard and KPIs
The admin dashboard shows active users, live listings, 30-day GMV in rands, open disputes and an "attention queue" for items that need triage. Below the tiles: a recent orders table with buyer and seller names, a top-categories breakdown and a 30-day GMV chart.
3.18.2 User management (CRUD)
The user management page is a searchable, filterable table. Clicking a row opens a side drawer with the user's profile, order history and KYC status. Admins can edit name, email, role and status (active, suspended, deleted). Only the super-admin can hard-delete.
3.18.3 Listing moderation
The moderation page shows the queue of pending listings plus flagged live listings, with system risk signals (price anomaly, off-platform call-to-action, new-account flag). Three actions: approve, request edit, reject and warn. A rejection requires an audit note.
3.18.4 Order management
The order management page shows the full orders table with status filter pills. Click any order for the detail view: items, buyer and seller, delivery tracking, status workflow, escrow state and admin actions (force complete, issue refund, add note).
3.18.5 Dispute resolution
The disputes page opens disputes oldest first. The split panel shows the buyer's side and the seller's side with their evidence. Three resolution buttons: refund buyer (escrow refunded, order cancelled), partial split (admin enters the amounts), release to seller (escrow released, order completed). A resolution note is required. Both parties are emailed.
3.18.6 Escrow ledger
The escrow ledger is a read-only ledger of every escrow transaction. Use it to verify the auto-release cron is working, check frozen escrows tied to open disputes and reconcile against PayFast settlement reports.
3.18.7 Driver approvals and payouts
The verifications page holds the queue of driver applications with their ID and licence uploads. The driver payouts page groups pending driver earnings by driver, exports a bank-ready CSV for the weekly EFT batch and marks the batch as paid once the bank confirms.
3.18.8 Audit log
The audit log records every admin action: actor, action, target type, target id, timestamp and a note. Required for POPIA accountability and useful for support investigations.
3.19 Checking web traffic and statistics
Three signals are available out of the box:
- The admin dashboard KPI tiles for active users, live listings, 30-day GMV and open disputes, computed directly from the database.
- The admin analytics page for orders by day, top categories, and search-term frequency.
- CloudWatch (in the AWS deploy) for technical signals: ALB request count and 5xx rate, RDS CPU and free storage, Redis memory, Auto Scaling instance count. Alarms are wired to an SNS topic that emails the operator.
A Lighthouse CI workflow runs on every push and asserts mobile performance, accessibility, best-practices and SEO budgets against the four most-visited pages. An external uptime monitor (UptimeRobot) pings the health endpoint every five minutes and emails on failure.
3.20 Appendix: setting up the website
3.20.1 Local development
mysql -u root -p -e "CREATE DATABASE m2y CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u root -p -e "CREATE USER 'm2y'@'localhost' IDENTIFIED BY 'm2y_dev'; GRANT ALL ON m2y.* TO 'm2y'@'localhost';"
mysql -u m2y -pm2y_dev m2y < db/schema.sql
for f in db/migrations/*.sql; do mysql -u m2y -pm2y_dev m2y < "$f"; done
for f in db/seeds/*.sql; do mysql -u m2y -pm2y_dev m2y < "$f"; done
php -S localhost:8080 -t app app/router.php
Open http://localhost:8080 in any browser. The router script makes the PHP built-in server serve the custom 404 page (Apache uses ErrorDocument; the router emulates that for local dev).
3.20.2 Production deployment (AWS)
cd deploy/terraform
terraform init
terraform validate
terraform plan
terraform apply
The Terraform stack provisions a VPC, an Application Load Balancer with HTTP-to-HTTPS redirect, an Auto Scaling group of EC2 instances, RDS MySQL Multi-AZ with 7-day backups, ElastiCache Redis Multi-AZ, an S3 uploads bucket, Secrets Manager and a CloudWatch alarm set (with SNS email) plus an AWS Budgets cost alarm. Each instance bootstraps from deploy/user-data.sh: install PHP and Apache, pull secrets from Secrets Manager into Apache SetEnv, install the CloudWatch agent, run deploy/migrate.php behind a Redis lock. The complete runbook is in deploy/GO-LIVE-EC2.md and every external account (PayFast, Titan, Paxi, GoDaddy) is documented in deploy/EXTERNAL-ACCOUNTS.md.
3.20.3 Demo accounts (password password123)
- buyer@m2y.online (Buyer)
- seller@m2y.online (Seller)
- driver@m2y.online (Driver)
- admin@m2y.online (Administrator)
PayFast sandbox card for the demo flow: 4000 0000 0000 0002, expiry 12/30, CVV 123.
3.21 Troubleshooting and help
3.21.1 Common errors
| Error | Cause | Fix |
| Bad CSRF token | Session expired or form submitted twice | Refresh the page and try again |
| 403 Forbidden | Wrong role for this page | Sign in with the correct role |
| PayFast "Payment failed" | Card declined or bank timeout | Try again or use a different method |
| File too large | Photo exceeds 2 MB | Resize before upload |
| Listing rejected | Moderator flagged it | Check the rejection reason in the seller dashboard and edit |
| OTP expired | The 6-digit code is valid for 24 hours | Contact support to issue a new code |
3.21.2 Getting help
- Email: support@m2y.online
- In-app: the "?" help icon opens the FAQ and a contact form.
- Response time: disputes within 48 hours, general queries within 24 hours.