Skip to main content
Metered billing charges subscribers based on their actual consumption during a billing period rather than a fixed recurring fee. The bill is computed at the end of each period by summing all usage records reported since the last invoice. This model suits API products, cloud infrastructure, data pipelines, and any service where consumption varies significantly between customers or from month to month. Subscribd implements metered plans with rule: 'metered' and a rule_config.unit_price that defines the cost per unit.

Define a metered plan

Set rule to 'metered' and configure the per-unit price in rule_config. The top-level price is 0 because usage drives the entire bill.
config/subscribd.php
'plans' => [
    'compute' => [
        'name'        => 'Compute',
        'description' => 'Pay for what you use. Billed monthly in arrears.',
        'rule'        => 'metered',
        'price'       => 0,             // base price is 0; usage drives the bill
        'currency'    => 'USD',
        'interval'    => 'month',
        'interval_count' => 1,
        'rule_config' => [
            'unit_price' => 2,          // $0.02 per unit
        ],
        'features'    => ['compute_units' => null],
    ],
],
After defining the plan, sync it to the database:
php artisan subscribd:plan sync

Report usage with RecordUsage

Call RecordUsage whenever a unit of consumption occurs — at the end of a job, after handling an API request, or at the close of a processing batch. Subscribd accumulates these records and totals them when the billing period closes.
use Pixelworxio\Subscribd\Actions\RecordUsage;

// At the end of a job, API request, or batch:
app(RecordUsage::class)->execute(
    $subscriptionItem,
    $unitsConsumed,
    idempotencyKey: $jobId,  // prevents duplicate records on retries
);
$subscriptionItem is the SubscriptionItem model associated with the metered plan on the subscriber’s subscription. Retrieve it via $subscription->items()->where('plan_key', 'compute')->firstOrFail().

Idempotency keys

The idempotencyKey parameter prevents duplicate usage records when a job is retried after a failure. Pass a stable, unique identifier for the operation — a job ID, request ID, or event ID all work well. If RecordUsage receives the same key a second time, it records nothing and returns the existing record.
Omitting idempotencyKey is safe for fire-and-forget operations, but any retry logic — queued jobs, webhooks with retries, API clients with automatic retries — should always supply one. Double-counting usage produces incorrect invoices that are difficult to reverse.

When the bill is computed

Subscribd sums all usage records for the period at billing period end and produces the final invoice:
total = sum(units_consumed) × unit_price
For the compute plan at unit_price: 2 (0.02/unit),asubscriberwhoreports50,000unitsinamonthreceivesaninvoicefor0.02/unit), a subscriber who reports 50,000 units in a month receives an invoice for **1,000.00**.
Metered billing is always in arrears — the subscriber pays for the previous period’s consumption, not the current one. This is why the base price is 0; there is no charge at the start of a period.

Usage reporting patterns

Report usage at the end of each discrete unit of work:
use Pixelworxio\Subscribd\Actions\RecordUsage;

class ProcessVideoJob implements ShouldQueue
{
    public function handle(): void
    {
        // ... do the work ...

        app(RecordUsage::class)->execute(
            $this->subscriptionItem,
            1,
            idempotencyKey: $this->job->uuid(),
        );
    }
}