Source code for fastapi_payments.pricing.usage_based

"""Usage-based pricing model implementation."""

from .base import PricingStrategy
from typing import Dict, Any, Optional, List


[docs] class UsageBasedPricing(PricingStrategy): """ Usage-based pricing strategy that charges based on consumption. """
[docs] def __init__( self, price_per_unit, minimum_charge=0.0, maximum_charge=None, tax_rate=0.0 ): """ Initialize the usage-based pricing strategy. Args: price_per_unit: Price per unit of usage minimum_charge: Minimum charge regardless of usage maximum_charge: Maximum charge cap (optional) tax_rate: Tax rate """ self.price_per_unit = price_per_unit self.minimum_charge = minimum_charge self.maximum_charge = maximum_charge self.tax_rate = tax_rate
[docs] def calculate_price(self, usage=0, tax_rate=None) -> float: """ Calculate price using usage-based model. Args: usage: Usage units tax_rate: Override default tax rate (optional) Returns: Calculated total price """ tax_rate = tax_rate if tax_rate is not None else self.tax_rate # Calculate base amount from usage amount = usage * self.price_per_unit # Apply minimum charge if needed if amount < self.minimum_charge: amount = self.minimum_charge # Apply maximum charge cap if specified if self.maximum_charge is not None and amount > self.maximum_charge: amount = self.maximum_charge # Apply tax total = amount * (1 + tax_rate) return total
[docs] def get_billing_items(self, usage=0) -> List[Dict[str, Any]]: """ Get itemized billing details. Args: usage: Usage units Returns: List of billing items """ base_amount = usage * self.price_per_unit billable_amount = base_amount items = [] # Create base usage item items.append( { "description": f"Usage ({usage} units)", "quantity": usage, "unit_price": self.price_per_unit, "amount": base_amount, } ) # Add minimum charge adjustment if needed if base_amount < self.minimum_charge: adjustment = self.minimum_charge - base_amount items.append( { "description": "Minimum charge adjustment", "quantity": 1, "unit_price": adjustment, "amount": adjustment, } ) billable_amount = self.minimum_charge # Add maximum charge adjustment if needed if self.maximum_charge is not None and base_amount > self.maximum_charge: adjustment = self.maximum_charge - base_amount items.append( { "description": "Maximum charge adjustment", "quantity": 1, "unit_price": adjustment, "amount": adjustment, } ) billable_amount = self.maximum_charge return items
[docs] def calculate_proration(self, days_used, days_in_period, usage=0) -> float: """ Calculate prorated amount. Args: days_used: Days used in period days_in_period: Total days in period usage: Usage units Returns: Prorated price """ if days_in_period <= 0: return 0.0 # For usage-based, we don't typically prorate based on days # as the billing is directly tied to usage # But we can prorate any minimum charges if self.minimum_charge > 0: prorated_minimum = self.minimum_charge * \ (days_used / days_in_period) usage_amount = usage * self.price_per_unit # Return the greater of prorated minimum or actual usage return max(prorated_minimum, usage_amount) else: # Just return the usage-based amount return usage * self.price_per_unit
[docs] def validate_plan_change( self, current_plan: Dict[str, Any], new_plan: Dict[str, Any] ) -> bool: """ Validate if a plan change is allowed. Args: current_plan: Current plan details new_plan: New plan details to switch to Returns: True if the plan change is allowed, False otherwise """ # Usage-based plans typically allow all changes return True