Skip to content
All projects

Backend Engineering · Billing Architecture · Stripe

FileVault Subscription Billing System

A subscription billing workflow for digital media storage, where the monthly price is calculated from the number of stored files using tiered pricing.

PHPLaravelStripeMySQLService classes

This is a private project. The case study is sanitized and illustrative: client names, URLs, data, credentials, and infrastructure details are intentionally omitted.

01Overview

FileVault charges customers monthly based on how many files they store. The price depends on which tier the file count falls into, and each tier has its own cost per file.

The key design goal was to keep the pricing rules easy to reason about and test, and to keep them separate from the code that actually talks to Stripe.

02Key Challenges

Pricing that depends on usage
The monthly amount changes with the file count, so the tier boundaries and per-file costs must be applied the same way every time a price is calculated.
Keeping money logic testable
When pricing is mixed with payment API calls, every test needs a payment provider. Pricing has to be verifiable on its own.
Rules before charges
Invalid inputs or misconfigured tiers must be caught before a billing operation runs, not after a customer is charged.

03My Contributions

  • PHP/Laravel service architecture
  • Dynamic monthly pricing calculations
  • File-count-based pricing tiers
  • Cost-per-file calculation logic
  • Stripe billing integration
  • Monthly recurring billing workflows
  • Pricing validation and business rules
  • Billing configuration and maintainable service design

04Architecture

Pricing is calculated first and passed to billing as a finished result. Billing never works out prices itself.

  1. 1

    File count

    The number of stored files for the billing period is the input.

  2. 2

    Tier resolution

    The count is matched against configured tier boundaries.

  3. 3

    Price calculation

    Cost per file and the monthly total are calculated by a pure pricing service.

  4. 4

    Validation

    The result is checked against business rules before it moves on.

  5. 5

    Billing

    A separate billing service takes the validated result and handles the Stripe recurring charge.

05Engineering Decisions

Pricing is a calculation, billing is a side effect
The pricing service takes a file count and tier configuration and returns a result without calling external services. It can be unit-tested with plain inputs, and billing code only consumes its output.
Tiers as configuration
Tier boundaries and per-file costs live in configuration rather than scattered conditionals, so changing a price does not mean changing control flow.
Validate before charging
Pricing results are validated against business rules before any Stripe operation, so bad input fails early and clearly.
phpillustrative
final class PricingCalculator
{
    public function __construct(private TierTable $tiers) {}

    public function quote(int $fileCount): PriceQuote
    {
        if ($fileCount < 0) {
            throw new InvalidArgumentException('File count cannot be negative.');
        }

        $tier = $this->tiers->forFileCount($fileCount);

        return new PriceQuote(
            fileCount: $fileCount,
            tier: $tier->name,
            costPerFileCents: $tier->costPerFileCents,
            monthlyTotalCents: $fileCount * $tier->costPerFileCents,
        );
    }
}

// Billing consumes the quote. It never calculates prices itself.
$quote = $pricing->quote($account->fileCount());
$billing->chargeMonthly($account, $quote);
Illustrative and simplified: pricing returns a value object and never calls the payment provider.