hawk_ex

HawkEx is an Elixir toolkit for SaaS billing infrastructure. It provides Ecto schemas and service modules for plans, subscriptions, entitlements, audit logging, CSV exports, and lightweight operational queries.

The library is designed to live inside a Phoenix application. Your app owns the account schema, payment provider integration, authentication, and usage tracking. HawkEx owns the durable billing records and the small APIs needed to answer common questions such as "does this account have access?" and "what is left on this plan?"

Features

  • Plan and subscription records with trial, active, past due, and canceled states.
  • Entitlement checks for boolean and limit-based features.
  • Optional PubSub events for subscription lifecycle changes.
  • Audit log records for billing events and application-defined actions.
  • Synchronous CSV exports and optional async exports through Oban.
  • Pluggable CSV storage, with local disk and S3 adapters.
  • Shared pagination helpers for dashboard-style views.

Dashboard

You can use hawk_ex_dashboard alongside HawkEx to add an admin-facing Phoenix dashboard. It provides a UI for viewing and managing subscriptions, entitlements, audit logs, CSV exports, and related operational data powered by this library.

Installation

Add hawk_ex to your dependencies:

def deps do
  [
    {:hawk_ex, "~> 0.1.0"}
  ]
end

Then copy the migrations:

mix hawk_ex.install
mix ecto.migrate

Configuration

At minimum, configure the Ecto repo and the schema that represents the billable account in your application:

config :hawk_ex,
  repo: MyApp.Repo,
  account_schema: MyApp.Accounts.Organization

Optional integrations:

config :hawk_ex,
  pubsub: MyApp.PubSub,
  oban: Oban,
  csv_storage: {HawkEx.CSV.Storage.Local, path: "priv/exports"}

For S3-backed CSV exports:

config :hawk_ex,
  csv_storage: {HawkEx.CSV.Storage.S3,
    bucket: "my-app-exports",
    prefix: "exports/"}

Entitlements

Use the top-level HawkEx module for the most common access checks:

if HawkEx.allowed?(account, :export_csv) do
  export_csv()
end

case HawkEx.remaining(account, :api_calls) do
  :unlimited -> allow_request()
  count when count > 0 -> allow_request()
  _ -> deny_request()
end

remaining/2 returns the limit configured on the account's current plan. It does not count usage. Your application should store and subtract usage for metered features.

Billing

Subscriptions are managed through HawkEx.Billing:

{:ok, subscription} = HawkEx.Billing.subscribe(account, :pro)
{:ok, canceled} = HawkEx.Billing.cancel(account)
{:ok, changed} = HawkEx.Billing.change_plan(account, :enterprise)

HawkEx keeps canceled subscriptions for history. New subscriptions create new records, while change_plan/2 updates the active subscription in place in the current release.

CSV Exports

Small exports can be generated synchronously:

{:ok, csv, row_count} = HawkEx.CSV.export(account, :subscriptions)

Async exports require Oban to be installed and configured:

{:ok, export} = HawkEx.CSV.export_async(account, :audit_logs)

Custom exports implement the HawkEx.CSV.Formatter behaviour.

Audit Logs

Record application actions with:

HawkEx.Audit.track(current_user, "settings.updated", organization)

When PubSub is configured, HawkEx also records its own subscription lifecycle events through the audit listener.

Documentation

Generate local documentation with:

mix docs

Published package documentation will be available on HexDocs after release.

Contributing

Contributions are welcome. Please keep changes focused, include tests for new or changed behavior, and run the checks before opening a pull request:

mix format
mix test
mix docs

For larger changes, open an issue or discussion first so the design can be aligned before implementation.

License

HawkEx is released under the MIT License. See LICENSE for details.