๐Ÿ“š ACT Environmental - Ordering System Documentation

Complete API and Integration Guide

ACT Phase I - Ordering System

A complete quote-to-project management system with automated Stripe quotes, e-signatures, and payment processing. Built with Python Flask and Firebase, integrated with ACT's existing project management workflow.

> Note: This system uses Stripe for all quote generation and payment processing.

Features

  • ๐Ÿ” Firebase Authentication - Secure user authentication for customers and admins
  • ๐Ÿ“ Quote Requests - Customers submit service requests via web portal
  • ๐Ÿ’ฐ Quote Pricing - Admin sets pricing before sending estimates
  • ๐Ÿ“„ Stripe Quotes - Professional quotes with custom fields (site address, tax parcels, payment terms)
  • โœ๏ธ Dropbox Sign Integration - Electronic signature collection on quotes
  • ๐Ÿงพ Automatic Invoices - Auto-create invoices from quotes (when payment terms = "upon authorization")
  • ๐Ÿ’ณ Stripe Payments - Accept credit card and ACH payments via Stripe
  • ๐Ÿ‘ฅ Client Management - Manage customer data with custom payment terms in Firestore
  • ๐Ÿ“Š Project Integration - Seamlessly integrates with existing `projectManagement` collection
  • ๐ŸŽจ Customer Portal - Web interface for customers to request quotes, view estimates, and pay invoices
  • ๐Ÿ”’ Admin Dashboard - Administrative controls for quote and client management
  • ๐Ÿ”„ Automated Workflow - Quote โ†’ Signature โ†’ Auto-Invoice (if "upon authorization") โ†’ Payment
  • Technology Stack

  • Backend: Python Flask
  • Authentication: Firebase Authentication
  • Database: Google Firestore
  • Quote & Payment Processing: Stripe
  • E-Signature: Dropbox Sign (formerly HelloSign)
  • Deployment: Google Cloud Run (containerized)
  • Frontend: HTML/CSS/JavaScript with Firebase SDK
  • Workflow

    Stripe Quote Created โ†’ Sent for Signature โ†’ Signed โ†’ Accepted โ†’ 
    Payment โ†’ In Progress โ†’ Completed

    Detailed Status Flow:

    1. requested - Customer submits quote request 2. quote_priced - Admin sets pricing 3. estimate_sent / sent - Stripe quote created and sent for signature 4. accepted / approved - Customer signs via Dropbox Sign (auto-updates status) 5. in_progress - Payment received, work begins 6. completed - Project finished 7. cancelled / declined - Cancelled or declined at any stage

    Signature & Payment Flow:

  • When admin prices a quote, a Stripe quote is automatically created with:
  • - Line items with detailed descriptions - Custom fields: Site address, tax parcels, customer project number, payment terms - Professional PDF format
  • The Stripe quote PDF is sent to Dropbox Sign for electronic signature
  • Customer receives email from Dropbox Sign to review and sign
  • Upon signature completion, webhook automatically:
  • - Marks proposal as accepted - Updates order status to accepted - Auto-creates and sends invoice (only if payment terms = "upon authorization") - Activates project in `projectManagement` with status active
  • For other payment terms (Net 30, Net 60, etc.), admin manually sends invoice when ready
  • Invoice Automation:

  • Payment Terms = "upon authorization": Invoice automatically created and sent when quote is signed
  • Other Payment Terms: Admin manually creates invoice via API endpoint when ready
  • All invoices sent via Stripe Invoicing with hosted payment pages
  • Customers can view and pay invoices directly from the customer portal
  • Prerequisites

  • Python 3.11+
  • Firebase project (`act-phase-i`)
  • Stripe account (for quotes and payments)
  • Dropbox Sign account (for e-signatures)
  • Docker (for Cloud Run deployment)
  • Quick Start

    1. Firebase Setup

    Already configured for project `act-phase-i`:

  • โœ… Firebase Authentication enabled
  • โœ… Firestore database created
  • โœ… Service account key: `act-phase-i-firebase-adminsdk-fbsvc-ec0218836c.json`
  • 2. Stripe Setup

    1. Sign up at Stripe 2. Get your API keys from the dashboard (Developers โ†’ API keys) 3. Enable Payment Links and Payment Methods in your Stripe dashboard 4. Update `.env` file with your secret and publishable keys

    3. Dropbox Sign Setup

    1. Sign up at Dropbox Sign 2. Go to API Settings โ†’ Create an API key 3. (Optional) Create a template for proposals with custom fields 4. Configure webhook URL: `https://your-domain.com/api/dropbox-sign/webhook` 5. Update `.env` file

    4. Environment Configuration

    The `.env` file configuration:

    # Firebase
    FIREBASE_PROJECT_ID=act-phase-i
    FIREBASE_CREDENTIALS_PATH=./service-account-key.json/act-phase-i-6644998ca560.json

    Stripe Configuration (Quotes & Payments)

    STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key

    Dropbox Sign Configuration

    DROPBOX_SIGN_API_KEY=your_dropbox_sign_api_key DROPBOX_SIGN_CLIENT_ID=your_client_id # Optional DROPBOX_SIGN_TEST_MODE=true # Set to false for production DROPBOX_SIGN_TEMPLATE_ID=your_template_id # Optional - uses template if provided

    Application

    APP_URL=https://your-domain.com EMAIL_SENDING_ENABLED=true ORDER_CC_EMAIL=karenf@act.earth

    6

    4. Install and Run

    # Install dependencies
    pip install -r requirements.txt

    Run the application

    python3 app.py

    Access the application:

  • API: http://localhost:8080
  • Customer Portal: http://localhost:8080/static/index.html
  • Health Check: http://localhost:8080/health
  • API Endpoints

    Authentication

  • `POST /api/auth/verify` - Verify Firebase token
  • `GET /api/auth/me` - Get current user info + profile
  • `PUT /api/auth/profile` - Update user profile
  • Quotes (Primary Admin Workflow)

  • `POST /api/quotes/by-project//price` - [Main Endpoint] Price project & send Stripe quote + signature request (admin only)
  • - Required: `price` in request body - Automatically creates Stripe quote and sends for signature - Updates status to "proposal pending" - See detailed documentation in Quote Pricing API section below

    Quote Management (Customer Portal)

  • `GET /api/quotes/my-estimates` - Get authenticated user's estimates with status and invoice info
  • `GET /api/quotes/my-invoices` - Get authenticated user's invoices with payment links
  • Orders

  • `GET /api/orders` - Get all orders
  • `GET /api/orders/` - Get specific order
  • `PUT /api/orders/` - Update order
  • `PATCH /api/orders//status` - Update order status
  • `DELETE /api/orders/` - Delete order (admin)
  • Estimates

  • `POST /api/estimates` - Create estimate with PDF
  • `GET /api/estimates` - Get all estimates
  • `GET /api/estimates/` - Get specific estimate
  • `GET /api/estimates//pdf` - Download estimate PDF
  • `POST /api/estimates//approve` - Approve estimate
  • `POST /api/estimates//reject` - Reject estimate
  • Invoices

  • `POST /api/invoices` - Create invoice with PDF
  • `POST /api/invoices/send-from-quote/` - Send invoice from quote (creates if needed) (admin)
  • `POST /api/invoices//send` - Send existing invoice (admin)
  • `GET /api/invoices` - Get all invoices
  • `GET /api/invoices/` - Get specific invoice
  • `GET /api/invoices//pdf` - Download invoice PDF
  • `PATCH /api/invoices//status` - Update invoice status
  • `GET /api/invoices/overdue` - Get overdue invoices (admin)
  • Customer Portal Endpoints

  • `GET /api/quotes/my-estimates` - Get authenticated user's estimates with status and invoice info
  • `GET /api/quotes/my-invoices` - Get authenticated user's invoices with payment links
  • Payments (Stripe)

  • `POST /api/payments/stripe/create-payment-intent` - Create Stripe payment intent
  • `POST /api/payments/stripe/process` - Process Stripe payment
  • `GET /api/payments/transaction/` - Get transaction details
  • `POST /api/payments/refund/` - Refund transaction (admin)
  • Clients

  • `GET /api/clients/` - Get client by ID
  • `GET /api/clients/email/` - Get client by email
  • `GET /api/clients` - Get all clients (admin)
  • `GET /api/clients/search?q=` - Search clients (admin)
  • `POST /api/clients/add` - Create client with Firebase account (admin)
  • `PUT /api/clients/` - Update client (admin can update payment_terms)
  • Webhooks

  • `POST /api/dropbox-sign/webhook` - Handle signature events (called by Dropbox Sign when quotes are signed)
  • `POST /api/stripe/webhook` - Handle Stripe payment events (called by Stripe when invoices are paid)
  • ---

  • `GET /api/dropbox-sign/signature-status/` - Check signature status
  • Events handled:
  • - `signature_request_sent` - Quote sent for signature - `signature_request_viewed` - Client viewed the quote - `signature_request_all_signed` - Auto-marks proposal as accepted + creates invoice (if payment terms = "upon authorization") - `signature_request_declined` - Client declined

    Quote Pricing API (iOS App)

    This is the only endpoint needed to price a project and automatically generate + send a Stripe quote with e-signature.

    Overview

    When you set a price, the system automatically: 1. โœ… Saves the price to the order in Firestore 2. โœ… Creates a professional Stripe quote with all project details 3. โœ… Sends the quote to the client via Dropbox Sign for e-signature 4. โœ… Updates project status to "proposal pending"

    Endpoint

    POST /api/quotes/by-project//price

    Authentication

  • Required: Admin Firebase ID token in `Authorization: Bearer `
  • Permission: Admin role required
  • Request Body

    Minimal (most common):

    {
      "price": 1500
    }

    With Optional Fields:

    {
      "price": 1500,
      "notes": "Includes soil sampling and analysis",
      "valid_days": 14
    }

    Request Parameters

    | Field | Type | Required | Description | |-------|------|----------|-------------| | `price` | number | Yes | Total price for the project in USD | | `notes` | string | No | Internal pricing notes (not shown to client) | | `valid_days` | number | No | Quote validity in days. Only used if order doesn't have `turnaround_days` set. Default: 14 days |

    Success Response (200)

    {
      "success": true,
      "order_id": "abc123",
      "ACTProjectNumber": "11160",
      "status": "proposal pending",
      "stripe_quote_id": "qt_1234567890",
      "stripe_quote_pdf_url": "https://files.stripe.com/v1/...",
      "signature_request_id": "fa123abc456",
      "message": "Quote created and sent successfully"
    }

    Response Fields

    | Field | Type | Description | |-------|------|-------------| | `success` | boolean | Always `true` for successful requests | | `order_id` | string | Firestore order document ID | | `ACTProjectNumber` | string | ACT project number (e.g., "11160") | | `status` | string | New project status: `"proposal pending"` | | `stripe_quote_id` | string | Stripe quote ID for tracking | | `stripe_quote_pdf_url` | string | URL to download the quote PDF | | `signature_request_id` | string | Dropbox Sign signature request ID (null if signature sending failed) | | `message` | string | Success message |

    Error Responses

    404 - Project Not Found:

    {
      "error": "Order not found for ACTProjectNumber"
    }

    400 - Missing Client:

    {
      "error": "Client not found for this order"
    }

    400 - Stripe Not Configured:

    {
      "error": "Stripe not configured"
    }

    400 - Missing Price:

    {
      "error": "Items are required for pricing"
    }

    401 - Unauthorized:

    {
      "error": "Unauthorized"
    }

    403 - Not Admin:

    {
      "error": "Admin access required"
    }

    Example: iOS Swift Request

    func sendQuote(projectNumber: String, price: Double) async throws {
        let url = URL(string: "https://ordering.act.earth/api/quotes/by-project/\(projectNumber)/price")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let body: [String: Any] = ["price": price]
        request.httpBody = try JSONSerialization.data(withJSONObject: body)
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw QuoteError.invalidResponse
        }
        
        if httpResponse.statusCode == 200 {
            let result = try JSONDecoder().decode(QuoteResponse.self, from: data)
            print("โœ… Quote sent! Status: \(result.status)")
        } else {
            let error = try JSONDecoder().decode(ErrorResponse.self, from: data)
            throw QuoteError.apiError(error.error)
        }
    }

    struct QuoteResponse: Codable { let success: Bool let orderId: String let ACTProjectNumber: String let status: String let stripeQuoteId: String let stripeQuotePdfUrl: String? let signatureRequestId: String? let message: String enum CodingKeys: String, CodingKey { case success case orderId = "order_id" case ACTProjectNumber case status case stripeQuoteId = "stripe_quote_id" case stripeQuotePdfUrl = "stripe_quote_pdf_url" case signatureRequestId = "signature_request_id" case message } }

    struct ErrorResponse: Codable { let error: String }

    Example: cURL Request

    curl -X POST https://ordering.act.earth/api/quotes/by-project/11160/price \
      -H "Authorization: Bearer YOUR_FIREBASE_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"price": 1500}'

    What Happens After Sending

    1. Client receives email from Dropbox Sign with the quote PDF to review and sign 2. When client signs: - Order status automatically updates to `"accepted"` - If payment terms = "upon authorization", invoice is auto-created and sent - Project status changes to `"active"` (ready for work) 3. Client can view and pay the invoice from the customer portal

    Important Notes

  • Quote is always sent - There is no option to price without sending. Setting the price automatically creates and sends the quote.
  • Turnaround days - The quote validity period is pulled from the order's `turnaround_days` field (set when the quote request was created). Falls back to 14 days if not set.
  • Project details - All project information (site address, tax parcels, project type, etc.) is automatically pulled from Firestore and included in the quote.
  • Payment terms - Client's payment terms are automatically included in the quote from their Firestore profile.
  • One endpoint only - This is the only endpoint you need for the entire quote workflow. Don't use other deprecated endpoints.
  • Firebase Collections

    `clients`

    {
      client_id: "firebase_uid",
      name: "John Doe",
      email: "john@example.com",
      phone: "+1234567890",
      company: "ACME Corp",
      address: "123 Main St",
      payment_terms: "upon authorization",  // Default payment terms for quotes
      created_at: Timestamp,
      stripe_customer_id: "cus_stripe_id"
    }

    `orders`

    {
      order_id: "uuid",
      ACTProjectNumber: "10891",
      client_id: "firebase_uid",
      client_name: "John Doe",
      address: "Project address",
      tax_parcels: ["Block 123 Lot 45", "Block 123 Lot 46"],
      customer_project_number: "CLIENT-2024-001",  // Optional customer reference
      status: "requested",
      payment_status: "not_required",
      estimate_id: "uuid",
      invoice_id: "uuid",
      items: [{description, quantity, unit_price}],
      
      // Stripe Quote fields
      stripe_quote_id: "qt_stripe_id",
      stripe_quote_pdf_url: "https://...",
      stripe_quote_status: "open",
      stripe_quote_created_at: Timestamp,
      
      // Stripe Invoice fields (auto-populated when quote accepted)
      stripe_invoice_id: "in_stripe_id",
      stripe_invoice_url: "https://...",  // Hosted payment page URL
      stripe_invoice_status: "open",
      stripe_invoice_created_at: Timestamp,
      stripe_invoice_sent_at: Timestamp,
      
      created_at: Timestamp
    }

    `proposals`

    {
      proposal_id: "PROP-123",
      order_id: "uuid",
      client_id: "firebase_uid",
      amount: 2500.00,
      status: "sent",  // sent, accepted, approved, declined
      accepted: false,
      accepted_at: Timestamp,
      approved: false,  // For backward compatibility
      approved_at: Timestamp,
      signature_request_id: "dropbox_sign_request_id",
      signature_status: "pending",  // pending, viewed, signed, declined
      signature_sent_at: Timestamp,
      signature_completed_at: Timestamp,
      stripe_quote_id: "qt_stripe_id",
      created_at: Timestamp
    }

    `projectManagement` (Existing Collection - Integrated)

    {
      ACTProjectNumber: "10891",        // Document ID
      order_id: "uuid",                 // Link to orders
      clientName: "John Doe",
      address: "Project address",
      status: "requested",
      createdBy: "user@email.com",
      createdAt: Timestamp,
      statusUpdatedAt: Timestamp,
      proposal: {},
      database: {},
      inspection: {},
      historicals: {},
      adjacentLots: {},
      report: {},
      files: []
    }

    Deployment to Cloud Run

    Using Deployment Script

    chmod +x deploy.sh
    ./deploy.sh

    Manual Deployment

    # Build and push image
    gcloud builds submit --tag gcr.io/act-phase-i/ordering-system

    Deploy to Cloud Run

    gcloud run deploy ordering-system \ --image gcr.io/act-phase-i/ordering-system \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --set-env-vars FIREBASE_PROJECT_ID=act-phase-i,STRIPE_SECRET_KEY=your-stripe-key

    Project Structure

    Ordering_system/
    โ”œโ”€โ”€ app.py                          # Main Flask application
    โ”œโ”€โ”€ config.py                       # Configuration management
    โ”œโ”€โ”€ requirements.txt                # Python dependencies
    โ”œโ”€โ”€ Dockerfile                      # Container configuration
    โ”œโ”€โ”€ .env                           # Environment variables
    โ”œโ”€โ”€ services/                      # Business logic services
    โ”‚   โ”œโ”€โ”€ firebase_service.py        # Firebase Auth & Firestore
    โ”‚   โ”œโ”€โ”€ order_service.py           # Order & project management
    โ”‚   โ”œโ”€โ”€ client_service.py          # Client management
    โ”‚   โ”œโ”€โ”€ estimate_service.py        # PDF estimate generation
    โ”‚   โ”œโ”€โ”€ invoice_service.py         # PDF invoice generation
    โ”‚   โ”œโ”€โ”€ stripe_service.py          # Stripe quote & payment processing
    โ”‚   โ”œโ”€โ”€ dropbox_sign_service.py    # Dropbox Sign e-signatures
    โ”‚   โ”œโ”€โ”€ payment_service.py
    โ”‚   โ””โ”€โ”€ braintree_service.py
    โ”œโ”€โ”€ routes/                        # API route handlers
    โ”‚   โ”œโ”€โ”€ auth_routes.py             # Authentication endpoints
    โ”‚   โ”œโ”€โ”€ quote_routes.py            # Quote request endpoints
    โ”‚   โ”œโ”€โ”€ order_routes.py            # Order management endpoints
    โ”‚   โ”œโ”€โ”€ estimate_routes.py         # Estimate endpoints
    โ”‚   โ”œโ”€โ”€ invoice_routes.py          # Invoice endpoints
    โ”‚   โ”œโ”€โ”€ client_routes.py           # Client management endpoints
    โ”‚   โ”œโ”€โ”€ dropbox_sign_routes.py     # Dropbox Sign webhook endpoints
    โ”‚   โ”œโ”€โ”€ payment_routes.py
    โ”‚   โ””โ”€โ”€ braintree_routes.py
    โ”œโ”€โ”€ middleware/                    # Authentication middleware
    โ”‚   โ””โ”€โ”€ auth_middleware.py         # JWT token verification
    โ”œโ”€โ”€ static/                        # Frontend files
    โ”‚   โ””โ”€โ”€ index.html                 # Customer portal
    โ””โ”€โ”€ generated_pdfs/                # Generated PDF files

    Key Integrations

    Stripe Quote & Payment System

  • Professional Quotes: Automatically generates detailed quotes with custom fields
  • Payment Collection: Integrated Stripe payment processing after quote acceptance
  • Custom Fields Display:
  • - Site Address: Full property address - Tax Parcels: All associated tax parcels - Customer Project Number: Client's internal reference - Payment Terms: Client-specific payment terms (stored per client) - ACT Project Number: Internal tracking number
  • Line Item: Single line "Phase I ESA" with full description
  • PDF Export: Professional PDF for signature collection
  • Dropbox Sign Integration

  • E-Signature Collection: Sends Stripe quote PDF for electronic signature
  • Webhook Automation: Real-time status updates via webhooks
  • Auto-Acceptance: When signed, automatically:
  • - Updates proposal status to `accepted` - Marks order as `accepted` - Activates project in `projectManagement` collection
  • Template Support: Optional custom template with mapped fields
  • Test Mode: Test signatures without sending actual emails
  • Payment Terms Management

  • Client-Specific: Each client has default payment terms
  • Admin Configurable: Admins can update payment terms in order page
  • Auto-Population: Payment terms automatically pulled for quotes
  • Default Value: "upon authorization" if not specified
  • Visible on Quotes: Displayed prominently in Stripe quote header
  • Stripe Payment Processing

  • Stripe Quotes: Professional quote generation with custom fields
  • Stripe Invoices: Automatic invoice creation from accepted quotes
  • Stripe Payments: Credit card and ACH payment collection
  • Payment Links: Secure Stripe-hosted payment pages
  • Quote-to-Payment Flow: Seamless transition from signed quote to payment
  • Invoice Automation

  • Smart Auto-Invoicing:
  • - If payment terms = "upon authorization": Invoice auto-created and sent when quote is signed - If payment terms = "Net 30", "Net 60", etc.: Admin manually sends invoice when ready
  • Stripe Integration: All invoices created via Stripe Invoicing API
  • Hosted Payment Pages: Customers receive secure Stripe-hosted invoice URLs
  • Email Notifications: Automatic email notifications sent to customers
  • Manual Invoice Control: API endpoint allows admin to send invoice anytime: `POST /api/invoices/send-from-quote/`
  • Customer Portal

  • Estimates Tab: View all quotes/estimates with status, amounts, and PDF downloads
  • Invoices Tab: View all invoices with payment status and direct "Pay Now" links
  • My Orders: Track all project requests and their current status
  • Profile Management: Update company information and contact details
  • Real-time Updates: See quote acceptance status and invoice availability
  • Secure Payment: Click-through to Stripe hosted invoice pages for secure payment
  • Security

  • All API endpoints require Firebase authentication
  • Admin-only endpoints check for admin custom claim
  • Firestore security rules should be configured
  • Environment variables for sensitive data
  • HTTPS enforced in production (Cloud Run)
  • Payment data processed securely via Stripe
  • PCI compliance handled by Stripe
  • Testing

    Test Firebase Connection

    python3 test_firebase.py

    Test Stripe Quote & Dropbox Sign Integration

    python3 test_stripe_quote_signature.py

    Review Project Schema

    python3 review_schema.py

    Troubleshooting

    Permission Errors

    If you get `403 Missing or insufficient permissions`: 1. Go to https://console.cloud.google.com/iam-admin/iam?project=act-phase-i 2. Find service account: `firebase-adminsdk-fbsvc@act-phase-i.iam.gserviceaccount.com` 3. Add roles: Cloud Datastore User and Firebase Admin

    Import Errors

    Make sure all dependencies are installed:
    pip install -r requirements.txt

    Dropbox Sign Webhook Issues

  • Verify webhook URL is publicly accessible (HTTPS required)
  • Check webhook is configured for `signature_request_all_signed` event
  • Test webhook with Dropbox Sign's test event
  • Check server logs for webhook errors
  • Stripe Quote Not Creating

  • Verify `STRIPE_SECRET_KEY` is set correctly
  • Check that customer has valid email address
  • Ensure order has items with pricing
  • Review Stripe dashboard for API errors
  • Documentation

    Detailed integration guides available:

  • `DROPBOX_SIGN_INTEGRATION.md` - E-signature setup and workflow
  • `DROPBOX_SIGN_TEMPLATE_GUIDE.md` - Template configuration
  • `ADMIN_CLIENT_MANAGEMENT.md` - Client management features
  • `QUOTE_TO_ESTIMATE_WORKFLOW.md` - Complete quote workflow
  • Support

    For issues and questions, contact the development team or open an issue in the repository.

    License

    MIT License


    ยฉ 2026 Advanced Cleanup Technologies Inc. | Back to Portal