Skip to main content

README.md

# PhoenixKitReferrals

[![Hex.pm](https://img.shields.io/hexpm/v/phoenix_kit_referrals.svg)](https://hex.pm/packages/phoenix_kit_referrals)
[![Elixir](https://img.shields.io/badge/Elixir-~%3E_1.18-4B275F)](https://elixir-lang.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Referral codes module for [PhoenixKit](https://github.com/BeamLabEU/phoenix_kit).

Issue and manage referral codes, enforce per-code and per-user limits, track usage, and
(optionally) require or apply a code at user registration.

This module was extracted from PhoenixKit core. The referral tables
(`phoenix_kit_referral_codes`, `phoenix_kit_referral_code_usage`) are still created by
PhoenixKit's own migrations — this package ships the schemas, the business logic, and the
admin UI that read and write them. Installing the package is enough; no extra migration
step is required.

## Features

- **Referral code CRUD** — create, edit, enable/disable, and delete codes from the admin
  panel, with random-code generation and an optional beneficiary user.
- **Validation & limits** — uniqueness, expiration dates, per-code usage caps, and a
  system-wide cap on how many codes a single user can create.
- **Usage tracking** — every redemption is recorded with usage stats (totals, unique
  users, recent activity) for admin dashboards.
- **Signup integration** — when enabled, the registration / OAuth / magic-link flows
  offer (or require) a code and record its usage on success.
- **Auto-discovery** — implements the `PhoenixKit.Module` behaviour; PhoenixKit finds it
  at startup with zero config and exposes an enable/disable toggle and a permission key.

## Installation

Add it to your PhoenixKit host app's deps:

```elixir
def deps do
  [
    {:phoenix_kit, "~> 1.7"},
    {:phoenix_kit_referrals, "~> 0.1"}
  ]
end
```

Then fetch dependencies:

```bash
mix deps.get
```

It is auto-discovered at compile time (via `extra_applications: [:phoenix_kit]` and the
`PhoenixKit.Module` behaviour) — the admin tabs, settings page, and routes appear
automatically. Enable it from **Admin → Modules**.

## What you get

- **Admin → Users → Referrals** — list, create, edit, and enable/disable codes, with
  per-code usage stats.
- **Admin → Settings → Referrals** — turn the system on, make a code required at signup,
  and cap uses-per-code / codes-per-user.
- **Signup integration** — PhoenixKit core dispatches to this module at runtime by module
  key, so core has no compile-time dependency on it: with the module absent the field
  simply doesn't appear.

## Settings

Configured from **Admin → Settings → Referrals** and persisted via `PhoenixKit.Settings`:

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `referral_codes_enabled` | boolean | `false` | Module on/off |
| `referral_codes_required` | boolean | `false` | Require a valid code at registration |
| `max_number_of_uses_per_code` | integer | `100` | System-wide cap on uses per code |
| `max_number_of_codes_per_user` | integer | `10` | System-wide cap on codes a user can create |

## Public API

`PhoenixKitReferrals` is the context (and the `ReferralCode` schema). Highlights:

- **Codes** — `list_codes/0`, `list_valid_codes/0`, `get_code/1`, `get_code!/1`,
  `get_code_by_string/1`, `create_code/1`, `update_code/2`, `delete_code/1`,
  `change_code/2`, `generate_random_code/0`
- **Validation** — `valid_for_use?/1`, `expired?/1`, `usage_limit_reached?/1`
- **Usage** — `use_code/2`, `get_usage_stats/1`, `list_usage_for_code/1`,
  `user_used_code?/2`
- **Limits** — `validate_user_code_limit/1`, `count_user_codes/1`
- **System** — `enabled?/0`, `required?/0`, `enable_system/0`, `disable_system/0`,
  `set_required/1`, `get_config/0`, `get_system_stats/0`,
  `get_max_uses_per_code/0` / `set_max_uses_per_code/1`,
  `get_max_codes_per_user/0` / `set_max_codes_per_user/1`

```elixir
# Create a code
{:ok, code} =
  PhoenixKitReferrals.create_code(%{
    code: "WELCOME",
    description: "Welcome promotion",
    max_uses: 100
  })

# Redeem it for a user (by UUID)
case PhoenixKitReferrals.use_code("WELCOME", user_uuid) do
  {:ok, _usage} -> :ok
  {:error, :code_expired} -> :handle
  {:error, :usage_limit_reached} -> :handle
  {:error, reason} -> {:error, reason}
end
```

## Local development

`phoenix_kit` resolves from Hex by default. To run against a local PhoenixKit checkout
(e.g. an unpublished core change), export `PHOENIX_KIT_PATH` and Mix swaps the Hex pin
for a local `path:` dep at resolve time:

```bash
PHOENIX_KIT_PATH=../phoenix_kit mix test
```

Unset, the published pin is used — so `mix hex.publish` and CI are unaffected.

```bash
mix deps.get       # Install dependencies
mix test           # Run tests
mix format         # Format code
mix credo --strict # Static analysis
mix dialyzer       # Type checking
mix docs           # Generate documentation
```

## License

MIT — see [LICENSE](LICENSE) for details.