My Solution for Design a QR Code System for a Grocery Shop
by nectar4678
System requirements
Functional:
Product Information via QR Code
- Customers should be able to scan a QR code using their mobile device to retrieve product information (price, description, availability, etc.).
- The system must support up to 10,000 products.
Self-Checkout
- Customers must be able to scan products, add them to a virtual cart, and process the checkout (including payment).
- The system should support up to 600 checkout transactions per day.
Promotions via QR Code
- Customers should be able to scan QR codes to view ongoing promotions (up to 5 concurrent promotions).
Non-Functional:
Scalability
- The system should handle up to 200 concurrent users during peak hours.
Performance
- Product information retrieval should take less than 2 seconds.
- Checkout transaction time should not exceed 5 seconds.
Security
- Customer data and transactions must be secured with end-to-end encryption.
- The system should comply with PCI-DSS for payment handling.
Reliability
- System uptime must be at least 99.9%, allowing for very minimal downtime.
Capacity estimation
To ensure scalability, we need to estimate the system’s capacity based on assumptions. Let's break down different factors:
Assumptions:
- Products: 10,000 different products, each with a unique QR code.
- Concurrent Users: 200 concurrent users (scanning, viewing promotions, or checking out).
- Transactions: 600 self-checkout transactions per day, or about 25 per hour during peak times.
- Promotion Access: 5 active promotions, accessed by 30% of customers.
- API Calls:
- Each customer will generate around 5-10 API calls per interaction (e.g., product scan, add to cart, checkout, promotions, etc.).
Capacity Calculation:
- Peak API Requests per Second:
- Assuming the 200 users are actively scanning products and processing transactions simultaneously, and each interaction generates 5 API requests within a 1-minute window:
- API Requests per second = (200 users * 5 API calls) / 60 seconds ≈ 17 requests per second (RPS).
- Database:
- We assume the product and transaction data will take up 10KB per product.
- 10,000 products would require approximately 100MB of storage for product data.
- Checkout and transaction logs may generate up to 100MB per month.
- Scaling Strategy:
- Use a load balancer to distribute requests across multiple servers.
- Implement database sharding for horizontal scaling as the product catalog or transaction volume increases.
- Use caching for frequently accessed product and promotion data to minimize load on the database.
API design
Product Information API
Endpoint: GET /api/products/{product_id}
Description: Retrieve product information based on the QR code scanned.
Response:
{
"product_id": "12345",
"name": "Organic Apple",
"price": 1.99,
"description": "Fresh organic apple from local farms",
"stock": 120
}
Checkout API
Endpoint: POST /api/checkout
Description: Process customer checkout by submitting the cart items.
Request:
{
"customer_id": "789",
"cart": [
{
"product_id": "12345",
"quantity": 2
},
{
"product_id": "23456",
"quantity": 1
}
],
"payment_method": "credit_card",
"total_amount": 5.97
}
Response:
{
"transaction_id": "xyz123",
"status": "success",
"message": "Checkout completed",
"receipt": {
"total": 5.97,
"items": [
{
"name": "Organic Apple",
"quantity": 2,
"price": 1.99
},
{
"name": "Banana",
"quantity": 1,
"price": 0.99
}
]
}
}
Promotions API
Endpoint: GET /api/promotions
Description: Get a list of active promotions.
Request: No parameters needed.
Response:
{
"promotions": [
{
"id": "promo1",
"description": "10% off on all dairy products",
"valid_until": "2024-12-31"
},
{
"id": "promo2",
"description": "Buy 1 get 1 free on select fruits",
"valid_until": "2024-11-30"
}
]
}
Inventory Update API
Endpoint: PUT /api/inventory/{product_id}
Description: Update product stock.
Request:
{
"product_id": "12345",
"stock": 150
}
Response:
{
"product_id": "12345",
"status": "updated",
"new_stock": 150
}
Database design
High-level design
Mobile Application:
- Frontend used by customers to scan QR codes, view product information, access promotions, and process self-checkout.
QR Code Scanner:
- Integrated into the mobile app to allow users to scan QR codes for products or promotions.
Backend/API Layer:
- Manages all business logic. Receives requests from the mobile app (e.g., product information, checkout, promotions) and interacts with the database to fetch or update data.
Database:
- Stores information on products, transactions, promotions, and customers. Ensures data consistency and handles all read/write operations.
Admin Portal:
- A web interface used by store managers to manage products, promotions, and stock levels.
Payment Gateway:
- Handles payment processing during the checkout phase. Ensures secure and PCI-DSS-compliant transactions.
Caching Layer:
- Stores frequently accessed data (e.g., product information, promotions) to reduce load on the database and improve performance.
Load Balancer:
- Distributes incoming API requests across multiple backend servers for scalability.
Request flows
Next, let's detail how a typical request flows through the system, both for product information retrieval and for a self-checkout process.
Product Information Flow
- Customer Scans QR Code: The mobile app captures the QR code and sends a request to the API Layer.
- API Layer Processes Request: The API layer queries the Caching Layer for the product data. If not cached, it retrieves the product details from the Database.
- Response to Mobile App: The product data is returned to the mobile app for display (price, stock, description).
Checkout Flow
- Customer Completes Shopping: After scanning products, the customer initiates the checkout from the mobile app.
- API Processes Checkout: The API Layer verifies cart items and calculates the total amount.
- Payment Gateway: The payment request is forwarded to the Payment Gateway to process the transaction.
- Update Inventory: Upon successful payment, the inventory is updated in the database for the purchased items.
- Transaction Logged: The transaction is logged, and a receipt is generated for the customer.
Detailed component design
7.1 Caching Layer
- Role: The caching layer stores frequently accessed data, such as product details and promotions, to reduce latency and offload pressure from the database.
- Scaling:
- Uses a distributed cache (like Redis or Memcached) to ensure scalability and fault tolerance.
- Expiration policies (TTL - Time to Live) can be applied to keep the cache fresh without needing manual invalidation.
- Algorithm:
- LRU (Least Recently Used) caching can be used to ensure that the most frequently accessed data remains in the cache while evicting less commonly accessed data when cache memory fills up.
7.2 Payment Gateway
- Role: This component handles the payment transactions during checkout. It ensures that payments are processed securely and that the system is compliant with PCI-DSS standards.
- Scaling:
- Most external payment processors (like Stripe or PayPal) offer their own scaling, so this component doesn't need custom scaling.
- Timeout and retry logic will be used to handle network failures or latency issues.
Trade offs/Tech choices
Relational Database (PostgreSQL):
- Chosen for its ability to handle complex queries (e.g., product searches, inventory updates) and transactional consistency required for checkout. However, it may need sharding for higher scalability.
Caching (Redis):
- Chosen to offload repeated read requests (e.g., product and promotion data), reducing the load on the main database. Caching may introduce data staleness issues, but with proper TTL configurations, this can be minimized.
REST API:
- Chosen for simplicity and widespread adoption. While a more complex GraphQL API could offer more flexibility, REST is easier to scale and maintain for this use case.
Failure scenarios/bottlenecks
Caching Failure: If the caching layer goes down, the database could become overwhelmed by frequent reads. Solution: Implement fallback mechanisms to directly query the database in case of cache failure.
Payment Gateway Latency: The payment gateway might experience delays, leading to checkout failures. Solution: Implement retry mechanisms and clear user messaging in the app during payment delays.
Concurrent User Bottlenecks: As the number of concurrent users grows, the API Layer could become a bottleneck. Solution: Horizontal scaling with load balancers can distribute the traffic across multiple servers.
Future improvements
Integration with Loyalty Programs: Adding features to integrate loyalty programs and external payment gateways.
AI-based Recommendations: Implement machine learning algorithms to suggest products or promotions based on shopping history.
Offline Mode: Allow the app to work offline for certain operations, like scanning product information, which can later sync when the user reconnects.