Vending Machine QR LCD
Python vending-machine payment prototype that creates single-use Razorpay QR codes, waits for signed payment confirmation, and triggers dispense only after webhook verification.
Outcomes
- ●100% of webhook events require valid HMAC-SHA256 signatures; unsigned packets rejected
- ●Webhook-to-dispense-trigger latency under 50ms in local simulation
- ●Thread-safe state engine blocks dispense for up to 300s QR TTL before timeout
Portfolio Highlights
- →Developed a production-ready UPI payment core for vending machines using FastAPI and Razorpay, featuring signed webhook verification and dispense gating.
- →Engineered a high-fidelity HMAC-SHA256 simulation tool to validate fintech security logic in "Test Mode," reducing integration risk for live deployments.
- →Designed a hardware-agnostic architecture that separates high-level payment orchestration from low-level motor/LCD control.
Snapshot
- Period: April 2026
- Domain: IoT Payment Workflows / Fintech
- Tech Stack: FastAPI, Razorpay SDK, HMAC-SHA256, Uvicorn
- AI Orchestration: Developed with Cursor and Gemini CLI; AI used for scaffolding the FastAPI webhook endpoints and Pydantic schemas, while I manually implemented the security handshake and threading logic.
- Status: Phase 1 Validated (Software Core Complete)
The Business Problem
Physical vending machines require robust, low-latency payment verification to prevent theft or "failed dispense" scenarios. However, developing for Indian UPI (Unified Payments Interface) via Razorpay presents a major developer experience hurdle: their "Test Mode" does not emit real webhook events for QR scans, making it nearly impossible to validate the production "Dispense" logic before going live.
Technical Deep Dive: The Signed Local Simulator
To bridge the "Test Mode" gap, I engineered a high-fidelity local simulation layer that exercises the exact same security path as a live Razorpay event.
The Solution:
Instead of mocking the payment, I built a secondary endpoint `/webhook/simulate/{qr_id}` that:
- 1.Generates a production-shaped Razorpay JSON payload.
- 2.Signs it using a real HMAC-SHA256 signature with the local `WEBHOOK_SECRET`.
- 3.Posts it to the actual `/webhook` listener.
This ensured that the entire signature verification logic was production-hardened before a single real rupee was spent.
Evaluation & Metrics
- Security: 100% of webhook events require valid HMAC-SHA256 signatures; unsigned or incorrectly signed packets are rejected.
- Reliability: Implemented a thread-safe `VendingMachine` state engine that blocks dispense for up to 300 seconds (QR TTL) before timing out and closing the Razorpay QR code.
- Latency: Webhook-to-dispense-trigger latency measured under 50ms (local simulation).
Key Decisions & Trade-offs
- QR API over Payment Links: Chose Razorpay's specialized QR Code API to match the "single-item physical kiosk" semantics, ensuring each transaction is isolated and idempotent.
- Threading over Asyncio: Opted for `threading.Event` for dispense gating. Since the hardware control loop (LCD/Motors) is often synchronous/blocking, this allows the FastAPI server to remain responsive while the "Machine" waits for payment.
- Pivot to Razorpay: Originally evaluated Stripe, but pivoted to Razorpay to optimize for the Indian market's dominance in UPI payments.
Development Timeline
- **AI-Accelerated Scaffolding:** Used AI agents to quickly generate the Pydantic models for the complex Razorpay response objects, saving hours of manual field-mapping.
- **Manual Security Review:** When the AI suggested a simple "token-based" auth for the simulator, I overrode it to implement the full HMAC signature path to ensure the security logic was identical to production.
- **Hardware Abstraction:** Designed the codebase with an abstract `Dispenser` class, allowing Phase 1 to run on a terminal while Phase 2 (Serial/GPIO) can be swapped in without touching the payment logic.