# Auburn Oil Customer Gateway - API A modern, high-performance REST API powering the Auburn Oil customer self-service portal. Built with FastAPI and designed for reliability, security, and ease of deployment. ``` ___ __ ____ _ __ / | __ __/ /_ __ ___________ / __ \(_) / / /| |/ / / / __ \/ / / / ___/ __ \ / / / / / / / ___ / /_/ / /_/ / /_/ / / / / / / / /_/ / / / /_/ |_\__,_/_.___/\__,_/_/ /_/ /_/ \____/_/_/ Customer Gateway API ``` ## What This Does This API serves as the backbone for Auburn Oil's customer portal, enabling: | Feature | Description | |---------|-------------| | **Customer Onboarding** | Multi-step registration for new customers with tank photo uploads | | **Order Management** | Place heating oil delivery orders with real-time pricing | | **Payment Processing** | Secure credit card handling via Authorize.net (PCI-compliant) | | **Account Management** | Password reset, profile updates, delivery history | | **Tank Inspections** | Upload and manage annual tank inspection photos | ## Tech Stack - **Framework**: FastAPI 0.104 (async Python) - **Database**: PostgreSQL with SQLAlchemy 2.0 (async) - **Authentication**: JWT tokens with bcrypt password hashing - **Payments**: Authorize.net CIM (tokenized card storage) - **Server**: Uvicorn ASGI ## Quick Start ### Prerequisites - Docker & Docker Compose - PostgreSQL database (can be remote) - Authorize.net merchant account (sandbox for development) ### 1. Configure Environment Copy the example environment file and customize: ```bash cp .env.example .env.dev ``` Edit `.env.dev` with your settings: ```env # Database POSTGRES_SERVER=your-db-host POSTGRES_PORT=5432 POSTGRES_DBNAME=auburnoil POSTGRES_USERNAME=postgres POSTGRES_PASSWORD=your-secure-password # Security (generate a strong key!) JWT_SECRET_KEY=your-256-bit-secret-key JWT_ALGORITHM=HS256 JWT_ACCESS_TOKEN_EXPIRE_MINUTES=1440 # Payments (use sandbox credentials for dev) AUTH_NET_API_LOGIN_ID=your-login-id AUTH_NET_TRANSACTION_KEY=your-transaction-key # Email (for password resets) SMTP_SERVER=smtp.gmail.com SMTP_PORT=587 SMTP_USERNAME=your-email@gmail.com SMTP_PASSWORD=your-app-password SMTP_FROM_EMAIL=noreply@yourdomain.com # Frontend URL (for password reset links) FRONTEND_URL=http://localhost:3000 ``` ### 2. Deploy with Docker **Development** (with hot-reload): ```bash cd deploy docker compose -f docker-compose.dev.yml up --build ``` **Local Network Testing**: ```bash cd deploy docker compose -f docker-compose.local.yml up --build ``` **Production**: ```bash cd deploy docker compose -f docker-compose.prod.yml up -d --build ``` ### 3. Access the API | Environment | API URL | Docs | |-------------|---------|------| | Development | http://localhost:8000 | http://localhost:8000/docs | | Production | https://api.portal.auburnoil.com | https://api.portal.auburnoil.com/docs | ## API Endpoints ### Authentication (`/auth`) | Method | Endpoint | Description | |--------|----------|-------------| | `POST` | `/auth/login` | Login with email/password, returns JWT | | `POST` | `/auth/register` | Register existing customer (requires account #) | | `POST` | `/auth/new` | New customer single-step registration | | `POST` | `/auth/step1` | New customer wizard - Step 1 (info) | | `POST` | `/auth/step2` | New customer wizard - Step 2 (credentials) | | `POST` | `/auth/step3` | New customer wizard - Step 3 (tank photos) | | `GET` | `/auth/me` | Get current user profile | | `POST` | `/auth/forgot-password` | Request password reset email | | `POST` | `/auth/reset-password` | Complete password reset | | `POST` | `/auth/change-password` | Change password (authenticated) | | `POST` | `/auth/upload-tank-images` | Upload tank inspection photos | | `GET` | `/auth/tank-images/{account}` | Get tank image history | ### Orders (`/order`) | Method | Endpoint | Description | |--------|----------|-------------| | `POST` | `/order/` | Place new delivery order (min 100 gallons) | ### Customer Info (`/info`) | Method | Endpoint | Description | |--------|----------|-------------| | `GET` | `/info/deliveries` | Get delivery history (last 20) | | `GET` | `/info/pricing/current` | Get current oil price per gallon | ### Payments (`/payment`) | Method | Endpoint | Description | |--------|----------|-------------| | `POST` | `/payment/process` | Process payment (saved or new card) | | `GET` | `/payment/cards` | List saved payment methods | | `POST` | `/payment/cards` | Save new card (tokenize) | | `PUT` | `/payment/cards/{id}` | Update card details | | `DELETE` | `/payment/cards/{id}` | Remove saved card | | `POST` | `/payment/cards/{id}/set-default` | Set default payment method | | `POST` | `/payment/sync-billing-info` | Sync billing to Authorize.net | ## Database Setup ### Run Migrations Apply database migrations in order: ```bash # Connect to your PostgreSQL database psql -h your-host -U postgres -d auburnoil # Run migrations \i migrations/20240108_create_portal_user_table.sql \i migrations/20240108_add_tank_image_upload_dates.sql ``` ### Key Tables | Table | Purpose | |-------|---------| | `customer_customer` | Customer profiles and addresses | | `portal_user` | Portal login credentials | | `delivery_delivery` | Delivery orders and history | | `card_card` | Saved payment methods (tokenized) | | `transactions` | Payment transaction records | | `customer_tank` | Tank inspection metadata | | `pricing_oil_oil` | Daily oil pricing | ## Project Structure ``` api/ ├── main.py # FastAPI app entry point ├── config.py # Environment configuration ├── database.py # Database connection (async) ├── models.py # SQLAlchemy ORM models ├── schemas.py # Pydantic request/response schemas ├── routers/ │ ├── auth.py # Authentication endpoints │ ├── info.py # Customer info endpoints │ ├── order.py # Order management │ └── payment.py # Payment processing ├── services/ │ └── authorizenet.py # Authorize.net integration ├── .env.example # Environment template ├── .env.dev # Development config ├── .env.local # Local network config ├── .env.prod # Production config ├── Dockerfile.dev # Development container ├── Dockerfile.local # Local network container ├── Dockerfile.prod # Production container └── requirements.txt # Python dependencies ``` ## Security Features - **JWT Authentication**: Stateless token-based auth with configurable expiration - **Password Hashing**: bcrypt with automatic truncation for compatibility - **PCI Compliance**: Card data never stored locally (Authorize.net tokenization) - **Image Sanitization**: Uploaded images resized, metadata stripped - **CORS Protection**: Environment-specific allowed origins - **Input Validation**: Pydantic schemas validate all inputs ## Environment Variables Reference | Variable | Required | Description | |----------|----------|-------------| | `MODE` | Yes | `DEVELOPMENT`, `LOCAL`, or `PRODUCTION` | | `POSTGRES_SERVER` | Yes | Database host | | `POSTGRES_PORT` | Yes | Database port (usually 5432) | | `POSTGRES_DBNAME` | Yes | Database name | | `POSTGRES_USERNAME` | Yes | Database user | | `POSTGRES_PASSWORD` | Yes | Database password | | `JWT_SECRET_KEY` | Yes | Secret for signing tokens (use 256+ bits) | | `JWT_ALGORITHM` | Yes | Algorithm (HS256 recommended) | | `JWT_ACCESS_TOKEN_EXPIRE_MINUTES` | Yes | Token lifetime in minutes | | `AUTH_NET_API_LOGIN_ID` | Yes | Authorize.net API Login ID | | `AUTH_NET_TRANSACTION_KEY` | Yes | Authorize.net Transaction Key | | `SMTP_SERVER` | Yes | SMTP server for emails | | `SMTP_PORT` | Yes | SMTP port (587 for TLS) | | `SMTP_USERNAME` | Yes | SMTP username | | `SMTP_PASSWORD` | Yes | SMTP password/app password | | `SMTP_FROM_EMAIL` | Yes | From address for emails | | `FRONTEND_URL` | Yes | Frontend URL for reset links | ## Troubleshooting ### Common Issues **Database connection failed** ``` Check POSTGRES_* environment variables Ensure database is accessible from Docker network Verify firewall allows connections on port 5432 ``` **JWT token invalid** ``` Ensure JWT_SECRET_KEY is the same across restarts Check token hasn't expired Verify JWT_ALGORITHM matches what was used to sign ``` **Authorize.net errors** ``` Sandbox vs Production: Use correct credentials for each Test cards: Use Authorize.net test card numbers in sandbox Check API Login ID and Transaction Key are correct ``` **Image upload fails** ``` Max file size: 20MB per image Supported formats: JPEG, PNG Check /images volume is mounted correctly ``` ## Development ### Running Locally (without Docker) ```bash # Create virtual environment python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate # Install dependencies pip install -r requirements.txt # Set environment variables export MODE=DEVELOPMENT # ... set other variables # Run with hot-reload uvicorn main:app --reload --host 0.0.0.0 --port 8000 ``` ### API Documentation FastAPI auto-generates interactive documentation: - **Swagger UI**: http://localhost:8000/docs - **ReDoc**: http://localhost:8000/redoc - **OpenAPI JSON**: http://localhost:8000/openapi.json --- **Auburn Oil Customer Gateway** - Built with reliability in mind for New Hampshire and Massachusetts heating oil customers.