A robust, scalable e-commerce backend API built with Go, following clean architecture principles and best practices.
- User Management: Registration, authentication, profile management
- Product Management: CRUD operations, categories, variants, search
- Checkout System: Session-based checkout, add/update/remove items, apply discounts, shipping methods
- Order Processing: Create orders, payment processing, order status tracking
- Payment Integration: Support for multiple payment providers (Stripe, MobilePay, etc.)
- Email Notifications: Order confirmations, status updates
New: The Checkout System now uses a session-based approach with cookies. See Checkout Session Documentation for details.
- Language: Go 1.20+
- Database: SQLite (development) / PostgreSQL (production)
- Authentication: JWT
- Payment Processing: Stripe, MobilePay
- Email: SMTP integration
The project follows clean architecture principles with clear separation of concerns:
├── cmd/ # Application entry points
│ ├── api/ # API server
│ ├── migrate/ # Database migration tool
│ └── seed/ # Database seeding tool
├── config/ # Configuration
├── internal/ # Internal packages
│ ├── api/ # API layer (handlers, middleware, server)
│ ├── application/ # Application layer (use cases)
│ ├── domain/ # Domain layer (entities, repositories interfaces)
│ └── infrastructure/ # Infrastructure layer (repositories implementation, services)
├── migrations/ # Database migrations
├── templates/ # Email templates
└── testutil/ # Testing utilities
- Go 1.20+
- SQLite (for local development) or PostgreSQL 15+ (for production)
- Docker (optional)
For a quick start with Docker Compose:
- Clone the repository:
git clone https://github.com/zenfulcode/commercifygo.git
cd commercify- Start the services using Docker Compose:
docker-compose up -dThis will start:
- PostgreSQL database
- Commercify API server
- Run database migrations (First startup also migrates automatically):
docker-compose exec api /app/commercify-migrate -up- Seed the database with sample data (optional):
docker-compose exec api /app/commercify-seed -all-
Access the API at
http://localhost:6091 -
To stop the services:
docker-compose downCreate a .env file in the root directory by copying the .env.example
cp .env.example .envThe application includes configurable email settings for transactional emails. Configure the following environment variables in your .env file:
# Email Service Configuration
EMAIL_ENABLED=true # Enable/disable email functionality
EMAIL_SMTP_HOST=smtp.example.com # SMTP server hostname
EMAIL_SMTP_PORT=587 # SMTP server port (usually 587 for TLS)
EMAIL_SMTP_USERNAME=username # SMTP authentication username
EMAIL_SMTP_PASSWORD=password # SMTP authentication password
# Email Addresses and Branding
EMAIL_FROM_ADDRESS=noreply@example.com # From address for outgoing emails
EMAIL_FROM_NAME=My Store # From name for outgoing emails
EMAIL_ADMIN_ADDRESS=admin@example.com # Admin email for order notifications
EMAIL_CONTACT_ADDRESS=support@example.com # Customer support contact email (used in templates)
STORE_NAME=My Store # Store name displayed in email templatesEmail Features:
- Order Confirmation: Sent when orders are placed
- Order Shipped: Sent when orders are marked as shipped (with optional tracking)
- Order Notifications: Sent to admin when new orders are received
- Checkout Recovery: Sent to customers who abandon their carts (if implemented)
Template Customization:
Email templates are located in templates/emails/ and can be customized to match your brand.
The application supports both SQLite for local development and PostgreSQL for production.
SQLite is the easiest way to get started with local development:
- Copy the local development environment file:
cp .env.local .env- Run the application:
make dev-sqlite
# or
go run cmd/api/main.goThe SQLite database file (commercify.db) will be created automatically in the project root.
For production or if you prefer PostgreSQL for development:
- Using Docker (Recommended):
# Start PostgreSQL with Docker
make db-start
# Setup database with migrations and seed data
make dev-setup- Manual PostgreSQL Setup:
Create a PostgreSQL user (optional):
createuser -s newuserCreate a PostgreSQL database:
createdb -U newuser commercifyCopy and configure environment file:
cp .env.example .env
# Edit .env and set:
# DB_DRIVER=postgres
# DB_HOST=localhost
# DB_PORT=5432
# DB_USER=your_user
# DB_PASSWORD=your_password
# DB_NAME=commercifyRun migrations:
go run cmd/migrate/main.go -upSeed the database with sample data (optional):
go run cmd/seed/main.go -allThe project includes helpful Make commands for database management:
# SQLite Development
make dev-sqlite # Start with SQLite
make dev-setup-sqlite # Setup SQLite environment
make dev-reset-sqlite # Reset SQLite database
# PostgreSQL Development
make dev-postgres # Start with PostgreSQL
make dev-setup # Setup PostgreSQL environment
make dev-reset # Reset PostgreSQL environment
# Database Operations (PostgreSQL)
make db-start # Start PostgreSQL container
make db-stop # Stop PostgreSQL container
make db-logs # View database logs
make db-clean # Clean database and volumesgo build -o bin/api cmd/api/main.go
./commercifyOr simply:
go run cmd/api/main.goAll protected endpoints require a JWT token in the Authorization header:
Authorization: Bearer <token>
GET /api/checkout- Retrieves the current checkout session for a user. If no checkout exists, a new one will be created.POST /api/checkout/items- Adds a product item to the current checkout session.PUT /api/checkout/items/{productId}Updates the quantity or variant of an item in the current checkout.DELETE /api/checkout/items/{sku}- Removes an item from the current checkout session using SKU.DELETE /api/checkout- Removes all items from the current checkout session.PUT /api/checkout/shipping-addres- Sets the shipping address for the current checkout.PUT /api/checkout/billing-address- Sets the billing address for the current checkout.
POST /api/users/register- Register a new userPOST /api/users/login- Login and get JWT tokenGET /api/users/me- Get current user profilePUT /api/users/me- Update user profilePUT /api/users/me/password- Change passwordGET /api/admin/users- List all users (admin only)GET /api/admin/users/{id}- Get user by ID (admin only)PUT /api/admin/users/{id}/role- Update user role (admin only)PUT /api/admin/users/{id}/deactivate- Deactivate user (admin only)PUT /api/admin/users/{id}/activate- Reactivate user (admin only)
GET /api/admin/products- List products with paginationGET /api/products/{id}- Get product detailsGET /api/products/search- Search productsGET /api/categories- List product categoriesPOST /api/admin/products- Create productPUT /api/admin/products/{id}- Update productDELETE /api/admin/products/{id}- Delete product
POST /api/admin/products/{productId}/variants- Add variantPUT /api/admin/products/{productId}/variants/{variantId}- Update variantDELETE /api/admin/products/{productId}/variants/{variantId}- Delete variant
POST /api/orders- Create order for authenticated userGET /api/orders/{id}- Get order detailsGET /api/orders- List user's ordersPOST /api/orders/{id}/payment- Process payment for user orderPOST /api/orders/{id}/discounts- Apply discount to orderDELETE /api/orders/{id}/discounts- Remove discount from orderGET /api/admin/orders- List all orders (admin only)PUT /api/admin/orders/{id}/status- Update order status (admin only)
GET /api/payment/providers- Get available payment providersPOST /api/admin/payments/{paymentId}/capture- Capture payment (admin only)POST /api/admin/payments/{paymentId}/cancel- Cancel payment (admin only)POST /api/admin/payments/{paymentId}/refund- Refund payment (admin only)
POST /api/shipping/options- Calculate shipping options for address and orderPOST /api/shipping/rates/{id}/cost- Calculate cost for specific shipping ratePOST /api/admin/shipping/methods- Create shipping method (admin only)PUT /api/admin/shipping/methods/{id}- Update shipping method (admin only)POST /api/admin/shipping/zones- Create shipping zone (admin only)PUT /api/admin/shipping/zones/{id}- Update shipping zone (admin only)POST /api/admin/shipping/rates- Create shipping rate (admin only)PUT /api/admin/shipping/rates/{id}- Update shipping rate (admin only)POST /api/admin/shipping/rates/weight- Add weight-based rate (admin only)POST /api/admin/shipping/rates/value- Add value-based rate (admin only)
GET /api/discounts- List active discountsPOST /api/orders/{id}/discounts- Apply discount to orderDELETE /api/orders/{id}/discounts- Remove discount from orderPOST /api/admin/discounts- Create discount (admin only)PUT /api/admin/discounts/{id}- Update discount (admin only)DELETE /api/admin/discounts/{id}- Delete discount (admin only)GET /api/admin/discounts- List all discounts (admin only)
POST /api/webhooks/stripe- Stripe webhook endpointPOST /api/webhooks/mobilepay- MobilePay webhook endpoint
The database consists of the following tables:
users- User accounts and authentication information
categories- Product categories with hierarchical structureproducts- Product information including name, description, price, and stockproduct_variants- Variations of products with different attributes (size, color, etc.)
orders- Customer orders with status, amounts, and addressesorder_items- Individual items in orders
payment_transactions- Record of payment attempts, successes, and failures
discounts- Promotion codes with various discount types and rules
shipping_methods- Available shipping methodsshipping_zones- Geographic shipping zonesshipping_rates- Shipping pricing based on weight, value, or other factors
webhooks- Configuration for external service webhook endpoints
go test ./...go test -cover ./...Install the golang-migrate/migrate tool
brew install migrateusing the cli tool to generate migration files, then you can use the following command, which creates both the files in the right format:
migrate create -ext sql -dir migrations -seq add_friendly_numbersotherwise you can create them manually using:
touch migrations/[sequence]_[migration_name].up.sql
touch migrations/[sequence]_[migration_name].down.sqlWhere migrations is the migrations folder, the sequence is the 6 digits in front and migration_name is a short description
The application supports multiple payment providers through a flexible payment service architecture:
- Stripe: Credit card payments
- MobilePay Mobile payments
- Mock: Test payment provider for development
Payment providers can be enabled/disabled through configuration, and new providers can be added by implementing the PaymentService interface.
The system uses user-friendly identifiers for better readability:
- Order Numbers: Format
ORD-YYYYMMDD-000001(date-based with sequential numbering) - Product Numbers: Format
PROD-000001(sequential numbering)
These identifiers make it easier to reference orders and products in the UI and customer communications.
Commercify implements Stripe as a payment provider following Clean Architecture principles. The implementation:
- Supports credit card payments using Stripe's Payment Intents API
- Handles 3D Secure authentication flows
- Provides webhook integration for asynchronous event handling
- Manages payment lifecycle (authorize, capture, refund, cancel)
- Create a Stripe account at stripe.com
- Get your API keys from the Stripe Dashboard
- Set the following environment variables:
STRIPE_ENABLED=true
STRIPE_SECRET_KEY=sk_test_your_key
STRIPE_PUBLIC_KEY=pk_test_your_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_signing_secret
STRIPE_PAYMENT_DESCRIPTION=Commercify Store Purchase
To handle asynchronous payment events (3D Secure authentication, payment success/failure):
- Create a webhook endpoint in your Stripe Dashboard
- Point it to:
https://your-domain.com/api/webhooks/stripe - Select the following events to listen for:
payment_intent.succeededpayment_intent.payment_failedpayment_intent.canceledpayment_intent.requires_actionpayment_intent.processingpayment_intent.amount_capturable_updatedcharge.succeededcharge.failedcharge.refundedcharge.dispute.createdcharge.dispute.closed
- Copy the signing secret and set it as
STRIPE_WEBHOOK_SECRETin your environment
Commercify supports several payment flows with Stripe:
Direct Payment Payment is authorized and captured immediately.
Authorization and Capture Payment is first authorized, then captured later when the order is fulfilled.
3D Secure Authentication When required by the bank, customers will be redirected to complete 3D Secure authentication.
Use Stripe's test cards for development:
4242 4242 4242 4242- Successful payment4000 0000 0000 3220- 3D Secure authentication required4000 0000 0000 9995- Payment declined
For more test card numbers, visit Stripe's testing documentation.
The project includes useful maintenance commands for database cleanup and optimization:
The system provides two modes for managing expired and old checkouts:
# Regular cleanup (recommended for scheduled runs)
make expire-checkouts
# or
go run ./cmd/expire-checkoutsThis command performs the following operations:
- Marks checkouts with customer/shipping info as abandoned after 15 minutes of inactivity
- Deletes empty checkouts older than 24 hours
- Deletes abandoned checkouts older than 7 days
- Marks remaining expired checkouts as expired (legacy support)
# Force deletion (use with caution)
make force-delete-checkouts
# or
go run ./cmd/expire-checkouts -forceThis command performs aggressive cleanup:
- Force deletes all expired, abandoned, and completed checkouts
- Force deletes checkouts older than 30 days regardless of status
- Should be used carefully as it permanently removes checkout data
# Show help and available options
./bin/expire-checkouts --help
# Regular cleanup (safe for automation)
./bin/expire-checkouts
# Force delete all expired checkouts
./bin/expire-checkouts -forceFor production environments, consider scheduling regular cleanup:
# Example crontab entry (runs every hour)
0 * * * * /path/to/commercify/bin/expire-checkouts
# Example crontab entry for weekly force cleanup (use with caution)
0 2 * * 0 /path/to/commercify/bin/expire-checkouts -force