A comprehensive ASP.NET Core Web API for managing an intelligent ice cream vending machine with state machine architecture, payment processing, and real-time tracking.
- Features
- Tech Stack
- Project Architecture
- Prerequisites
- Installation
- Configuration
- Database Setup
- Running the Application
- API Documentation
- State Machine Flow
- Testing
- Contributing
- State Machine Architecture: Manages vending machine states (Idle, Item Selection, Payment, Dispensing)
- Payment Processing: Integrated with Stripe for secure payments
- Product Management: Full CRUD operations for ice cream products
- Transaction Tracking: Complete audit trail of all transactions
- QR Code Scanning: Initiates purchase flow via QR scan
- Email Notifications: Automated confirmation emails after purchase
- Redis Caching: Optimized product data retrieval
- JWT Authentication: Secure API access with role-based authorization
- Image Upload: Product image management
- State History: Complete log of all state transitions
- .NET 9.0
- ASP.NET Core Web API
- Entity Framework Core 9.0
- SQL Server
- Mapster - Object mapping
- Stripe.NET - Payment processing
- StackExchange.Redis - Caching
- ASP.NET Core Identity - Authentication & Authorization
- JWT Bearer - Token-based authentication
- State Machine Pattern
- Observer Pattern
- Repository Pattern
- Dependency Injection
Advanced-Vending-Machine/
βββ VendingMachine.PL/ # Presentation Layer (API Controllers)
βββ VendingMachine.BLL/ # Business Logic Layer
β βββ Service/ # Business services
β βββ StateMachine/ # State machine implementation
βββ VendingMachine.DAL/ # Data Access Layer
βββ Data/ # DbContext
βββ Model/ # Entity models
βββ Repository/ # Data repositories
βββ DTO/ # Data Transfer Objects
βββ Enums/ # Enumerations
βββ Migrations/ # EF Core migrations
- .NET 9.0 SDK
- SQL Server (LocalDB or full version)
- Redis (Optional for caching)
- Visual Studio 2022 or VS Code
- Stripe Account for payment processing
git clone https://github.com/yourusername/advanced-vending-machine.git
cd advanced-vending-machinedotnet restoredotnet tool install --global dotnet-efCreate appsettings.json in the VendingMachine.PL directory:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Server=.;Database=VendingMachineDB;Trusted_Connection=True;TrustServerCertificate=True;",
"Redis": "localhost:6379"
},
"Jwt": {
"SecretKey": "YourSuperSecretKeyHereMustBeLongEnough123456789"
},
"Stripe": {
"SecretKey": "sk_test_your_stripe_secret_key"
},
"SmtpSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"Email": "your-email@gmail.com",
"Password": "your-app-password"
}
}- Sign up at Stripe
- Get your API keys from the Dashboard
- Add your secret key to
appsettings.json
For Gmail:
- Enable 2-factor authentication
- Generate an app-specific password
- Add credentials to
appsettings.json
If not using Redis, you can comment out the Redis configuration in Program.cs.
Edit the connection string in appsettings.json to match your SQL Server instance:
"DefaultConnection": "Server=YOUR_SERVER;Database=VendingMachineDB;Trusted_Connection=True;TrustServerCertificate=True;"cd VendingMachine.PL
dotnet ef database updateThe application automatically seeds:
Products:
- Vanilla Ice Cream - $5.00
- Chocolate Ice Cream - $5.50
- Strawberry Ice Cream - $6.00
- Mint Ice Cream - $5.75
- Cookie Dough Ice Cream - $6.50
Users:
| Password | Role | |
|---|---|---|
| Lama@gmail.com | La@000 | Admin |
| nemeh@gmail.com | Ly@000 | Admin |
| layal@gmail.com | na@000 | Customer |
cd VendingMachine.PL
dotnet runThe API will be available at:
- HTTPS:
https://localhost:7259 - HTTP:
http://localhost:5038
dotnet publish -c Release -o ./publishhttps://localhost:7259/api/v1
POST /Accounts/Login
Content-Type: application/json
{
"email": "Lama@gmail.com",
"password": "La@000"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}GET /Accounts/ConfirmEmail?token={token}&userId={userId}GET /ProductsGET /Products/{id}POST /Products
Authorization: Bearer {token}
Content-Type: multipart/form-data
Name: Vanilla Ice Cream
Price: 5.0
Quantity: 50
MainImage: [file]PATCH /Products/{id}
Authorization: Bearer {token}
Content-Type: multipart/form-data
Name: Updated Name
Price: 6.0
Quantity: 40
MainImage: [file]PATCH /Products/{id}/toggle-status
Authorization: Bearer {token}DELETE /Products/{id}
Authorization: Bearer {token}POST /Transactions
Authorization: Bearer {token}
Content-Type: application/json
{
"productId": 1
}GET /Transactions/{id}
Authorization: Bearer {token}DELETE /Transactions/{id}
Authorization: Bearer {token}POST /CheckOut/payment
Authorization: Bearer {token}
Content-Type: application/json
{
"transactionId": 1
}Response:
{
"success": true,
"message": "Payment session created successfully",
"url": "https://checkout.stripe.com/...",
"paymentId": "cs_test_..."
}GET /CheckOut/success/{session_id}/{transactionId}GET /CheckOut/cancelPOST /Scan/scan-qr
Content-Type: application/json
{
"qrCode": "VEND-12345"
}POST /vending/trigger?evt=QR_ScannedAvailable Events:
QR_ScannedItem_SelectedPayment_InitiatedPayment_ConfirmedPayment_FailedDispense_CompleteResetError_Occurred
GET /vending/stateResponse:
{
"state": "Idle"
}GET /vending/historyResponse:
{
"state": [
{
"id": 1,
"stateBefore": "Idle",
"stateAfter": "ItemSelection",
"eventTriggered": "QR_Scanned",
"timestamp": "2025-12-14T10:00:00"
}
]
}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β State Transitions β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Idle
ββ[QR_Scanned]ββ> ItemSelection
ββ[Item_Selected]ββ> PaymentPending
ββ[Payment_Initiated]ββ> PaymentProcessing
ββ[Payment_Confirmed]ββ> ProductDispensing
β ββ[Dispense_Complete]ββ> Idle
ββ[Payment_Failed]ββ> ItemSelection
[Error_Occurred] from any state ββ> Error ββ> Idle
| State | Description |
|---|---|
| Idle | Machine waiting for customer interaction |
| ItemSelection | Customer browsing and selecting products |
| PaymentPending | Waiting for payment initiation |
| PaymentProcessing | Processing payment through Stripe |
| ProductDispensing | Dispensing the selected product |
| Error | Error state for handling failures |
- Import the API endpoints
- Login to get JWT token
- Add token to Authorization header
- Test endpoints
# 1. Login
POST /api/v1/Accounts/Login
{
"email": "Lama@gmail.com",
"password": "La@000"
}
# 2. Get Products
GET /api/v1/Products
# 3. Create Transaction
POST /api/v1/Transactions
{
"productId": 1
}
# 4. Process Payment
POST /api/v1/CheckOut/payment
{
"transactionId": 1
}- JWT Bearer token authentication
- Role-based authorization (Admin/Customer)
- Password hashing with ASP.NET Identity
- Email confirmation required
- Account lockout after failed login attempts
- HTTPS enforcement
- SQL injection protection via EF Core
Solution: Verify SQL Server is running and connection string is correct
Solution: Comment out Redis configuration or install Redis locally
Solution: Check SMTP settings and ensure app password is used for Gmail
Solution: Verify Stripe API key is correct and in test mode
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License.
- Stripe for payment processing
- Redis for caching solutions
- Microsoft for .NET and Entity Framework
- The open-source community
Built with β€οΈ using .NET 9.0