12. Design Vending Machine System
Step 1: Clarify the requirements
Functional Requirement
- Display a range of items 展示商品
- Accepts user payment (cash, credit card, phone payment) 接受用户付款
- Dispense the item after payment 付款后发放商品
- Show product prices and information 显示商品价格和信息
- Inventory management, for managing all the vending machines and their inventory
- Payment system
- Notification system, notify the owner company about inventory status
Non-Functional Requirement
- Network Stability 网络稳定
- TCP: Reliable, ensures that data sent is received completely and correctly.
- MQTT: Commonly used in Iot, Remote controlling. It's a light-weight protocol, and it's not reliable in sometimes, eventhought we could choose the QoS (Quality of Service) level.
- High Availability 高可用性
- Consistency 一致性
Step 2: Database and Schema
-- machines table
CREATE TABLE machines (
machine_id INT AUTO_INCREMENT PRIMARY KEY,
location VARCHAR(255) NOT NULL,
make VARCHAR(50),
model VARCHAR(50),
status ENUM('ACTIVE', 'INACTIVE', 'MAINTENANCE') NOT NULL,
last_refill_date DATE,
last_service_date DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- items table
CREATE TABLE items (
item_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(5,2) NOT NULL,
description TEXT
);
-- machine_inventory table
CREATE TABLE machine_inventory (
machine_id INT,
item_id INT,
quantity INT NOT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
);
-- transactions table
CREATE TABLE transactions (
transaction_id INT AUTO_INCREMENT PRIMARY KEY,
machine_id INT,
item_id INT,
transaction_amount DECIMAL(5,2) NOT NULL,
transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('PENDING', 'SUCCESS', 'FAILED') NOT NULL,
);
-- users table
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
role ENUM('ADMIN', 'OPERATOR') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Step 3: API Designs
Machine management
Get a list of all machines
GET /v1/machines
Response:
[
{
"machine_uuid": 1,
"location": "1st Floor, Building A",
"status": "ACTIVE"
},
...
]
Check machine's heartbeat
GET /machines/{machine_id}/status
Response:
{
"status": "ACTIVE"
}
Get all products and their prices on a single machine
GET /v1/machines/{machine_id}/items
Response:
[
{
"item_id": 1,
"item_name": "Coke",
"item_price": 1.5
},
...
]
User purchases items
- Check inventory 检查库存: Confirm the item is in stock before buying.
- Give change (In cash) 找零: Return change if the user overpays
- Transactions 事务: Keep Transactions during purchase to avoid issues.
Endpoint: /v1/machines/{machine_id}/purchase
Method: POST
Request:
{
"item_id": 2,
"quantity": 1,
"payment_amount": 1.5
}
// Success
{
"status": "success",
"change_amount": 0.0,
"message": "Thank you for your purchase."
}
// Failure
{
"status": "failed",
"message": "Insufficient stock or payment amount."
}
Release an item
POST /machines/{machine_id}/release
Request:
{
"item_id": "123",
"transaction_id": "456"
}
Response:
{
"status": "success"
}
Payment system
- PCI Safety: Keep Payment Card Industry (PCI) standards
- Transactions and Idempotency 事务与幂等:
- Make payments transactional to roll back errors.
- Ensure APIs give same result even if called multiple times
- HTTPS: Use HTTPS to encrypt data during transfer
- Tokenization or Hashing: Convert sensitive data to a token or a hashing value, not in plain text
Start a payment transaction
Endpoint: /api/startPayment
Method: POST
Request:
{
"machineId": "MCH12345",
"ItemId": "ITEM98765",
"paymentMethod": "CREDIT_CARD" // CASH", "CREDIT_CARD", "APPLE_PAY"
}
Response:
{
"transactionId": "TXN0011223344",
"amountDue": "5.50"
}
Process a payment
Endpoint: /api/completePayment
Method: POST
Request:
{
"transactionId": "TXN0011223344",
"amountPaid": 6.00,
"paymentDetails": {
"cardNumber": "4111111111111111",
"expiryDate": "12/25",
"cvv": "123"
}
}
Response:
{
"change": 0.50,
"status": "SUCCESS"
}