Adds GET /customer/wallet/exchange/preview?coins= and POST /customer/wallet/exchange (docs/prd-point-coin.md F4, K3, PC-401). The customer exchanges a multiple of the organization's coin_amount and gets (coins / coin_amount) x point_amount EnakPoint, approved by their PIN (K8). A malformed amount is refused before the PIN is checked, so it costs no attempt. In one transaction the wallet is locked, EXCHANGE_OUT takes the EnakCoin in K9 order and EXCHANGE_IN adds the EnakPoint; the two rows share a group, point at each other and both freeze the rate in their metadata. The EnakPoint are split over the EnakCoin lots they came from, each part keeping its lot's expiry and pointing back at it, so exchanging cannot extend a balance's life. The split takes floor(coins so far x rate) per lot, which adds up exactly because the total is a multiple of coin_amount. EnakPoint have no validity of their own until the expiry model is decided (N4), so the EnakCoin lot is for now the only bound. The Idempotency-Key header (or X-Idempotency-Key) is required. A retry with the same key is recognised under the wallet lock and replayed with the ids and rate the first attempt froze, even if the rate has changed since; the same key for another amount is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Backend Template
Clean architecture based backend template in Go.
Makefile
Makefile requires installed dependecies:
$ make
Usage: make [command] [ENV=staging|production]
Commands:
run Run server (default: staging)
run ENV=production Run server with production config
rename-project name={name} Rename project
build-http Build http server
migration-create name={name} Create migration
migration-up Up migrations
migration-up ENV=production Up migrations (production DB)
migration-down Down last migration
docker-up Up docker services
docker-down Down docker services
fmt Format source code
test Run unit tests
HTTP Server
The server takes no CLI flags. It reads ENV_MODE and loads the matching YAML file from
infra/ — see Running the Application for details.
# Build, then start with the staging config (default)
$ go build -o ./bin/http-server ./cmd/server/main.go
$ ENV_MODE=staging ./bin/http-server
# Start with the production config
$ ENV_MODE=production ./bin/http-server
API Docs
License
This project is licensed under the MIT License.
Apskel POS Backend
A SaaS Point of Sale (POS) Restaurant System backend built with clean architecture principles in Go.
Architecture Overview
This application follows a clean architecture pattern with clear separation of concerns:
Handler → Service → Processor → Repository
Layers
-
Contract Package (
internal/contract/)- Request/Response DTOs for API communication
- Contains JSON tags for serialization
- Input validation tags
-
Handler Layer (
internal/handler/)- HTTP request/response handling
- Request validation using go-playground/validator
- Route definitions and middleware
- Transforms contracts to/from services
-
Service Layer (
internal/service/)- Business logic orchestration
- Calls processors and transformers
- Coordinates between different business operations
-
Processor Layer (
internal/processor/)- Complex business operations
- Cross-repository transactions
- Business rule enforcement
- Handles operations like order creation with inventory updates
-
Repository Layer (
internal/repository/)- Data access layer
- Individual repository per entity
- Database-specific operations
- Uses entities for database models
-
Supporting Packages:
- Models (
internal/models/) - Pure business logic models (no database dependencies) - Entities (
internal/entities/) - Database models with GORM tags - Constants (
internal/constants/) - Type-safe enums and business constants - Transformer (
internal/transformer/) - Contract ↔ Model conversions - Mappers (
internal/mappers/) - Model ↔ Entity conversions
- Models (
Key Features
- Clean Architecture: Strict separation between business logic and infrastructure
- Type Safety: Constants package with validation helpers
- Validation: Comprehensive request validation using go-playground/validator
- Error Handling: Structured error responses with proper HTTP status codes
- Database Independence: Business logic never depends on database implementation
- Testability: Each layer can be tested independently
API Endpoints
Health Check
GET /health- Health check endpoint (registered at the root, not under/api/v1)
Organizations
POST /api/v1/organizations- Create organizationGET /api/v1/organizations- List organizationsGET /api/v1/organizations/{id}- Get organization by IDPUT /api/v1/organizations/{id}- Update organizationDELETE /api/v1/organizations/{id}- Delete organization
Users
POST /api/v1/users- Create userGET /api/v1/users- List usersGET /api/v1/users/{id}- Get user by IDPUT /api/v1/users/{id}- Update userDELETE /api/v1/users/{id}- Delete userPUT /api/v1/users/{id}/password- Change passwordPUT /api/v1/users/{id}/activate- Activate userPUT /api/v1/users/{id}/deactivate- Deactivate user
Orders
POST /api/v1/orders- Create order with itemsGET /api/v1/orders- List ordersGET /api/v1/orders/{id}- Get order by IDGET /api/v1/orders/{id}?include_items=true- Get order with itemsPUT /api/v1/orders/{id}- Update orderPUT /api/v1/orders/{id}/cancel- Cancel orderPUT /api/v1/orders/{id}/complete- Complete orderPOST /api/v1/orders/{id}/items- Add item to order
Order Items
PUT /api/v1/order-items/{id}- Update order itemDELETE /api/v1/order-items/{id}- Remove order item
Running the Application
Prerequisites
| Tool | Version | Needed for |
|---|---|---|
| Go | 1.24+ | building & running the server |
| golang-migrate | latest | make migration-* targets |
| make | any | shortcut commands (Windows: use Git Bash / WSL, see note below) |
| docker & docker-compose | optional | running Postgres/Redis locally |
| air | optional | hot reload during development (.air.toml is already configured) |
1. Clone & install dependencies
git clone <repository-url>
cd apskel-pos-backend
go mod download
2. Configuration
Configuration is not read from .env files — it is loaded from YAML files in infra/
by config/configs.go using viper.
The file is selected by the ENV_MODE environment variable:
ENV_MODE |
Config file loaded |
|---|---|
local |
infra/local.yaml |
development |
infra/development.yaml |
staging (default) |
infra/staging.yaml |
production |
infra/production.yaml |
Any other/unset value falls back to staging. Only staging.yaml and production.yaml are
committed — for local/development copy one of them first:
cp infra/staging.yaml infra/local.yaml
Two important notes:
- The config path is relative to the working directory, so always run the server from the
repository root, otherwise viper panics with
failed to read config file. - Push notifications need
infra/firebase-service-account.json(git-ignored). Without it, obtain the file from the team before enabling FCM features.
3. Run migrations
The migration targets build the DB URL from the credentials at the top of the Makefile:
make migration-up # staging DB (default)
make migration-up ENV=production # production DB
make migration-create name=create_cash_advances_table
make migration-down # roll back the last migration
make migration-force version=87 # clear a dirty migration state
4. Start the server
make run # ENV_MODE=staging
make run ENV=production # ENV_MODE=production
make run is just a wrapper around:
ENV_MODE=staging go run cmd/server/main.go
The server listens on the server.port value from the loaded YAML (4000 for both staging and
production). Verify it is up:
curl http://localhost:4000/health
All application routes live under /api/v1 (see internal/router/router.go).
Windows note
The run/start targets use POSIX inline env-var syntax, which cmd.exe and PowerShell do not
understand. Either run make from Git Bash / WSL, or start the server directly:
# PowerShell
$env:ENV_MODE = "staging"; go run cmd/server/main.go
:: cmd.exe
set ENV_MODE=staging && go run cmd/server/main.go
Hot reload
ENV_MODE=local air # rebuilds ./tmp/main on every .go change
5. Other commands
make # show all available targets
make fmt # go fmt ./...
make test # go test ./... -v
# Build a binary (make build-http still points at the old ./cmd/http path)
go build -o ./bin/http-server ./cmd/server/main.go
Running with Docker
docker-compose.yaml provides Postgres (5432), Redis (6379), and the API image. See
DOCKER.md for the full workflow.
make docker-up # docker-compose up -d
make docker-down # docker-compose down
If you use the containerised Postgres/Redis, point infra/local.yaml at localhost:5432 /
localhost:6379 instead of the remote hosts baked into staging.yaml.
Example API Usage
Create Organization
curl -X POST http://localhost:4000/api/v1/organizations \
-H "Content-Type: application/json" \
-d '{
"name": "My Restaurant",
"plan_type": "premium"
}'
Create User
curl -X POST http://localhost:4000/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"organization_id": "uuid-here",
"username": "john_doe",
"email": "john@example.com",
"password": "password123",
"full_name": "John Doe",
"role": "manager"
}'
Create Order with Items
curl -X POST http://localhost:4000/api/v1/orders \
-H "Content-Type: application/json" \
-d '{
"outlet_id": "uuid-here",
"user_id": "uuid-here",
"table_number": "A1",
"order_type": "dine_in",
"notes": "No onions",
"order_items": [
{
"product_id": "uuid-here",
"quantity": 2,
"unit_price": 15.99
}
]
}'
Project Structure
apskel-pos-backend/
├── cmd/
│ └── server/ # Application entry point
├── internal/
│ ├── app/ # Application wiring and dependency injection
│ ├── contract/ # API contracts (request/response DTOs)
│ ├── handler/ # HTTP handlers and routes
│ ├── service/ # Business logic orchestration
│ ├── processor/ # Complex business operations
│ ├── repository/ # Data access layer
│ ├── models/ # Pure business models
│ ├── entities/ # Database entities (GORM models)
│ ├── constants/ # Business constants and enums
│ ├── transformer/ # Contract ↔ Model transformations
│ └── mappers/ # Model ↔ Entity transformations
├── migrations/ # Database migrations
├── Makefile # Build and development commands
├── go.mod # Go module definition
└── README.md # This file
Dependencies
- Gorilla Mux - HTTP router and URL matcher
- GORM - ORM for database operations
- PostgreSQL Driver - PostgreSQL database driver
- Validator - Struct validation
- UUID - UUID generation and parsing
Contributing
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.