An open API service indexing awesome lists of open source software.

https://github.com/ashtishad/xpay

xPay is a digital wallet backend built with Go. It provides a secure foundation for digital financial transactions, including user-auth service, wallet service, card service and multiple payment gateway integrations.
https://github.com/ashtishad/xpay

aws docker domain-driven-design go golang postgresql

Last synced: 3 months ago
JSON representation

xPay is a digital wallet backend built with Go. It provides a secure foundation for digital financial transactions, including user-auth service, wallet service, card service and multiple payment gateway integrations.

Awesome Lists containing this project

README

          

## xPay: Digital Wallet

## Table of Contents
- [Quick Start](#quick-start)
- [Tech Stack](#tech-stack)
- [Progress](#progress)
- [Architecture and Request Flow](#architecture-and-request-flow)
- [Directory Structure](#directory-structure)
- [API Documentation](#api-documentation)

## Quick Start

This project supports two environments: Production and Development. Docker Desktop is required to run the application.

Clone the repository:
```bash
git clone git@github.com:ashtishad/xpay.git && cd xpay
```

### Production
For users who want to run the project and interact with the API (no code changes):
```bash
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
```

### Development
For developers who intend to modify the code and contribute to the project:
```bash
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` and `setup-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](https://github.com/ashtishad/xpay/wiki)**

## Tech Stack



Skills

**Core Libraries:** [Gin](https://github.com/gin-gonic/gin), [pgx](https://github.com/jackc/pgx), [golang-migrate](https://github.com/golang-migrate/migrate), [golang-jwt](https://github.com/golang-jwt/jwt/), [viper](https://github.com/spf13/viper), [swaggo/swag](https://github.com/swaggo/swag), [golangci-lint](https://golangci-lint.run/), and [testify](https://github.com/stretchr/testify).

## Progress

βœ… 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 | βœ…
βœ…
πŸ”„
πŸ”„
πŸ”„ |

Back to Top

## Architecture and Request Flow:

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)

1. Domain Models: `internal/domain/*.go`
2. Repositories: `internal/domain/*_repository.go`
3. HTTP Handlers: `internal/server/handlers/*.go`
4. DTOs: `internal/server/dto/*.go`
5. Routes: `internal/server/routes/*.go`

Back to Top

## Directory Structure

command: `tree -a -I '.git|.DS_Store|.gitignore|.idea|.vscode|docs'`

```bash
β”œβ”€β”€ .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
```

Back to Top

## API Documentation

### Authentication Endpoints

#### Register User
- **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**:
```json
{
"fullName": "John Doe",
"email": "someone@example.com",
"password": "samplepass"
}
```
- **Success Response**: `201 Created`
- **Error Responses**: `400 Bad Request`, `409 Conflict`, `500 Internal Server Error`

#### Login
- **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**:
```json
{
"email": "someone@example.com",
"password": "samplepass"
}
```
- **Success Response**: `200 OK`
- **Error Responses**: `400 Bad Request`, `401 Unauthorized`, `404 Not Found`, `500 Internal Server Error`

### User Management Endpoints

#### Create User with Specific Role
- **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**:
```json
{
"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`

### Wallet Endpoints

#### Create a New Wallet
- **URL**: `/api/v1/users/{user_uuid}/wallets`
- **Method**: `POST`
- **Access**: Admin, Merchant, User
- **Authentication**: Required (Bearer Token)
- **Request Body**:
```json
{
"currency": "USD"
}
```
- **Success Response**: `201 Created`
- **Error Responses**: `400 Bad Request`, `401 Unauthorized`, `403 Forbidden`, `409 Conflict`, `500 Internal Server Error`

#### Get Wallet Balance
- **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`

#### Update Wallet Status
- **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**:
```json
{
"status": "inactive"
}
```
- **Success Response**: `200 OK`
- **Error Responses**: `400 Bad Request`, `401 Unauthorized`, `403 Forbidden`, `404 Not Found`, `500 Internal Server Error`

### Card Endpoints

#### Add a New Card to Wallet
- **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**:
```json
{
"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`

#### Get Card Details
- **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`

#### Update Card Details
- **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**:
```json
{
"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`

#### Delete Card
- **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`

#### List Cards
- **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 provider
- `status` (optional): Filter by card status
- **Success Response**: `200 OK`
- **Error Responses**: `401 Unauthorized`, `403 Forbidden`, `500 Internal Server Error`

[Back to Top](#top)