# Milestone 3: Billing Engine

> Generate invoices, process payments, and manage account credits.

---

## Overview

The Billing Engine transforms metered usage into invoices, processes SYNOR payments, and manages prepaid balances and credits.

---

## Components

### 3.1 Invoice Generator

```rust
// crates/synor-economics/src/billing/invoice.rs

/// Invoice for a billing period
pub struct Invoice {
    /// Invoice ID
    pub invoice_id: InvoiceId,
    /// Account ID
    pub account_id: AccountId,
    /// Billing period
    pub period_start: u64,
    pub period_end: u64,
    /// Line items
    pub line_items: Vec<LineItem>,
    /// Subtotal before discounts
    pub subtotal_micro_synor: u64,
    /// Applied discounts
    pub discounts: Vec<AppliedDiscount>,
    /// Credits applied
    pub credits_applied: u64,
    /// Total due
    pub total_due_micro_synor: u64,
    /// Invoice status
    pub status: InvoiceStatus,
    /// Due date
    pub due_date: u64,
    /// Payment transaction (if paid)
    pub payment_tx: Option<TxId>,
    /// Created at
    pub created_at: u64,
}

pub struct LineItem {
    pub service: ServiceType,
    pub resource: ResourceType,
    pub description: String,
    pub quantity: u64,
    pub unit: MeteringUnit,
    pub unit_price_micro_synor: u64,
    pub amount_micro_synor: u64,
}

pub struct AppliedDiscount {
    pub discount_type: DiscountType,
    pub description: String,
    pub amount_micro_synor: u64,
}

pub enum DiscountType {
    /// Volume discount based on usage tier
    Volume { tier: u8 },
    /// Commitment discount (reserved capacity)
    Commitment { commitment_id: CommitmentId },
    /// Promotional credit
    Promotional { promo_code: String },
    /// Referral credit
    Referral { referrer_id: AccountId },
}

pub enum InvoiceStatus {
    Draft,
    Finalized,
    Sent,
    Paid,
    Overdue,
    Voided,
}

pub struct InvoiceGenerator {
    metering: UsageAggregator,
    pricing: PricingEngine,
    discounts: DiscountEngine,
}

impl InvoiceGenerator {
    /// Generate invoice for an account
    pub async fn generate_invoice(
        &self,
        account: &AccountId,
        period_start: u64,
        period_end: u64,
    ) -> Result<Invoice, BillingError> {
        // Get usage summary
        let usage = self.metering.get_usage_summary(account, period_start, period_end).await?;

        // Convert to line items
        let line_items = self.usage_to_line_items(&usage);

        // Calculate subtotal
        let subtotal = line_items.iter().map(|li| li.amount_micro_synor).sum();

        // Apply discounts
        let discounts = self.discounts.calculate_discounts(account, &line_items).await?;
        let discount_total: u64 = discounts.iter().map(|d| d.amount_micro_synor).sum();

        // Apply credits
        let available_credits = self.get_available_credits(account).await?;
        let credits_applied = std::cmp::min(available_credits, subtotal - discount_total);

        // Calculate total
        let total_due = subtotal.saturating_sub(discount_total).saturating_sub(credits_applied);

        Ok(Invoice {
            invoice_id: InvoiceId::new(),
            account_id: account.clone(),
            period_start,
            period_end,
            line_items,
            subtotal_micro_synor: subtotal,
            discounts,
            credits_applied,
            total_due_micro_synor: total_due,
            status: InvoiceStatus::Draft,
            due_date: period_end + 30 * 24 * 3600, // 30 days
            payment_tx: None,
            created_at: current_timestamp(),
        })
    }
}
```

### 3.2 Payment Processor

```rust
// crates/synor-economics/src/billing/payment.rs

/// Payment processor for SYNOR token payments
pub struct PaymentProcessor {
    /// Synor blockchain client
    blockchain: SynorClient,
    /// Billing contract address
    billing_contract: Address,
}

pub struct PaymentRequest {
    pub invoice_id: InvoiceId,
    pub amount_micro_synor: u64,
    pub payer: Address,
}

pub struct PaymentResult {
    pub payment_id: PaymentId,
    pub tx_id: TxId,
    pub amount_micro_synor: u64,
    pub timestamp: u64,
    pub status: PaymentStatus,
}

pub enum PaymentStatus {
    Pending,
    Confirmed,
    Failed { reason: String },
}

impl PaymentProcessor {
    /// Process payment for an invoice
    pub async fn process_payment(
        &self,
        request: PaymentRequest,
    ) -> Result<PaymentResult, PaymentError> {
        // Verify invoice exists and is payable
        let invoice = self.get_invoice(&request.invoice_id).await?;
        if invoice.status != InvoiceStatus::Finalized && invoice.status != InvoiceStatus::Sent {
            return Err(PaymentError::InvoiceNotPayable);
        }

        // Build payment transaction
        let tx = self.blockchain.build_transaction(
            TransactionType::Transfer {
                to: self.billing_contract,
                amount: request.amount_micro_synor,
                memo: format!("Invoice payment: {}", request.invoice_id),
            },
            request.payer,
        )?;

        // Submit and wait for confirmation
        let tx_id = self.blockchain.submit_transaction(tx).await?;
        let confirmation = self.blockchain.wait_for_confirmation(&tx_id).await?;

        // Update invoice status
        self.mark_invoice_paid(&request.invoice_id, &tx_id).await?;

        Ok(PaymentResult {
            payment_id: PaymentId::new(),
            tx_id,
            amount_micro_synor: request.amount_micro_synor,
            timestamp: current_timestamp(),
            status: PaymentStatus::Confirmed,
        })
    }

    /// Set up auto-pay for an account
    pub async fn setup_auto_pay(
        &self,
        account: &AccountId,
        funding_source: Address,
        max_amount_per_invoice: u64,
    ) -> Result<AutoPayConfig, PaymentError> {
        // Verify funding source has sufficient balance/allowance
        // Store auto-pay configuration
        Ok(AutoPayConfig {
            account_id: account.clone(),
            funding_source,
            max_amount_per_invoice,
            enabled: true,
        })
    }
}
```

### 3.3 Credit System

```rust
// crates/synor-economics/src/billing/credit.rs

/// Account credit management
pub struct CreditManager {
    storage: CreditStorage,
}

pub struct AccountCredit {
    pub account_id: AccountId,
    pub balance_micro_synor: u64,
    pub credits: Vec<CreditEntry>,
}

pub struct CreditEntry {
    pub credit_id: CreditId,
    pub amount_micro_synor: u64,
    pub remaining_micro_synor: u64,
    pub source: CreditSource,
    pub expires_at: Option<u64>,
    pub created_at: u64,
}

pub enum CreditSource {
    /// Prepaid balance deposit
    Prepaid { tx_id: TxId },
    /// Promotional credit
    Promotional { campaign: String },
    /// Referral bonus
    Referral { referred_by: AccountId },
    /// SLA credit (compensation)
    SlaCredit { incident_id: String },
    /// Manual adjustment
    Manual { reason: String, admin: Address },
}

impl CreditManager {
    /// Add credits to an account
    pub async fn add_credits(
        &self,
        account: &AccountId,
        amount: u64,
        source: CreditSource,
        expires_at: Option<u64>,
    ) -> Result<CreditEntry, CreditError> {
        let entry = CreditEntry {
            credit_id: CreditId::new(),
            amount_micro_synor: amount,
            remaining_micro_synor: amount,
            source,
            expires_at,
            created_at: current_timestamp(),
        };

        self.storage.add_credit(account, &entry).await?;
        Ok(entry)
    }

    /// Get available credit balance
    pub async fn get_balance(&self, account: &AccountId) -> Result<u64, CreditError> {
        let credits = self.storage.get_credits(account).await?;
        let now = current_timestamp();

        let balance = credits.iter()
            .filter(|c| c.expires_at.map(|e| e > now).unwrap_or(true))
            .map(|c| c.remaining_micro_synor)
            .sum();

        Ok(balance)
    }

    /// Apply credits to an invoice
    pub async fn apply_credits(
        &self,
        account: &AccountId,
        amount: u64,
        invoice_id: &InvoiceId,
    ) -> Result<u64, CreditError> {
        let mut credits = self.storage.get_credits(account).await?;
        let now = current_timestamp();

        // Sort by expiration (FIFO for expiring credits)
        credits.sort_by_key(|c| c.expires_at.unwrap_or(u64::MAX));

        let mut remaining = amount;
        let mut applied = 0u64;

        for credit in credits.iter_mut() {
            if remaining == 0 {
                break;
            }

            // Skip expired credits
            if credit.expires_at.map(|e| e <= now).unwrap_or(false) {
                continue;
            }

            let apply_amount = std::cmp::min(credit.remaining_micro_synor, remaining);
            credit.remaining_micro_synor -= apply_amount;
            remaining -= apply_amount;
            applied += apply_amount;

            // Record usage
            self.storage.record_credit_usage(
                &credit.credit_id,
                apply_amount,
                invoice_id,
            ).await?;
        }

        Ok(applied)
    }
}
```

### 3.4 Prepaid Balance

```rust
// crates/synor-economics/src/billing/prepaid.rs

/// Prepaid balance management (pay-as-you-go with deposits)
pub struct PrepaidManager {
    credits: CreditManager,
    blockchain: SynorClient,
    deposit_address: Address,
}

impl PrepaidManager {
    /// Deposit SYNOR to prepaid balance
    pub async fn deposit(
        &self,
        account: &AccountId,
        amount: u64,
        from: Address,
    ) -> Result<DepositResult, PrepaidError> {
        // Build deposit transaction
        let tx = self.blockchain.build_transaction(
            TransactionType::Transfer {
                to: self.deposit_address,
                amount,
                memo: format!("Prepaid deposit: {}", account),
            },
            from,
        )?;

        // Submit and confirm
        let tx_id = self.blockchain.submit_transaction(tx).await?;
        self.blockchain.wait_for_confirmation(&tx_id).await?;

        // Add to credits
        self.credits.add_credits(
            account,
            amount,
            CreditSource::Prepaid { tx_id: tx_id.clone() },
            None, // Prepaid credits don't expire
        ).await?;

        Ok(DepositResult {
            tx_id,
            amount,
            new_balance: self.credits.get_balance(account).await?,
        })
    }

    /// Check if account has sufficient balance
    pub async fn has_sufficient_balance(
        &self,
        account: &AccountId,
        required: u64,
    ) -> Result<bool, PrepaidError> {
        let balance = self.credits.get_balance(account).await?;
        Ok(balance >= required)
    }

    /// Deduct from prepaid balance (for real-time billing)
    pub async fn deduct(
        &self,
        account: &AccountId,
        amount: u64,
        reason: &str,
    ) -> Result<DeductResult, PrepaidError> {
        let balance = self.credits.get_balance(account).await?;

        if balance < amount {
            return Err(PrepaidError::InsufficientBalance {
                available: balance,
                required: amount,
            });
        }

        // Create a temporary invoice for the deduction
        let invoice_id = InvoiceId::new_instant();
        self.credits.apply_credits(account, amount, &invoice_id).await?;

        Ok(DeductResult {
            amount_deducted: amount,
            remaining_balance: self.credits.get_balance(account).await?,
        })
    }
}
```

---

## Tasks

- [ ] Implement Invoice data model and generator
- [ ] Build PaymentProcessor for SYNOR payments
- [ ] Implement CreditManager for account credits
- [ ] Build PrepaidManager for deposit-based billing
- [ ] Create billing smart contract
- [ ] Implement auto-pay functionality
- [ ] Add invoice email/notification system
- [ ] Build billing dashboard API
- [ ] Implement usage alerts and spending limits

---

## Validation Commands

```bash
# Check account balance
synor billing balance

# Get current invoice
synor billing invoice --current

# View invoice history
synor billing invoices --limit 10

# Add prepaid balance
synor billing deposit 100 SYNOR

# Set up auto-pay
synor billing auto-pay enable --max 500

# Export usage report
synor billing export --format csv --period 2026-01
```

---

## API Endpoints

```
GET  /api/v1/billing/balance          - Get account balance
GET  /api/v1/billing/invoices         - List invoices
GET  /api/v1/billing/invoices/:id     - Get invoice details
POST /api/v1/billing/invoices/:id/pay - Pay invoice
GET  /api/v1/billing/usage            - Get usage summary
POST /api/v1/billing/deposit          - Deposit prepaid balance
GET  /api/v1/billing/credits          - List credits
POST /api/v1/billing/auto-pay         - Configure auto-pay
```
