PhoenixKitReferrals

Hex.pmElixirLicense: MIT

Referral codes module for PhoenixKit.

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

Installation

Add it to your PhoenixKit host app's deps:

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

Then fetch dependencies:

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

Entering a referral code by hand is friction most users skip, so this module also accepts codes via URL:

  1. Share https://yourapp.com/?ref=CODE (any page — it doesn't have to be the signup page). ?referral=CODE is also accepted as an alias.
  2. A small script shipped by this module (see JS integration below) stores the code in the visitor's localStorage for 30 days and strips the param from the address bar. If the visitor already has a stored code, a later link won't overwrite it (first-touch attribution).
  3. Whenever the visitor reaches registration, magic-link registration, or clicks an OAuth provider button, the stored code is applied automatically — the existing referral_code form field is filled in, or ?referral_code= is appended to the OAuth link. From there it flows through PhoenixKit core's existing registration/OAuth handling exactly as if the visitor had typed it in.

No database schema changes or core phoenix_kit changes are required — capture and autofill are pure client-side script working with fields/links core already renders and already reads server-side.

JS integration

Ships a prebuilt script via PhoenixKit.Module.js_sources/0, the same mechanism phoenix_kit_crm uses. Any host that has already installed phoenix_kit's JS integration (done once by mix phoenix_kit.install / mix phoenix_kit.update) picks this up automatically on the next compile — no manual app.js or layout edits needed.

To change the capture window, set before the bundle loads:

<script>window.PhoenixKitReferralsConfig = {ttlDays: 14};</script>

Settings

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

KeyTypeDefaultDescription
referral_codes_enabledbooleanfalseModule on/off
referral_codes_requiredbooleanfalseRequire a valid code at registration
max_number_of_uses_per_codeinteger100System-wide cap on uses per code
max_number_of_codes_per_userinteger10System-wide cap on codes a user can create

Public API

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

# 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:

PHOENIX_KIT_PATH=../phoenix_kit mix test

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

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 for details.