This project supports two environments: Production and Development. Docker Desktop is required to run the application.
Clone the repository:
git clone git@github.com:ashtishad/xpay.git && cd xpay
For users who want to run the project and interact with the API (no code changes):
make setup-prod-env # Set up the environment
make up # Run the application
### More Commands
make down # Stop the application
make down-data # Stop and remove postgres data
For developers who intend to modify the code and contribute to the project:
make setup-dev-env # Set up the environment
make up # Required for starting the postgres docker service
make watch # Run with live reload
### More Commands
make run # Run normally
make down # Stop the application
make down-data # Stop and remove postgres data
make test # Run tests
make lint # Run linter
make swagger # Generate Swagger docs
make migrate-create name=your_migration_name # Create a new migration
Note:
setup-prod-env
andsetup-dev-env
copy appropriate configurations and set up necessary tools for each environment.
For all available commands, see the
Makefile
in the project root.
For detailed guide on various aspects of the project, refer to the wiki
Core Libraries: Gin, pgx, golang-migrate, golang-jwt, viper, swaggo/swag, golangci-lint, and testify.
✅ Implemented | 🔄 In Progress/Planned
Area | Features and Best Practices | Status |
---|---|---|
API Design & Architecture | • Domain Driven Design, Clean Architecure • RESTful API • Event streaming with Apache Kafka • OpenAPI 2.0 specifications |
✅ ✅ 🔄 ✅ |
Security | • JWT-ES256 with ECDSA asymmetric key pairs • AES-256-GCM for card data encryption • SQL injection prevention with parameterized sql queries • Role based access control (RBAC) • DTO for controlled data to the client • User input and query param validation • IP-Based Rate limiting with Token Bucket algorithm |
✅ ✅ ✅ ✅ ✅ ✅ ✅ |
Database | • ACID transactions with appropriate isolation levels • Raw SQL for performance • Connection pooling with pgx, exposing standard *sql.DB • Optimized indexing and unique constraints • Version-controlled schema changes with migrations |
✅ ✅ ✅ ✅ ✅ |
Core Operations & Observability | • Custom AppError interface for error handling • Centralized configuration management with Viper • Structured logging with slog • Context with timeout for each request • Comprehensive test coverage • Code quality with golangci-lint |
✅ ✅ ✅ ✅ ✅ ✅ |
Payment Gateways | • Idempotent payment processing • Stripe integration • PayPal integration • Webhook handling for asynchronous events |
🔄 🔄 🔄 🔄 |
Deployment & Monitoring | • Multi-stage Docker builds for minimal image size • GitHub Actions CI pipeline • AWS RDS with PostgreSQL • ECS Fargate for serverless container deployment • Prometheus metrics and Grafana dashboards |
✅ ✅ 🔄 🔄 🔄 |
The project follows domain-driven design, loosely coupled clean architecture, suited for large codebase.
┌─────────┐ JSON ┌───────────────┐ Domain ┌─────────────┐
│ Client ├───────────►│ Handlers ├───────────►│ Repositories│
└─────────┘ (DTO) └───────────────┘ Models └─────────────┘
│ │
│ │
│ Domain │
│ Models │
│ │
┌─────────┐ JSON ┌───────────────┐ Domain ┌─────────────┐
│ Client ◄───────────┤ Handlers ◄───────────┤Repositories │
└─────────┘ (DTO) └───────────────┘ Models └─────────────┘
Example: Create a wallet
wallet.go (model) -> wallet_repository.go -> wallet_handlers.go (using DTOs)
- Domain Models:
internal/domain/*.go
- Repositories:
internal/domain/*_repository.go
- HTTP Handlers:
internal/server/handlers/*.go
- DTOs:
internal/server/dto/*.go
- Routes:
internal/server/routes/*.go
command: tree -a -I '.git|.DS_Store|.gitignore|.idea|.vscode|docs'
├── .github
│ └── workflows
│ └── test.yaml # CI/CD pipeline for running tests
├── internal
│ ├── domain
│ │ ├── card.go # Card domain model
│ │ ├── card_repository.go # Card repository interface, database interactions
│ │ ├── helpers.go # Domain-specific helper functions
│ │ ├── user.go # User domain model
│ │ ├── user_repository.go # User repository interface, database interactions
│ │ ├── wallet.go # Wallet domain model
│ │ └── wallet_repository.go # Wallet repository interface, database interactions
│ ├── secure
│ │ ├── card_aes.go # Card AES-256 with GCM mode, Validate, Encrypt and Decrypt
│ │ ├── jwt.go # JWT token handling, generate and validate tokens
│ │ ├── password.go # Password hashing and verification with bcrypt
│ │ └── password_test.go # Password utility tests
│ │ ├── rbac
│ │ │ ├── policy.json # RBAC policies for the API
│ │ │ ├── policy.go # Loading policy from policy.json
│ │ │ ├── rbac.go # Core logic of RBAC
│ │ │ └── rbac_test.go # Unit tests
│ ├── server
│ │ ├── handlers
│ │ │ ├── auth.go # Login, Register handlers
│ │ │ ├── card.go # Card http handlers
│ │ │ ├── helpers.go # Handlers helper functions
│ │ │ ├── user.go # User HTTP handlers
│ │ │ └── wallet.go # Wallet HTTP handlers
│ │ ├── middlewares
│ │ │ ├── auth.go # Auth middleware (Validate token, Set Authorized user in req context)
│ │ │ ├── cors.go # CORS middleware
│ │ │ ├── gin_logger.go # Custom Logging middleware for gin
│ │ │ ├── middlewares.go # Core Middleware setup
│ │ │ ├── rate_limiter.go # IP-Based rate limiter with token bucket algorithm
│ │ │ └── request_id.go # Request ID middleware, sets X-Request-ID header
│ │ ├── routes
│ │ │ ├── auth.go # Authentication routes
│ │ │ ├── card.go # Card routes
│ │ │ ├── routes.go # Core routes setup
│ │ │ ├── user.go # User routes
│ │ │ └── wallet.go # Wallet routes
│ │ ├── dto
│ │ │ ├── auth.go # Authentication-related DTOs/REST API Request Response Structurers
│ │ │ ├── card.go # Card dto
│ │ │ ├── shared.go # Shared dto
│ │ │ ├── user.go # User dto
│ │ │ └── wallet.go # Wallet routes
│ │ └── server.go # HTTP server setup with gin
│ ├── infra
│ │ ├── postgres
│ │ │ ├── postgres_connection.go # Postgres connection setup with pgx, returns *sql.DB
│ │ │ └── postgres_migrations.go # Database migration handling with golang-migrate/v4
│ │ ├── kafka
│ │ │ └── sample.md # Placeholder for Kafka integration
│ ├── common
│ │ ├── app_errs.go # Custom error types
│ │ ├── config.go # Configuration management
│ │ ├── constants.go # Global constants
│ │ ├── context_keys.go # Context key definitions
│ │ ├── custom_err_messages.go # Error message definitions
│ │ ├── slog_config.go # Structured logging configuration
│ │ └── timeouts.go # Context timeout constants
├── migrations
│ ├── 000001_create_users_table.down.sql # User table rollback
│ ├── 000001_create_users_table.up.sql # User table creation
│ ├── 000002_create_wallets_table.down.sql # Wallet table rollback
│ └── 000002_create_wallets_table.up.sql # Wallet table creation
│ ├── 000003_create_cards_table.down.sql # Cards table rollback
│ └── 000003_create_cards_table.up.sql # Cards table creation
├── scripts/
│ ├── pre-push # Git pre-push hook (runs tests and lint before every push)
│ ├── setup-dev-env.sh # Script to set up development environment
│ └── setup-prod-env.sh # Script to set up production environment
├── env-configs/
│ ├── dev.Makefile # Makefile for development environment
│ ├── prod.Makefile # Makefile for production environment
│ ├── compose.dev.yaml # Docker Compose file for development
│ ├── compose.prod.yaml # Docker Compose file for production
│ └── config.example.yaml # Example configuration file
├── config.yaml # Application configuration
├── main.go # Application entry point
├── Makefile # Development commands and shortcuts
├── Dockerfile # Docker file with multi stage builds
├── .dockerignore # Directories to ignore in the Docker builds
├── README.md # Project documentation
├── compose.yaml # Docker Compose configuration
├── go.mod # Go module definition
├── go.sum # Go module checksums
└── .air.toml # Live reload configuration with air
- URL:
/api/v1/register
- Method:
POST
- Description: Registers a new user with hashed password, generates JWT tokens, sets an HTTP-only cookie and X-Request-Id header.
- Access: Public
- Request Body:
{ "fullName": "John Doe", "email": "someone@example.com", "password": "samplepass" }
- Success Response:
201 Created
- Error Responses:
400 Bad Request
,409 Conflict
,500 Internal Server Error
- URL:
/api/v1/login
- Method:
POST
- Description: Authenticate a user, verifies password, generates JWT token, sets an HTTP-only cookie and X-Request-Id header.
- Access: Public
- Request Body:
{ "email": "someone@example.com", "password": "samplepass" }
- Success Response:
200 OK
- Error Responses:
400 Bad Request
,401 Unauthorized
,404 Not Found
,500 Internal Server Error
- URL:
/api/v1/users
- Method:
POST
- Description: Creates a new user with a specific role.
- Access: Admin (can create any role), Agent (can create user or merchant roles)
- Authentication: Required (Bearer Token)
- Request Body:
{ "fullName": "Keanu Reeves", "email": "keanu@example.com", "password": "keanupass", "role": "admin" }
- Success Response:
201 Created
- Error Responses:
400 Bad Request
,401 Unauthorized
,403 Forbidden
,409 Conflict
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets
- Method:
POST
- Access: Admin, Merchant, User
- Authentication: Required (Bearer Token)
- Request Body:
{ "currency": "USD" }
- Success Response:
201 Created
- Error Responses:
400 Bad Request
,401 Unauthorized
,403 Forbidden
,409 Conflict
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/balance
- Method:
GET
- Access: Admin, Agent, Merchant, User (own wallet only)
- Authentication: Required (Bearer Token)
- Success Response:
200 OK
- Error Responses:
401 Unauthorized
,403 Forbidden
,404 Not Found
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/status
- Method:
PATCH
- Access: Admin, Agent, Merchant, User (own wallet only)
- Authentication: Required (Bearer Token)
- Request Body:
{ "status": "inactive" }
- Success Response:
200 OK
- Error Responses:
400 Bad Request
,401 Unauthorized
,403 Forbidden
,404 Not Found
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/cards
- Method:
POST
- Access: Admin, Merchant, User (own wallet only)
- Authentication: Required (Bearer Token)
- Request Body:
{ "cardNumber": "4111111111111111", "provider": "visa", "type": "credit", "expiryDate": "12/25", "cvv": "123" }
- Success Response:
201 Created
- Error Responses:
400 Bad Request
,401 Unauthorized
,403 Forbidden
,404 Not Found
,409 Conflict
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/cards/{card_uuid}
- Method:
GET
- Access: Admin, Agent (read-only), Merchant, User (own cards only)
- Authentication: Required (Bearer Token)
- Success Response:
200 OK
- Error Responses:
401 Unauthorized
,403 Forbidden
,404 Not Found
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/cards/{card_uuid}
- Method:
PATCH
- Access: Admin, Merchant, User (own cards only)
- Authentication: Required (Bearer Token)
- Request Body:
{ "expiryDate": "12/26", "status": "inactive" }
- Success Response:
200 OK
- Error Responses:
400 Bad Request
,401 Unauthorized
,403 Forbidden
,404 Not Found
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/cards/{card_uuid}
- Method:
DELETE
- Access: Admin, Merchant, User (own cards only)
- Authentication: Required (Bearer Token)
- Success Response:
200 OK
- Error Responses:
401 Unauthorized
,403 Forbidden
,404 Not Found
,500 Internal Server Error
- URL:
/api/v1/users/{user_uuid}/wallets/{wallet_uuid}/cards
- Method:
GET
- Access: Admin, Agent (read-only), Merchant, User (own wallet only)
- Authentication: Required (Bearer Token)
- Query Parameters:
provider
(optional): Filter by card providerstatus
(optional): Filter by card status
- Success Response:
200 OK
- Error Responses:
401 Unauthorized
,403 Forbidden
,500 Internal Server Error