synor/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-03-BillingEngine.md
Gulshan Yadav 3c9470abba a
2026-01-11 19:05:44 +05:30

483 lines
13 KiB
Markdown

# 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
```