AuroraMeter.Plans (Aurora Meter v0.4.0)

View Source

Compile-time DSL for declaring billing plans, plus runtime lookups.

Define a plans module:

defmodule MyApp.Plans do
  use AuroraMeter.Plans

  plan :free do
    price 0
    limit :ai_generations, 50, :hard
    feature :api_access, false
  end

  plan :pro do
    price 2_000
    limit :ai_generations, 1_000, :hard
    feature :api_access, true
    feature :seats, 5
  end

  plan :scale do
    price 2_000
    metered :ai_generations, included: 1_000, unit_price: 2
    feature :api_access, true
  end

  plan :payg do
    price 0
    counter :requests
    feature :api_access, true
  end
end

Then point config :aurora_meter, plans: MyApp.Plans. Definitions are validated at compile time (duplicate features, invalid modes, negative numbers all raise).

Summary

Functions

Returns all plans as %{id => Plan.t()} from the configured plans module.

Declares a counter feature: counter :requests.

Declares plain feature access or a plan-level value.

Returns the feature config for feature in plan_id, or nil.

Returns the value of a feature declaration in plan_id, or default.

Returns a single plan by id, or nil.

Declares a hard-capped feature: limit :feature, n, :hard.

Declares a metered feature: metered :feature, included: n, unit_price: cents.

Declares a plan. Contains price, limit, metered, counter and feature calls.

Sets the plan's monthly price in minor units (cents).

Functions

all()

@spec all() :: %{optional(atom()) => AuroraMeter.Plan.t()}

Returns all plans as %{id => Plan.t()} from the configured plans module.

counter(feature)

(macro)

Declares a counter feature: counter :requests.

A counter is measured but never billed and never blocked — the thing to reach for when the money lives somewhere else (a prepaid credit ledger, a usage-based invoice built outside Aurora Meter) and the plan only wants a number on the dashboard. check/2 is always :ok, remaining/2 is :unlimited, and quota/2 reports kind: :counter with limit, included and percent all nil — a counter has no denominator, so there is no bar to draw. See ADR 0006.

feature(name, value)

(macro)

Declares plain feature access or a plan-level value.

feature :api_access, true grants (or, with false, denies) access with no quota behind it. feature :seats, 5 declares a non-negative integer the plan carries for the host to read with AuroraMeter.feature_value/3 (seats, retention days, projects); integer features are always entitled and never metered.

feature_config(plan_id, feature)

@spec feature_config(atom(), atom()) :: AuroraMeter.Plan.feature_config() | nil

Returns the feature config for feature in plan_id, or nil.

feature_value(plan_id, feature, default \\ nil)

@spec feature_value(atom(), atom(), default) ::
  boolean() | non_neg_integer() | default
when default: term()

Returns the value of a feature declaration in plan_id, or default.

Only feature :name, value declarations (booleans and integers) have a value; a limit, a metered feature, an undeclared feature or an unknown plan all return default. To resolve the plan from a tenant use AuroraMeter.feature_value/3.

Examples

iex> AuroraMeter.Plans.feature_value(:pro, :seats)
5

iex> AuroraMeter.Plans.feature_value(:free, :seats, 1)
1

iex> AuroraMeter.Plans.feature_value(:pro, :api_access)
true

get(id)

@spec get(atom()) :: AuroraMeter.Plan.t() | nil

Returns a single plan by id, or nil.

limit(feature, count, mode)

(macro)

Declares a hard-capped feature: limit :feature, n, :hard.

metered(feature, opts)

(macro)

Declares a metered feature: metered :feature, included: n, unit_price: cents.

plan(id, list)

(macro)

Declares a plan. Contains price, limit, metered, counter and feature calls.

price(amount)

(macro)

Sets the plan's monthly price in minor units (cents).