A production-ready Splitwise alternative backend API built with Go, featuring AI-powered receipt parsing, comprehensive expense management, and advanced settlement calculations.
Unwise Backend is a robust, production-grade REST API for managing shared expenses and group finances. Built with Go and following clean architecture principles, it provides a complete solution for expense tracking, settlement calculations, and financial management across multiple groups and users.
- Production-Ready: Comprehensive error handling, rate limiting, security headers, and structured logging
- AI-Powered: Google Gemini integration for receipt scanning and transaction explanations
- Scalable Architecture: Clean separation of concerns with handlers, services, and repositories
- Transaction Safety: Atomic database operations ensuring data integrity
- Modern Tech Stack: Go 1.21+, PostgreSQL, Supabase, Chi router, and structured logging with zap
- Travel Groups: Split expenses during trips and vacations
- Household Management: Track shared household expenses and bills
- Couple Finance: Manage shared expenses between partners
- Friend Groups: Split bills, dinners, and shared activities
- Business Expenses: Track and settle team expenses
The backend follows Clean Architecture principles with clear layer separation:
- Handlers: HTTP request/response handling and validation
- Services: Business logic and transaction orchestration
- Repositories: Data access abstraction
- Models: Domain models and DTOs
- Middleware: Authentication, logging, security, and rate limiting
This architecture ensures maintainability, testability, and scalability while keeping the codebase clean and organized.
- JWT-based Authentication - Supabase JWT token validation with ES256/HS256 support
- Group Management - Create, update, and manage expense groups with multiple members
- Expense Tracking - Full CRUD operations for expenses with multiple split methods
- Settlement Calculations - Automatic calculation of who owes whom with edge list representation
- Receipt Scanning - AI-powered receipt parsing using Google Gemini Vision API
- Transaction Explanations - AI-generated explanations for expenses using Gemini
- Comments & Reactions - Social features for expenses with emoji reactions
- Friends System - Manage friend relationships and view cross-group balances
- Dashboard - Comprehensive user dashboard with metrics and recent activity
- CSV Import - Import expenses from Splitwise CSV exports
- CSV Export - Export group transactions to CSV format
- Placeholder Users - Support for non-registered users with claiming functionality
- Avatar Management - Upload and manage user and group avatars
- Balance Tracking - Real-time balance calculations across all groups
- EQUAL - Split expense equally among all participants
- PERCENTAGE - Split expense by percentage allocation
- ITEMIZED - Assign specific receipt items to specific users
- EXACT_AMOUNT - Specify exact amounts for each participant
- EXPENSE - Regular expense transactions
- REPAYMENT - Repayment transactions between users
- PAYMENT - Payment transactions
- TRIP - Travel/vacation groups
- HOME - Household expense groups
- COUPLE - Couple expense tracking
- OTHER - General purpose groups
- Database Transactions - Atomic operations for data integrity
- Structured Logging - JSON logging with request correlation IDs (zap)
- Rate Limiting - IP-based rate limiting (500 req/min general, 8 req/min for AI endpoints)
- Error Handling - Comprehensive error handling with custom error codes
- CORS Support - Configurable CORS middleware
- Request Timeouts - 60-second request timeout protection
- Graceful Shutdown - Proper server shutdown handling
- Health Checks - Health check endpoint for monitoring
- Language: Go 1.21+
- Framework: Chi (lightweight, idiomatic HTTP router)
- Database: PostgreSQL (Supabase/Neon compatible)
- ORM/Query: pgx/v5 (native PostgreSQL driver)
- Auth: Supabase JWT token validation (ES256/HS256)
- AI: Google Gemini API (Vision & Pro models)
- Storage: Supabase Storage (receipt images, avatars)
- Logging: zap (structured JSON logging)
- Testing: Go testing framework with mocks
.
βββ cmd/
β βββ server/
β βββ main.go # Application entry point, server setup
βββ config/
β βββ config.go # Configuration management (env vars)
βββ database/
β βββ database.go # Database connection pool & transaction management
βββ errors/
β βββ errors.go # Custom error types and error handling
βββ handlers/
β βββ handlers.go # Base handlers, error handling utilities
β βββ group_handlers.go # Group management endpoints
β βββ expense_handlers.go # Expense CRUD endpoints
β βββ receipt_handlers.go # Receipt scanning endpoint
β βββ explanation_handlers.go # AI expense explanation endpoint
β βββ dashboard_handlers.go # Dashboard endpoint
β βββ friend_handlers.go # Friends management endpoints
β βββ comment_handlers.go # Comments & reactions endpoints
β βββ user_handlers.go # User profile endpoints
β βββ avatar_handlers.go # Avatar upload endpoints
β βββ import_handlers.go # CSV import endpoints
βββ middleware/
β βββ auth.go # JWT authentication middleware
β βββ logger.go # Request logging middleware
βββ migrations/
β βββ 001_initial_schema.up.sql
β βββ 002_add_category_to_expenses.up.sql
β βββ 003_add_group_type.up.sql
β βββ 004_add_expense_payers.up.sql
β βββ 005_add_dashboard_indexes.up.sql
β βββ 006_fix_category_constraint.up.sql
β βββ 007_add_avatar_url_to_groups.up.sql
β βββ 008_add_tax_fields_to_expenses.up.sql
β βββ 009_add_explanation_to_expenses.up.sql
β βββ 010_create_friends_table.up.sql
β βββ 011_add_date_to_expenses.up.sql
β βββ 012_fix_date_timezone_shift.up.sql
β βββ 013_add_placeholder_users.up.sql
β βββ 014_add_expense_comments.up.sql
β βββ 015_add_placeholder_claiming.up.sql
βββ models/
β βββ models.go # Data models and types
βββ repository/
β βββ user_repository.go # User data access layer
β βββ group_repository.go # Group data access layer
β βββ expense_repository.go # Expense data access layer
β βββ friend_repository.go # Friend data access layer
β βββ comment_repository.go # Comment data access layer
βββ services/
β βββ user_service.go # User business logic
β βββ group_service.go # Group business logic
β βββ expense_service.go # Expense business logic
β βββ settlement_service.go # Settlement calculation logic
β βββ receipt_service.go # Receipt parsing service
β βββ explanation_service.go # AI explanation service
β βββ dashboard_service.go # Dashboard aggregation service
β βββ friend_service.go # Friend management service
β βββ comment_service.go # Comment management service
β βββ import_service.go # CSV import service
βββ storage/
β βββ storage.go # Storage interface abstraction
β βββ http_client.go # Supabase Storage HTTP client
βββ scripts/
β βββ generate_token/ # JWT token generation utility
β βββ seed/ # Database seeding script
β βββ show_users/ # User listing utility
βββ postman/
β βββ unwise_financial_tests.postman_collection.json # API test collection
βββ go.mod
βββ go.sum
βββ Makefile
βββ README.md
The project follows Clean Architecture principles with clear separation of concerns:
-
Handlers Layer (
handlers/)- HTTP request/response handling
- Input validation
- Error response formatting
- Route registration
-
Services Layer (
services/)- Business logic implementation
- Transaction orchestration
- Authorization checks
- Data aggregation and transformation
-
Repository Layer (
repository/)- Database query execution
- Data access abstraction
- Transaction support via
WithTx()pattern - Batch query optimization
-
Models Layer (
models/)- Domain models and DTOs
- Type definitions and constants
- JSON serialization tags
-
Middleware Layer (
middleware/)- Authentication (JWT validation)
- Request logging
- CORS handling
- Rate limiting
- Dependency Injection - Services and repositories injected via constructors
- Repository Pattern - Data access abstraction for testability
- Transaction Pattern -
WithTx()for atomic operations - Error Wrapping - Custom error types with HTTP status mapping
- Interface Segregation - Small, focused interfaces per service
- Go 1.21 or higher
- PostgreSQL database (Supabase or Neon recommended)
- Google Gemini API key
- Supabase project (for auth and storage)
- Clone the repository:
git clone <repository-url>
cd unwise-backend- Install dependencies:
go mod download- Set up environment variables:
Create a .env file in the root directory:
# Server Configuration
PORT=8080
ENV=development
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
MAX_BODY_SIZE=1048576
# Database
DATABASE_URL=postgresql://user:password@host:5432/dbname
# Supabase Configuration
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_JWT_SECRET=your-jwt-secret
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Storage Configuration
SUPABASE_STORAGE_BUCKET=receipts
SUPABASE_STORAGE_URL=https://your-project.supabase.co/storage/v1
SUPABASE_GROUP_PHOTOS_BUCKET=group-photos
SUPABASE_USER_AVATARS_BUCKET=user-avatars
# AI Services
GEMINI_API_KEY=your-gemini-api-key- Run database migrations:
# Install migrate tool if not already installed
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
# Run all migrations
make migrate-up
# Or manually
migrate -path migrations -database "$DATABASE_URL" up- Seed the database (optional):
make seedThis creates 6 test users in both Supabase Auth and your database:
- alice@example.com
- bob@example.com
- charlie@example.com
- diana@example.com
- eve@example.com
- frank@example.com
All test users have password: TestPassword123!
- Start the server:
make run
# or
go run cmd/server/main.goThe server will start on port 8080 (or the port specified in your .env file).
All endpoints except /health require a Bearer token in the Authorization header:
Authorization: Bearer <jwt-token>
GET /health- Health check endpoint
GET /api/dashboard- Get user dashboard with metrics, groups, and recent activity
GET /api/user/me- Get current user profilePOST /api/user/avatar- Upload user avatarDELETE /api/user/me- Delete user account (requires zero balance)GET /api/user/placeholders- Get claimable placeholder usersPOST /api/user/placeholders/{placeholderID}/claim- Claim a placeholder as yourselfPOST /api/user/placeholders/{placeholderID}/assign- Assign placeholder to existing user
GET /api/groups- Get all groups for authenticated user (with balances)POST /api/groups- Create a new group{ "name": "Trip to Japan", "type": "TRIP", "member_emails": ["alice@example.com", "bob@example.com"] }GET /api/groups/{groupID}- Get specific group detailsPUT /api/groups/{groupID}- Update group nameDELETE /api/groups/{groupID}- Delete group (requires zero balances)
POST /api/groups/{groupID}/members- Add member by emailPOST /api/groups/{groupID}/placeholders- Add placeholder memberDELETE /api/groups/{groupID}/members/{userID}- Remove member (requires zero balance)
GET /api/groups/{groupID}/expenses- Get all expenses in groupGET /api/groups/{groupID}/transactions- Get all transactions (expenses + settlements)GET /api/groups/{groupID}/balances- Get balance edge list (who owes whom)GET /api/groups/{groupID}/settlements- Get settlement suggestionsGET /api/groups/{groupID}/export- Export group transactions as CSVPOST /api/groups/{groupID}/avatar- Upload group avatar
POST /api/groups/{groupID}/settle- Create a settlement transaction{ "payer_id": "user-id-1", "receiver_id": "user-id-2", "amount": 50.00 }
POST /api/expenses- Create a new expense{ "group_id": "group-uuid", "total_amount": 100.00, "description": "Dinner at restaurant", "split_method": "EQUAL", "type": "EXPENSE", "splits": [ {"user_id": "user-1", "amount": 50.00}, {"user_id": "user-2", "amount": 50.00} ], "payers": [ {"user_id": "user-1", "amount_paid": 100.00} ], "date": "2024-01-15T19:30:00Z", "tax": 10.00, "cgst": 5.00, "sgst": 5.00, "service_charge": 5.00 }GET /api/expenses/{expenseID}- Get specific expense detailsPUT /api/expenses/{expenseID}- Update expenseDELETE /api/expenses/{expenseID}- Delete expense
GET /api/expenses/{expenseID}/comments- Get all comments for expensePOST /api/expenses/{expenseID}/comments- Create a comment{ "text": "Great dinner!" }DELETE /api/expenses/{expenseID}/comments/{commentID}- Delete your own comment
POST /api/expenses/{expenseID}/comments/{commentID}/reactions- Add emoji reaction{ "emoji": "π" }DELETE /api/expenses/{expenseID}/comments/{commentID}/reactions- Remove reaction
GET /api/friends- Get all friends with cross-group balancesGET /api/friends/search- Search for potential friends by email/namePOST /api/friends- Add a friend{ "friend_id": "user-uuid" }DELETE /api/friends/{friendID}- Remove a friend
POST /api/scan-receipt- Upload and parse receipt image- Content-Type:
multipart/form-data - Field name:
image - Returns: Parsed receipt data with items, tax breakdown, and total
- Rate limited: 8 requests per minute per IP
- Content-Type:
POST /api/expenses/explain- Generate AI explanation for expense{ "transaction_id": "expense-uuid" }- Rate limited: 8 requests per minute per IP
POST /api/groups/{groupID}/import/splitwise/preview- Preview Splitwise CSV import- Content-Type:
multipart/form-data - Field name:
file(CSV file) - Returns: Preview of expenses that will be imported
- Content-Type:
POST /api/groups/{groupID}/import/splitwise- Import Splitwise CSV- Content-Type:
multipart/form-data - Fields:
file: CSV filemember_mapping: JSON mapping of Splitwise users to your users
{ "Splitwise User 1": "user-uuid-1", "Splitwise User 2": "user-uuid-2" }- Content-Type:
- JWT Authentication - Supabase JWT validation with ES256/HS256 support
- Rate Limiting - IP-based rate limiting (500 req/min general, 8 req/min AI endpoints)
- Security Headers - X-Content-Type-Options, X-Frame-Options, CSP, Referrer-Policy, X-XSS-Protection
- HSTS - Strict-Transport-Security enabled in production for HTTPS enforcement
- Request Body Size Limit - 1MB default limit to prevent memory exhaustion attacks
- CORS Protection - Configurable CORS middleware with production warnings
- Request Timeouts - 60-second timeout to prevent resource exhaustion
- Input Validation - Comprehensive validation for all inputs (UUID format, string length limits)
- Authorization Checks - Group membership verification for all operations
- Error Sanitization - User-friendly error messages without exposing internals
make testmake build# Apply all migrations
make migrate-up
# Rollback last migration
make migrate-down
# Create new migration
migrate create -ext sql -dir migrations -seq <migration_name># Format code
make fmt
# Run linter
make lint
# Run all checks
make checkusers- User accounts and profilesgroups- Expense groupsgroup_members- Group membership (many-to-many)expenses- Expense transactionsexpense_splits- How expense is split among usersexpense_payers- Who paid for the expensereceipt_items- Individual items from receipt scanningreceipt_item_assignments- Item-to-user assignmentscomments- Comments on expensescomment_reactions- Emoji reactions on commentsfriends- Friend relationships
- Users β Groups: Many-to-many via
group_members - Groups β Expenses: One-to-many
- Expenses β Splits: One-to-many
- Expenses β Payers: One-to-many
- Expenses β Comments: One-to-many
- Users β Friends: Many-to-many via
friendstable
All multi step operations use database transactions for atomicity:
- Expense Creation - Creates expense, splits, payers, and receipt items atomically
- Expense Updates - Updates expense and recreates splits/payers atomically
- Group Creation - Creates group and members atomically
- Settlement Creation - Creates settlement expense atomically
Transactions are managed via the database.WithTx() pattern, ensuring data integrity even on failures.
The API uses a comprehensive error handling system:
- Custom Error Types - Structured error codes (e.g.,
VALIDATION_001,NOT_FOUND_004) - HTTP Status Mapping - Automatic mapping of error types to HTTP status codes
- User-Friendly Messages - Clean error messages for clients
- Structured Logging - All errors logged with context and request IDs
{
"error": "User-friendly error message",
"code": "ERROR_CODE",
"details": "Additional context (optional)"
}- **Database transactions for data integrity
- **Structured JSON logging with request correlation IDs
- **Rate limiting to prevent abuse
- **Request timeouts
- **Graceful shutdown
- **CORS configuration
- **Error handling framework
- Pagination for list endpoints (GetExpenses, GetGroups, etc.)
- Row-level security in database queries
- Soft deletes for audit trails
- Enhanced health check with DB connectivity
- Context timeouts per handler
Copyright (c) 2024 Tanush Govind
All rights reserved.
This project, including all source code, design elements, styling, and documentation, is proprietary and confidential.
1. RESTRICTIONS You may NOT:
- Use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of this software
- Use the design, styling, or UI components for commercial or personal projects
- Remove or alter any copyright notices
- Create derivative works based on this codebase
2. PERMITTED USES You MAY:
- View the source code for educational purposes
- Reference this project in your portfolio with proper attribution
- Fork this repository for personal learning (not for redistribution)
3. ATTRIBUTION REQUIREMENT If you reference, derive inspiration from, or use any portion of this codebase in any form (including but not limited to code, design patterns, UI components, or styling), you must:
- Provide clear and visible attribution to the original author
- Include a link to this repository
- State that your work is "inspired by" or "based on" this project
4. COMMERCIAL USE Commercial use of any part of this codebase is strictly prohibited without explicit written permission from the copyright holder.
5. SCOPE This license applies to the entire codebase, including but not limited to:
- All source code files
- UI/UX design and styling
- Component architecture and patterns
- Documentation and README files
For licensing inquiries, please contact tanush1912@gmail.com
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- Makefile - Common development commands
- Migrations - Database migration files