ammar.sheikh
All case studies
ShippedTransit · Full-Stack Web2024 — 2025

ScanPayGo

Bus ticketing on a two-stage fare: a deposit holds the seat, the balance is taken only when the ticket is scanned.

Role · Full-stack engineer

Overview

A smart bus ticket booking and validation system, built as an academic project. Passengers register, hold a wallet balance, pick a seat against live availability, and board by having a unique QR code scanned; an admin side manages the fleet, approves refunds, and visualizes seat occupancy.

Problem

A booking is a promise, not a completed journey. Charging the full fare up front turns every no-show into refund work, while charging nothing up front reserves seats for free. Validation has the mirror problem: a ticket code that can be scanned twice is a fare that can be charged twice.

Solution

The fare is split across the two moments that actually matter. Fifteen percent is taken from the passenger's wallet as a deposit when the seat is booked; the remaining balance is deducted only when the ticket's QR code is scanned on boarding. Refunds are requested by the passenger and settled through an explicit admin approval step.

Architecture

A single Flask application over SQLite, serving Bootstrap-rendered views with Flask sessions carrying both passenger and admin authentication. Each ticket is issued with a unique QR code generated by Python's qrcode library, and the scan of that code — not the booking — is the event that settles the outstanding fare, so one code maps to exactly one charge.

Tech stack

  • Python
  • Flask
  • SQLite
  • Bootstrap
  • qrcode

Engineering decisions

  • Fare split into a booking deposit and a scan-time balance, so a held seat is never free and a no-show never requires unwinding a full payment.
  • The QR scan is the settlement event rather than the booking: one code, one fare, redeemable once.
  • Refunds pass through explicit admin approval instead of automatic reversal, keeping a human in the loop on money leaving the system.

Links