PhoenixKitReferrals (PhoenixKitReferrals v0.4.0)

Copy Markdown View Source

Referral code system for PhoenixKit - complete management in a single module.

This module provides both the Ecto schema definition and business logic for managing referral codes. It includes code creation, validation, usage tracking, and system configuration.

Schema Fields

  • code: The referral code string (unique, required, stored upcased)
  • description: Human-readable description of the code
  • status: Boolean indicating if the code is active
  • number_of_uses: Times the code has been used. Maintained by use_code/2; not settable through changeset/2
  • max_uses: Maximum number of times the code can be used
  • created_by_uuid: UUID of the admin who created the code
  • beneficiary_uuid: UUID of the user who benefits when this code is used (optional)
  • date_created: When the code was created
  • expiration_date: When the code expires (nil = never expires)

Core Functions

Code Management

Usage Tracking

System Settings

Usage Examples

# Check if system is enabled
if PhoenixKitReferrals.enabled?() do
  # System is active
end

# Create a new referral code
{:ok, code} = PhoenixKitReferrals.create_code(%{
  code: "WELCOME2024",
  description: "Welcome promotion",
  max_uses: 100,
  created_by_uuid: admin_user.uuid,
  expiration_date: ~U[2024-12-31 23:59:59.000000Z]
})

# Use a referral code during registration
case PhoenixKitReferrals.use_code("WELCOME2024", user_uuid) do
  {:ok, usage} -> # Code used successfully
  {:error, reason} -> # Handle error
end

Summary

Functions

Returns an %Ecto.Changeset{} for tracking referral code changes.

Creates a changeset for referral code creation and admin edits.

Counts the total number of referral codes created by a user.

Creates a referral code.

OTP apps whose templates Tailwind should scan for CSS classes.

Deletes a referral code.

Disables the referral codes system.

Enables the referral codes system.

Checks if the referral codes system is enabled.

Checks if a referral code has expired.

Generates a random 5-character alphanumeric referral code.

Generates a random code that no existing referral code already holds.

Gets a single referral code by UUID.

Same as get_code/1, but raises Ecto.NoResultsError if the code does not exist.

Gets a single referral code by its string value.

Gets the current referral codes system configuration.

Gets the maximum number of referral codes a single user can create.

Gets the maximum number of uses allowed per referral code.

Gets summary statistics for the referral codes system.

Gets usage statistics for a referral code.

Ships the referral-link capture script (URL ?referral= -> localStorage -> auto-filled referral_code field / OAuth links) via PhoenixKit's JS bundle extension point.

Returns the list of referral codes ordered by creation date.

Gets the most recent referral code usage records system-wide, for an admin activity feed.

Lists all usage records for a referral code.

Gets codes that are currently valid for use.

Checks if referral codes are required for user registration.

Sets the maximum number of referral codes a single user can create.

Sets the maximum number of uses allowed per referral code.

Sets whether referral codes are required for registration.

Gets the codes with the most recorded uses, for an admin leaderboard.

Updates a referral code.

Checks if a referral code has reached its usage limit.

Records usage of a referral code by a user.

Checks if a user has already used a specific referral code.

Checks if a referral code is currently valid for use.

Validates that a user hasn't exceeded their referral code creation limit.

Module version, shown on the admin Modules page. Keep in sync with mix.exs.

Functions

change_code(referral_code, attrs \\ %{})

Returns an %Ecto.Changeset{} for tracking referral code changes.

Examples

iex> PhoenixKitReferrals.change_code(code)
%Ecto.Changeset{data: %PhoenixKitReferrals{}}

changeset(referral_code, attrs)

Creates a changeset for referral code creation and admin edits.

The code string is trimmed and upcased before validation, so codes are stored in one canonical case.

Validations that constrain a value being setexpiration_date in the future, max_uses within the system limit — run only when that field is actually changing. An already-expired code, or a code whose max_uses predates a lowered system limit, therefore remains editable: an admin can still deactivate it, rename it, or fix its description.

The usage counter is not castable here. It is owned by use_code/2, which increments it atomically.

count_user_codes(user_uuid)

Counts the total number of referral codes created by a user.

Examples

iex> PhoenixKitReferrals.count_user_codes(1)
5

create_code(attrs \\ %{})

Creates a referral code.

Examples

iex> PhoenixKitReferrals.create_code(%{code: "TEST123", max_uses: 10})
{:ok, %PhoenixKitReferrals{}}

iex> PhoenixKitReferrals.create_code(%{code: ""})
{:error, %Ecto.Changeset{}}

css_sources()

OTP apps whose templates Tailwind should scan for CSS classes.

delete_code(referral_code)

Deletes a referral code.

Examples

iex> PhoenixKitReferrals.delete_code(code)
{:ok, %PhoenixKitReferrals{}}

iex> PhoenixKitReferrals.delete_code(code)
{:error, %Ecto.Changeset{}}

disable_system()

Disables the referral codes system.

Sets the "referral_codes_enabled" setting to false.

Examples

iex> PhoenixKitReferrals.disable_system()
{:ok, %Setting{}}

enable_system()

Enables the referral codes system.

Sets the "referral_codes_enabled" setting to true.

Examples

iex> PhoenixKitReferrals.enable_system()
{:ok, %Setting{}}

enabled?()

Checks if the referral codes system is enabled.

Returns true if the "referral_codes_enabled" setting is true. Any error (e.g. the database not being available yet at startup) is rescued and treated as disabled, so callers never need to special-case boot ordering.

Examples

iex> PhoenixKitReferrals.enabled?()
false

expired?(phoenix_kit_referrals)

Checks if a referral code has expired.

A code with no expiration_date never expires.

Examples

iex> PhoenixKitReferrals.expired?(code)
false

generate_random_code()

Generates a random 5-character alphanumeric referral code.

Returns a string of uppercase letters and digits, excluding the characters that read alike in most fonts (0, O, I, 1). Characters are drawn with replacement, so a code may repeat a character.

This does not check the code against existing codes — see generate_unique_code/1.

Examples

iex> PhoenixKitReferrals.generate_random_code()
"A7B2K"

generate_unique_code(attempts \\ 10)

Generates a random code that no existing referral code already holds.

Retries up to attempts times before giving up, so a caller never has to handle a unique-constraint violation from a generated code.

Examples

iex> PhoenixKitReferrals.generate_unique_code()
{:ok, "A7B2K"}

get_code(id)

Gets a single referral code by UUID.

Returns the referral code if found. Any input that is not a well-formed UUID string — including an integer id — returns nil.

Examples

iex> PhoenixKitReferrals.get_code("550e8400-e29b-41d4-a716-446655440000")
%PhoenixKitReferrals{}

iex> PhoenixKitReferrals.get_code("00000000-0000-0000-0000-000000000000")
nil

iex> PhoenixKitReferrals.get_code(123)
nil

get_code!(id)

Same as get_code/1, but raises Ecto.NoResultsError if the code does not exist.

Examples

iex> PhoenixKitReferrals.get_code!("550e8400-e29b-41d4-a716-446655440000")
%PhoenixKitReferrals{}

iex> PhoenixKitReferrals.get_code!("00000000-0000-0000-0000-000000000000")
** (Ecto.NoResultsError)

get_code_by_string(code_string)

Gets a single referral code by its string value.

The lookup trims and ignores case, so a user who types their code in lowercase still matches. Returns the referral code if found, nil otherwise.

Examples

iex> PhoenixKitReferrals.get_code_by_string("WELCOME2024")
%PhoenixKitReferrals{}

iex> PhoenixKitReferrals.get_code_by_string(" welcome2024 ")
%PhoenixKitReferrals{}

iex> PhoenixKitReferrals.get_code_by_string("INVALID")
nil

get_config()

Gets the current referral codes system configuration.

Returns a map with the current settings.

Examples

iex> PhoenixKitReferrals.get_config()
%{enabled: false, required: false}

get_max_codes_per_user()

Gets the maximum number of referral codes a single user can create.

Returns the system-wide limit for referral code creation per user. Defaults to 10 if not set.

Examples

iex> PhoenixKitReferrals.get_max_codes_per_user()
10

get_max_uses_per_code()

Gets the maximum number of uses allowed per referral code.

Returns the system-wide limit for how many times a single referral code can be used. Defaults to 100 if not set.

Examples

iex> PhoenixKitReferrals.get_max_uses_per_code()
100

get_system_stats()

Gets summary statistics for the referral codes system.

Returns counts and metrics useful for admin dashboards.

Examples

iex> PhoenixKitReferrals.get_system_stats()
%{total_codes: 10, active_codes: 8, total_usage: 150, codes_with_usage: 6}

get_usage_stats(code_uuid)

Gets usage statistics for a referral code.

Examples

iex> PhoenixKitReferrals.get_usage_stats(code_uuid)
%{total_uses: 5, unique_users: 3, last_used: ~U[...], recent_users: [...]}

js_sources()

Ships the referral-link capture script (URL ?referral= -> localStorage -> auto-filled referral_code field / OAuth links) via PhoenixKit's JS bundle extension point.

list_codes()

Returns the list of referral codes ordered by creation date.

Examples

iex> PhoenixKitReferrals.list_codes()
[%PhoenixKitReferrals{}, ...]

list_recent_usage(limit \\ 10)

Gets the most recent referral code usage records system-wide, for an admin activity feed.

Examples

iex> PhoenixKitReferrals.list_recent_usage(10)
[%PhoenixKitReferrals.ReferralCodeUsage{}, ...]

list_usage_for_code(code_uuid)

Lists all usage records for a referral code.

Examples

iex> PhoenixKitReferrals.list_usage_for_code(code_uuid)
[%PhoenixKitReferrals.ReferralCodeUsage{}, ...]

list_valid_codes()

Gets codes that are currently valid for use.

Returns codes that are active, not expired, and haven't reached usage limits. Mirrors valid_for_use?/1, including codes with no expiration_date — those never expire.

Examples

iex> PhoenixKitReferrals.list_valid_codes()
[%PhoenixKitReferrals{}, ...]

required?()

Checks if referral codes are required for user registration.

Returns true if the "referral_codes_required" setting is true.

Examples

iex> PhoenixKitReferrals.required?()
false

set_max_codes_per_user(max_codes)

Sets the maximum number of referral codes a single user can create.

Updates the system-wide limit for referral code creation per user.

Examples

iex> PhoenixKitReferrals.set_max_codes_per_user(5)
{:ok, %Setting{}}

set_max_uses_per_code(max_uses)

Sets the maximum number of uses allowed per referral code.

Updates the system-wide limit for referral code usage.

Examples

iex> PhoenixKitReferrals.set_max_uses_per_code(50)
{:ok, %Setting{}}

set_required(required)

Sets whether referral codes are required for registration.

Examples

iex> PhoenixKitReferrals.set_required(true)
{:ok, %Setting{}}

iex> PhoenixKitReferrals.set_required(false)
{:ok, %Setting{}}

top_codes(limit \\ 5)

Gets the codes with the most recorded uses, for an admin leaderboard.

Only codes with at least one use are returned. Preloads :creator and :beneficiary_user for display.

Examples

iex> PhoenixKitReferrals.top_codes(5)
[%PhoenixKitReferrals{}, ...]

update_code(referral_code, attrs)

Updates a referral code.

Examples

iex> PhoenixKitReferrals.update_code(code, %{description: "Updated"})
{:ok, %PhoenixKitReferrals{}}

iex> PhoenixKitReferrals.update_code(code, %{code: ""})
{:error, %Ecto.Changeset{}}

usage_limit_reached?(code)

Checks if a referral code has reached its usage limit.

Examples

iex> PhoenixKitReferrals.usage_limit_reached?(code)
false

use_code(code_string, user_uuid)

Records usage of a referral code by a user.

Claims one use of the code and writes the matching usage record in a single transaction. The claim is a conditional UPDATE that re-checks status, expiration, and the usage limit in the database, so two concurrent callers can never push a code past its max_uses.

A user may use a given code only once; a repeat attempt returns {:error, :already_used} and leaves the counter untouched.

Examples

iex> PhoenixKitReferrals.use_code("WELCOME2024", user_uuid)
{:ok, %PhoenixKitReferrals.ReferralCodeUsage{}}

iex> PhoenixKitReferrals.use_code("NOSUCHCODE", user_uuid)
{:error, :code_not_found}

iex> PhoenixKitReferrals.use_code("EXPIRED", user_uuid)
{:error, :code_expired}

user_used_code?(user_uuid, code_uuid)

Checks if a user has already used a specific referral code.

Examples

iex> PhoenixKitReferrals.user_used_code?(user_uuid, code_uuid)
false

valid_for_use?(code)

Checks if a referral code is currently valid for use.

A code is valid if:

  • It is active (status: true)
  • It has not exceeded its maximum uses
  • It has not expired (a code with no expiration_date never expires)

Examples

iex> PhoenixKitReferrals.valid_for_use?(code)
true

validate_user_code_limit(user_uuid)

Validates that a user hasn't exceeded their referral code creation limit.

Checks the current number of codes created by the user against the system limit. Returns {:ok, :valid} if within limits, {:error, reason} if limit exceeded.

Examples

iex> PhoenixKitReferrals.validate_user_code_limit(1)
{:ok, :valid}

iex> PhoenixKitReferrals.validate_user_code_limit(1)
{:error, "You have reached the maximum limit of 10 referral codes"}

version()

Module version, shown on the admin Modules page. Keep in sync with mix.exs.