List the key functional requirements for the system (Ask the AI for hints if stuck)...
Product selection and purchase: Users browse available products on a touchscreen display, see prices and stock availability, select a product, and complete a purchase. The machine guides the user through the entire transaction from selection to dispensing.
Multiple payment methods: The system accepts cash (coins and bills), credit and debit cards, and mobile payments via NFC or contactless wallets. Each method has different failure modes and latency characteristics, which directly affects the transaction state machine design.
Inventory management with low-stock alerts: The system tracks stock levels per product in real time, updates inventory after each sale or restocking event, and triggers alerts to operators when stock drops below a configurable threshold. This is what keeps machines profitable: an empty machine earns nothing.
Refunds on failed or cancelled transactions: If dispensing fails (product jam) or the user cancels mid-transaction, the system issues a refund. For cash, this means returning coins. For card payments, this means reversing the charge through the payment gateway.
Maintenance mode: Authorized service personnel access an admin mode to restock products, perform repairs, update pricing, and view diagnostic information. The machine must not accept customer transactions while in maintenance mode.
List the key non-functional requirements (performance, scalability, reliability, etc.)...
High availability and reliability: The machine should operate with minimal downtime. Every minute offline is lost revenue. The system must handle hardware failures, network outages, and software crashes gracefully without losing transaction data.
Secure payment processing: All payment data is encrypted in transit and at rest. Card transactions comply with PCI-DSS requirements. The machine never stores raw card numbers locally.
Scalability across a fleet: The architecture supports managing thousands of machines from a central cloud backend. Adding a new machine should be as simple as plugging it in and registering it with the central server. The backend must handle inventory reporting, remote configuration, and software updates across the entire fleet.
Estimate the scale of the system. Consider daily active users, read/write ratio, storage requirements, bandwidth, and any relevant QPS calculations...
Fleet scale: 10,000 machines across a country. At 300 transactions per machine per day: 3 million transactions per day total, or roughly 35 transactions per second across the fleet. This is well within the capacity of a single modestly provisioned backend database.
Heartbeat and telemetry: Each machine sends a heartbeat every 60 seconds (health status, temperature, connectivity). With 10,000 machines: 167 heartbeats per second. Add inventory sync events (after each transaction): another 35 per second. Total backend ingest rate: roughly 200 events per second, trivial for any modern message queue.
Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
GET /products
Response: 200 OK {
products: [
{
product_id: string,
name: string,
price: number,
image_url: string,
quantity: number,
available: boolean
}
]
}
This endpoint serves the touchscreen display. It reads from the local product catalog cache so it works even when the network is down. Products with zero quantity show as unavailable.
POST /transactions
Body: {
product_id: string,
payment_method: "cash" | "card" | "mobile",
idempotency_key: string
}
Response: 200 OK {
transaction_id: string,
status: "completed" | "failed" | "refunded",
change_due: number
}
This is a synchronous endpoint because the user is standing at the machine waiting. Unlike web APIs that can return 202 Accepted, a vending machine must complete the full cycle (payment, dispensing, confirmation) before responding. The idempotency key prevents double-charges if the UI retries after a timeout.
A vending machine sounds simple: put in money, press a button, get a snack. But when you manage a fleet of 10,000 machines across a country, the real challenges emerge: how do you guarantee that payment and dispensing happen atomically? What happens when the machine loses network connectivity? How do you prevent two people from buying the last item simultaneously? This is not a toy problem. It is a distributed embedded system with real-money transactions and physical hardware in the loop.
Product selection and purchase: Users browse available products on a touchscreen display, see prices and stock availability, select a product, and complete a purchase. The machine guides the user through the entire transaction from selection to dispensing.
Multiple payment methods: The system accepts cash (coins and bills), credit and debit cards, and mobile payments via NFC or contactless wallets. Each method has different failure modes and latency characteristics, which directly affects the transaction state machine design.
Inventory management with low-stock alerts: The system tracks stock levels per product in real time, updates inventory after each sale or restocking event, and triggers alerts to operators when stock drops below a configurable threshold. This is what keeps machines profitable: an empty machine earns nothing.
Refunds on failed or cancelled transactions: If dispensing fails (product jam) or the user cancels mid-transaction, the system issues a refund. For cash, this means returning coins. For card payments, this means reversing the charge through the payment gateway.
Maintenance mode: Authorized service personnel access an admin mode to restock products, perform repairs, update pricing, and view diagnostic information. The machine must not accept customer transactions while in maintenance mode.
High availability and reliability: The machine should operate with minimal downtime. Every minute offline is lost revenue. The system must handle hardware failures, network outages, and software crashes gracefully without losing transaction data.
Secure payment processing: All payment data is encrypted in transit and at rest. Card transactions comply with PCI-DSS requirements. The machine never stores raw card numbers locally.
Key Insight
Why is reliability more important than raw performance here? Unlike a web service handling millions of requests per second, a single vending machine processes maybe 200-500 transactions per day. The challenge is not throughput. It is ensuring every single transaction completes correctly, especially when hardware, network, or power fails mid-transaction. One failed dispense with no refund destroys customer trust.
Scalability across a fleet: The architecture supports managing thousands of machines from a central cloud backend. Adding a new machine should be as simple as plugging it in and registering it with the central server. The backend must handle inventory reporting, remote configuration, and software updates across the entire fleet.
The capacity numbers for a vending machine system are modest per machine but add up quickly across a fleet. The key insight is that the individual machine is not the bottleneck. The central cloud backend that aggregates data from thousands of machines is where capacity planning matters.
Per machine: 200-500 transactions per day in a high-traffic location (office lobby, airport terminal). That translates to roughly 0.003-0.006 transactions per second per machine. A single machine is never a throughput challenge.
Fleet scale: 10,000 machines across a country. At 300 transactions per machine per day: 3 million transactions per day total, or roughly 35 transactions per second across the fleet. This is well within the capacity of a single modestly provisioned backend database.
Heartbeat and telemetry: Each machine sends a heartbeat every 60 seconds (health status, temperature, connectivity). With 10,000 machines: 167 heartbeats per second. Add inventory sync events (after each transaction): another 35 per second. Total backend ingest rate: roughly 200 events per second, trivial for any modern message queue.
Interview Tip
The real capacity concern is not throughput but data volume over time. Each transaction generates a log entry (roughly 500 bytes). 3 million transactions per day is 1.5GB per day, or 547GB per year. Heartbeat data at 167 per second (100 bytes each) adds 1.4GB per day. After 3 years, you have roughly 3TB of operational data. Plan your retention and archival strategy early.
Product catalog: Small and mostly static. 50 products per machine with name, price, image URL, and category is under 50KB. Even across 10,000 machines with regional product variations, the catalog fits in a few megabytes.
Transaction logs: 500 bytes per transaction (machine ID, product ID, payment method, amount, status, timestamps). 3 million per day is 1.5GB per day, 547GB per year. This is the largest data source and benefits from time-partitioned storage with compression.
Inventory state: Current stock levels per product per machine. 50 products times 10,000 machines is 500,000 rows at roughly 50 bytes each, totaling 25MB. This fits comfortably in memory, enabling fast dashboard queries.
The vending machine system has two API surfaces: the local API running on the machine itself (handling user interactions and hardware control) and the cloud API that connects machines to the central backend. Understanding both is important because the machine must function independently when the cloud is unreachable.
GET /products
Response: 200 OK {
products: [
{
product_id: string,
name: string,
price: number,
image_url: string,
quantity: number,
available: boolean
}
]
}
This endpoint serves the touchscreen display. It reads from the local product catalog cache so it works even when the network is down. Products with zero quantity show as unavailable.
POST /transactions
Body: {
product_id: string,
payment_method: "cash" | "card" | "mobile",
idempotency_key: string
}
Response: 200 OK {
transaction_id: string,
status: "completed" | "failed" | "refunded",
change_due: number
}
This is a synchronous endpoint because the user is standing at the machine waiting. Unlike web APIs that can return 202 Accepted, a vending machine must complete the full cycle (payment, dispensing, confirmation) before responding. The idempotency key prevents double-charges if the UI retries after a timeout.
POST /api/machines/:machine_id/sync
Body: {
transactions: [...],
inventory_snapshot: {...},
heartbeat: { status, temperature, last_error }
}
Response: 200 OK {
config_updates: {...},
product_catalog_version: string
}
Machines batch-sync their data to the cloud periodically and receive configuration updates in the response. This request-response pattern is more reliable than server-push for devices behind NAT or firewalls.
GET /api/machines/:machine_id/inventory
Response: 200 OK {
machine_id: string,
products: [{ product_id, quantity, min_threshold }],
alerts: [{ product_id, alert_type: "low_stock" }]
}
PUT /api/machines/:machine_id/maintenance
Body: { mode: "enter" | "exit", operator_id: string }
Response: 200 OK { status: "maintenance" | "active" }
Maintenance endpoints require operator authentication. Entering maintenance mode remotely locks the machine from customer transactions.
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
ouchscreen UI: Displays the product catalog, guides the purchase flow, and shows transaction status. Reads from the local product cache so it loads instantly without network dependency.
Control System: The brain of the machine. It orchestrates the entire transaction lifecycle: receives user input from the UI, checks inventory, coordinates payment, triggers dispensing, and handles failures. This component runs the transaction state machine that is the core of the GATE (discussed in detail in the detailed component design section).
Payment Processor: Handles both local payment (cash, coins via hardware interfaces) and remote payment (cards, mobile via payment gateway API). It abstracts the payment method so the control system does not care whether the user paid with quarters or Apple Pay.
Dispensing Mechanism: The physical motor and sensor system that releases the product. Sensors confirm whether the product actually dropped into the pickup area. This confirmation signal is critical because payment should only be finalized after a successful dispense.
Inventory Manager: Tracks stock per slot, decrements after each sale, and flags products as unavailable when stock hits zero. Syncs inventory state to the cloud during each sync cycle.
Central Cloud Server: Receives sync data from all machines, stores it in PostgreSQL, and returns configuration updates. Handles machine registration, firmware updates, and fleet-wide product catalog management.
Admin Dashboard: Web application for fleet operators to monitor machine health, view inventory levels, analyze sales trends, and dispatch maintenance technicians. Queries the cloud database for real-time and historical data.
Alert Service: Monitors inventory thresholds and machine health data. Sends notifications (email, SMS, push) to operators when a machine needs restocking, reports an error, or goes offline unexpectedly.
Purchase flow: user selects product, control system checks inventory, payment processor charges via gateway, dispensing mechanism delivers product with sensor confirmation, inventory is decremented.
Step 1: User browses products: The touchscreen displays the product catalog from the local cache. Available products show with price and image. Out-of-stock items are grayed out.
Step 2: User selects a product: The UI sends the selection to the control system. The control system checks the inventory manager to confirm the product is in stock. If stock is zero, the user is shown a message and guided to select another product.
Step 3: User chooses payment method: The UI presents available payment options. If the card reader is malfunctioning or the network is down, card and mobile options are hidden and only cash is available.
Step 4: Payment processing: For cash, the machine waits for coins and bills to meet or exceed the product price. For cards, the payment processor sends an authorization request to the payment gateway. A 15-second timeout applies: if the gateway does not respond, the transaction is cancelled and the user is notified.
Step 5: Dispensing: After payment confirmation, the control system sends a DISPENSE command to the mechanism. The motor activates and pushes the product. The optical sensor in the pickup area detects whether the product arrived.
Step 6: Confirmation or recovery: If the sensor confirms the product dropped, the transaction is marked complete, inventory is decremented, and the user is thanked on screen. If the sensor does not detect the product within 5 seconds, the system retries once. If the retry also fails, the system initiates a refund and logs a dispense failure alert for maintenance.
Interview Tip
Why check inventory BEFORE accepting payment, not after? If you accept payment first and then discover the item is out of stock, you must process a refund. For card payments, refunds take 3-5 business days. The customer walked away without a product and without their money. Checking inventory first avoids this entirely: the user never pays for an unavailable item.
Inventory sync: machine sends stock levels to cloud server. Cloud checks thresholds and triggers low-stock alerts. Service staff restocks machine and confirms update.
Step 1: Machine syncs inventory: Every 5 minutes, the machine sends its current inventory snapshot to the cloud backend along with any new transactions since the last sync.
Step 2: Cloud processes the sync: The backend updates the central inventory table and transaction log. It compares current stock levels against configured thresholds.
Step 3: Low-stock alert generated: If any product's quantity is below its minimum threshold, the alert service notifies the assigned technician via push notification or SMS with the machine location and which products need restocking.
Step 4: Technician restocks: The technician arrives, enters maintenance mode (which locks out customer transactions), physically adds products, updates quantities on the maintenance screen, and exits maintenance mode. The machine immediately syncs the updated inventory to the cloud.
IDLE: Machine is waiting for user interaction. Display shows the product catalog.
PRODUCT_SELECTED: User has chosen a product. System has verified the product is in stock. Display shows payment options.
AWAITING_PAYMENT: System is waiting for the user to insert cash or tap a card. A 60-second timeout returns to IDLE (user walked away).
PAYMENT_PROCESSING: Payment is being verified. For cash, the system is counting inserted coins and bills. For cards, the payment gateway is authorizing the charge. A 15-second timeout cancels the transaction and returns any inserted cash.
DISPENSING: Payment confirmed. The motor is activated and the system is waiting for the dispensing sensor confirmation. A 5-second timeout triggers a retry.
DISPENSE_CONFIRMED: The sensor detected the product in the pickup area. Inventory is decremented, the transaction is logged, and change is dispensed if applicable.
TRANSACTION_COMPLETE: Success. Display thanks the user. After 5 seconds, returns to IDLE.
DISPENSE_FAILED: The product did not drop after the retry. The system initiates a refund (return cash or reverse card charge), logs the failure, and marks the slot as malfunctioning.
PAYMENT_FAILED: The payment gateway rejected the card, the user cancelled, or the timeout expired. No product is dispensed. Any inserted cash is returned.
The atomicity guarantee works through a simple principle: charge first, confirm dispense, refund on failure. Here is why this ordering is correct and the alternatives are worse:
Charge-then-dispense (our approach): If dispensing fails after a successful charge, the system detects the failure via sensors and issues a refund. The customer temporarily loses money but gets it back. The failure is detectable and recoverable.
Dispense-then-charge (the alternative): If the charge fails after a successful dispense, the product is already in the customer's hands. There is no way to recover it. The operator absorbs the cost. Worse, a malicious user could intentionally cause payment failures to get free products.
The key enabler is the dispensing sensor. Without it, the system cannot detect whether dispensing succeeded and cannot decide whether to refund. The sensor converts a physical event into a digital signal that the state machine can act on.
When the fleet manages shared inventory (multiple machines in the same location sharing a product catalog) or when a single machine processes rapid sequential requests, the last item creates a race condition.
Local concurrency (single machine): A vending machine processes one transaction at a time (the UI blocks new selections during an active transaction), so local concurrency is not an issue. The state machine enforces sequential processing.
Fleet-level concurrency (last item in cloud inventory): When the cloud backend manages shared inventory for promotions or limited-edition items, it uses atomic decrement with a floor check: UPDATE inventory SET quantity = quantity - 1 WHERE machine_id = X AND product_id = Y AND quantity > 0. If the update affects zero rows, the product is out of stock. This prevents overselling without explicit locking.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
Each vending machine runs a lightweight embedded database (SQLite) that stores the working data needed for independent operation:
products
product_id TEXT PRIMARY KEY
name TEXT
price REAL
image_url TEXT
category TEXT
slot_number INTEGER
inventory
product_id TEXT PRIMARY KEY
quantity INTEGER
min_threshold INTEGER
last_restocked DATETIME
pending_transactions
transaction_id TEXT PRIMARY KEY
product_id TEXT
payment_method TEXT
amount REAL
status TEXT
created_at DATETIME
synced BOOLEAN DEFAULT FALSE
The pending_transactions table acts as a write-ahead log. Every transaction is written here first, then synced to the cloud. The synced flag tracks which records have been successfully uploaded. If the machine crashes mid-transaction, the WAL allows recovery on reboot.
The cloud uses PostgreSQL for relational data and structured queries:
machines
machine_id UUID PRIMARY KEY
location TEXT
status TEXT (active, maintenance, offline)
last_heartbeat TIMESTAMP
firmware_version TEXT
config_version INTEGER
transactions
transaction_id UUID PRIMARY KEY
machine_id UUID FOREIGN KEY
product_id UUID FOREIGN KEY
payment_method TEXT
amount DECIMAL
status TEXT
created_at TIMESTAMP
synced_at TIMESTAMP
products
product_id UUID PRIMARY KEY
name TEXT
price DECIMAL
category TEXT
image_url TEXT
inventory
machine_id UUID COMPOSITE PK
product_id UUID COMPOSITE PK
quantity INTEGER
min_threshold INTEGER
last_restocked TIMESTAMP
The inventory table uses a composite primary key (machine_id, product_id) because each machine has its own independent stock of each product. This lets operators query "which machines are low on Coca-Cola?" with a simple WHERE clause.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
The core engineering challenge (the GATE of this problem) is ensuring that payment and dispensing happen atomically: no scenario should result in money taken without a product delivered, or a product dispensed without payment. This sounds simple but becomes complex when you consider every failure point: payment gateway timeout, dispensing jam, power loss, network outage, and concurrent requests for the last item.
Transaction state machine: states from Idle through Product Selected, Awaiting Payment, Payment Processing, Dispensing, and Dispense Confirmed. Failure transitions trigger refund and return to Idle.
The state machine is the heart of the control system. Every transaction moves through a defined sequence of states, and every failure has an explicit recovery path. There is no ambiguous state where the system does not know what to do.
Key Insight
Why a state machine? Because vending machine transactions involve physical hardware and real money. A simple if-else chain becomes unmaintainable when you account for every failure mode: what if payment times out? What if the dispenser jams? What if power dies between payment and dispensing? A state machine makes every state and transition explicit, and every failure path is a defined transition rather than an unhandled exception.
IDLE: Machine is waiting for user interaction. Display shows the product catalog.
PRODUCT_SELECTED: User has chosen a product. System has verified the product is in stock. Display shows payment options.
AWAITING_PAYMENT: System is waiting for the user to insert cash or tap a card. A 60-second timeout returns to IDLE (user walked away).
PAYMENT_PROCESSING: Payment is being verified. For cash, the system is counting inserted coins and bills. For cards, the payment gateway is authorizing the charge. A 15-second timeout cancels the transaction and returns any inserted cash.
DISPENSING: Payment confirmed. The motor is activated and the system is waiting for the dispensing sensor confirmation. A 5-second timeout triggers a retry.
DISPENSE_CONFIRMED: The sensor detected the product in the pickup area. Inventory is decremented, the transaction is logged, and change is dispensed if applicable.
TRANSACTION_COMPLETE: Success. Display thanks the user. After 5 seconds, returns to IDLE.
DISPENSE_FAILED: The product did not drop after the retry. The system initiates a refund (return cash or reverse card charge), logs the failure, and marks the slot as malfunctioning.
PAYMENT_FAILED: The payment gateway rejected the card, the user cancelled, or the timeout expired. No product is dispensed. Any inserted cash is returned.
The atomicity guarantee works through a simple principle: charge first, confirm dispense, refund on failure. Here is why this ordering is correct and the alternatives are worse:
Charge-then-dispense (our approach): If dispensing fails after a successful charge, the system detects the failure via sensors and issues a refund. The customer temporarily loses money but gets it back. The failure is detectable and recoverable.
Dispense-then-charge (the alternative): If the charge fails after a successful dispense, the product is already in the customer's hands. There is no way to recover it. The operator absorbs the cost. Worse, a malicious user could intentionally cause payment failures to get free products.
The key enabler is the dispensing sensor. Without it, the system cannot detect whether dispensing succeeded and cannot decide whether to refund. The sensor converts a physical event into a digital signal that the state machine can act on.
When the fleet manages shared inventory (multiple machines in the same location sharing a product catalog) or when a single machine processes rapid sequential requests, the last item creates a race condition.
Local concurrency (single machine): A vending machine processes one transaction at a time (the UI blocks new selections during an active transaction), so local concurrency is not an issue. The state machine enforces sequential processing.
Fleet-level concurrency (last item in cloud inventory): When the cloud backend manages shared inventory for promotions or limited-edition items, it uses atomic decrement with a floor check: UPDATE inventory SET quantity = quantity - 1 WHERE machine_id = X AND product_id = Y AND quantity > 0. If the update affects zero rows, the product is out of stock. This prevents overselling without explicit locking.
When a customer pays with cash exceeding the product price, the machine must return the correct change using available denominations. This is a variant of the coin change problem:
The algorithm uses a greedy approach (largest denomination first) which works correctly for standard currency denominations. It tracks available coins and bills in the hopper, deducting as it dispenses. If the machine cannot make exact change (insufficient denominations), it notifies the user before accepting payment. This pre-check avoids the scenario where payment is accepted but change cannot be returned.
The local SQLite database acts as a write-ahead log. Before the state machine transitions to a new state, it persists the transition to the pending_transactions table. On reboot after a crash:
Every architectural decision in this system involves a trade-off between reliability, complexity, and cost. Understanding these trade-offs shows interviewers that you made deliberate choices rather than adopting patterns blindly.
Local processing (transactions on the machine): The machine handles the complete purchase cycle without cloud involvement. This enables offline operation and sub-second transaction completion. The cost is duplicated logic (the machine runs its own inventory, payment, and state management) and delayed visibility (the cloud only sees data after sync).
Cloud processing (thin client machine): The machine sends every user action to the cloud, which orchestrates the transaction. This centralizes logic and provides real-time visibility. The cost is hard network dependency: every network glitch interrupts customer transactions.
Our choice: local processing with cloud sync. Revenue continuity during network outages outweighs the benefits of real-time cloud visibility. A machine that cannot sell during a 30-minute ISP outage loses dozens of transactions. A cloud dashboard that is 5 minutes behind loses no revenue.
Interview Tip
Interview signal: when discussing this trade-off, frame it as availability versus visibility. Local processing maximizes availability (the machine works offline). Cloud processing maximizes visibility (operators see everything in real time). The right answer depends on the business priority, and for a revenue-generating device, availability wins.
Cash support: Requires coin mechanisms, bill validators, change dispensers, and a coin hopper. Hardware cost increases by 30-40%. Maintenance increases (coin jams, full hoppers, counterfeit detection). But cash works without network, reaches unbanked customers, and provides a fallback during payment gateway outages.
Cashless only: Simpler hardware, lower maintenance, easier accounting (all transactions are digital). But every transaction requires network connectivity, excluding customers without cards and making the machine useless during network outages.
Our choice: both. The hardware cost is fixed and amortized over the machine's 5-10 year lifespan. Cash provides a critical fallback that keeps revenue flowing during network disruptions.
Strong consistency (real-time sync): The cloud always has the exact inventory state. Requires network connectivity for every transaction and adds latency. Provides accurate dashboards but creates a hard dependency.
Eventual consistency (periodic sync): The cloud's inventory is up to 5 minutes stale. No network dependency for transactions. Dashboards show approximate data. Restocking decisions work on threshold alerts which tolerate staleness.
Our choice: eventual consistency. Inventory decisions (when to restock) are not time-sensitive enough to justify the availability cost of strong consistency. A 5-minute delay in detecting low stock is negligible when the restocking process itself takes hours.
The system's reliability depends on handling every failure mode without losing money or products. Each failure scenario maps directly to a state machine transition designed to handle it.
Scenario: A user pays for a bag of chips. The motor activates but the bag is wedged against the coil and does not drop.
Detection: The optical sensor in the pickup area does not detect the product within 5 seconds. The system retries once (sometimes a second motor pulse frees the product). If the retry also fails, the state machine transitions to DISPENSE_FAILED.
Recovery: For card payments, the system sends a refund request to the payment gateway. For cash, the coin return mechanism dispenses the equivalent amount. The transaction is logged as "refunded - dispense failure" with the slot number. The slot is disabled for future purchases. A maintenance alert is queued for the next cloud sync.
Prevention: The inventory system tracks dispense failure rates per slot. If a slot exceeds 3 failures in 24 hours, it is permanently disabled until a technician inspects it, preventing repeated customer frustration.
Common Pitfall
Every failure scenario maps to a state machine transition. This is not a coincidence. The state machine was designed by enumerating every failure mode first, then adding explicit transitions for each. If you find a failure with no defined transition, you have found a bug in the state machine.
Payment failure and refund flow: card payment gateway timeout triggers transaction cancellation. Alternatively, payment succeeds but dispensing jams, triggering sensor-detected failure and automatic refund.
Scenario: The external payment gateway (Stripe, Square) is unreachable due to a service outage or network issue.
Detection: The payment processor's HTTP request to the gateway times out after 15 seconds or returns a 5xx error.
Recovery: The transaction is cancelled. The user is informed that card and mobile payments are temporarily unavailable. If cash hardware is present, the machine degrades gracefully to cash-only mode. The machine continues to accept cash transactions and syncs normally when the gateway recovers.
Offline operation: machine loses network, serves product catalog from local cache, processes cash transactions locally, queues data for batch sync when connectivity resumes.
Scenario: The machine's cellular or WiFi connection drops mid-day.
Detection: The sync request to the cloud backend times out. The machine marks itself as offline after 3 consecutive failed sync attempts.
Recovery: The machine switches to offline mode. The local product cache continues serving the catalog. Cash transactions proceed normally using local inventory and SQLite logging. Card and mobile payments are disabled (no gateway access). When connectivity resumes, the machine batch-syncs all queued transactions and inventory updates. The cloud backend processes them in chronological order using idempotency keys to prevent duplicates.
Scenario: The power goes out after the card has been charged but before the product is dispensed.
Recovery: On reboot, the recovery process finds the transaction in PAYMENT_PROCESSING or DISPENSING state. It queries the payment gateway to confirm whether the charge was processed. If charged, it attempts dispensing. If dispensing fails (product still jammed), it refunds. If the charge was not processed, it marks the transaction as failed. The write-ahead log in SQLite ensures no transaction is lost or double-processed.