Rust backend for SoraStore Commerce e-commerce platform.
Cargo workspace with 11 domain crates:
- blurp-core — Shared types, config, error handling, response envelope
- blurp-auth — JWT + Redis revocation, magic link, password, OAuth
- blurp-catalog — Products, variants, categories, stock management
- blurp-media — R2/S3 upload, product/variant media
- blurp-cart — Shopping cart, wishlist
- blurp-order — Checkout, orders, guest tracking
- blurp-payment — Xendit integration (invoices, webhooks)
- blurp-shipping — RajaOngkir API, cost calculation, locations
- blurp-admin — Admin endpoints (settings, order management)
- blurp-worker — Background jobs (Redis ZSET delayed queue)
- blurp-server — HTTP server, route composition
- Framework: Axum 0.8
- Database: PostgreSQL 16 (per-crate schemas)
- Cache: Redis 7
- Storage: Cloudflare R2 (S3-compatible)
- Payment: Xendit (sandbox/production)
- Shipping: RajaOngkir API
# Start Postgres + Redis
docker-compose up -d
# Run migrations
make migrate
# Start server
cargo run -p blurp-serverServer runs on http://localhost:8080
All responses use standard envelope: { "data": ..., "error": null/obj, "meta": null/obj }
POST /api/auth/register— Register with email/passwordPOST /api/auth/login— Login, returns JWTPOST /api/auth/logout— Revoke tokenPOST /api/auth/refresh— Refresh access tokenPOST /api/auth/magic-link— Request magic link email
GET /api/catalog/categories— List categoriesPOST /api/catalog/categories— Create category (admin)GET /api/catalog/products— List productsGET /api/catalog/products/{id}— Product detail with variants & mediaPOST /api/catalog/products— Create product (admin)POST /api/catalog/products/{id}/variants— Create variantPOST /api/catalog/products/{id}/media— Upload product mediaPOST /api/catalog/products/{id}/variants/{vid}/media— Upload variant media
GET /api/cart— Get cartPOST /api/cart/items— Add to cartPUT /api/cart/items/{id}— Update quantityDELETE /api/cart/items/{id}— Remove itemDELETE /api/cart— Clear cart
POST /api/orders/shipping-quote— Get shipping optionsPOST /api/orders/checkout— Create order with courier selectionGET /api/orders— List user ordersGET /api/orders/{id}— Order detailPOST /api/orders/guest/lookup— Guest order lookup by token
GET /api/shipping/provinces— List provincesGET /api/shipping/cities— List citiesGET /api/shipping/districts— List districtsPOST /api/shipping/cost— Calculate shipping cost
POST /api/payment/create-invoice— Create Xendit invoicePOST /api/payment/webhook— Handle payment callbackGET /api/payment/orders/{id}— Get payment status
GET /api/admin/settings/shipping— Shipping configGET /api/admin/settings/general— App settingsGET /api/admin/orders— List all ordersPUT /api/admin/orders/{id}/status— Update order statusPUT /api/admin/orders/{id}/fulfillment— Update tracking
- Stock reserved at checkout (15-minute expiry)
- Reserved but not deducted until payment confirmed
- Automatic cleanup of expired reservations
- Insufficient stock error prevents overselling
Orders with email in shipping address generate signed JWT token:
- Token valid 30 days
- SHA256 hash stored in DB for verification
- Lookup via
POST /api/orders/guest/lookup
# Unit tests
cargo test
# E2E tests (server must be running)
./target/debug/blurp-server &
cargo test -p blurp-server -- --test-threads=1E2E test files:
e2e_catalog_test.rs— Catalog listinge2e_cart_checkout_test.rs— Full checkout flowe2e_stock_test.rs— Stock reservatione2e_guest_tracking_test.rs— Guest token verification
Environment variables (or .env.local):
DATABASE_URL=postgres://blurp:blurp@localhost:5433/blurp
REDIS_URL=redis://localhost:6380
BLURP__AUTH__JWT_SECRET=your-secret
BLURP__SHIPPING__RAJAONGKIR_API_KEY=your-key
BLURP__SHIPPING__ORIGIN_DISTRICT_ID=22
BLURP__PAYMENT__XENDIT_API_KEY=your-sandbox-key
BLURP__STORAGE__R2_BUCKET=blurp
BLURP__STORAGE__R2_ACCOUNT_ID=your-account
BLURP__STORAGE__R2_ACCESS_KEY_ID=your-key
BLURP__STORAGE__R2_SECRET_ACCESS_KEY=your-secretProprietary — SoraStore Commerce