This project demonstrates how to build a lightweight billing system using Elixir and GenServer. It shows how to process payments, manage recurring billing cycles, and use Elixir’s concurrency model to schedule future tasks. The example is based on a session by Jackline Wayua and Achieng’ Linda.
Billing systems usually need to do a few things well:
- Accept a payment request
- Run billing logic reliably
- Store or update state
- Charge again next month without manual triggers
GenServer gives you an elegant way to handle these tasks because it lets you create long-running processes that hold state, respond to messages, and schedule future work.
This project shows how to use a single GenServer to automate monthly billing using GenServer.call/3 and Process.send_after/3.
- A GenServer that processes payments through a
:paycall - Automatically schedules the next month’s billing cycle
- Simple state management inside the GenServer
- Uses
Process.send_after/3for recurring tasks - Beginner-friendly starting point for subscription systems
- Easy to extend with real payment APIs or persistence
A simple client function triggers payment processing:
def process_payment(pid) do
GenServer.call(pid, :pay)
endThis sends a synchronous request to the GenServer asking it to process a payment.
Inside the GenServer:
@impl true
def handle_call(:pay, _from, attrs) do
response = server_process_payment(attrs)
schedule_next_monthly_payment()
{:reply, response, attrs}
endThe GenServer:
- Processes the payment
- Calculates when the next payment should occur
- Schedules a message to itself for that future date
- Returns the result
Recurring payments are managed using Process.send_after/3:
def schedule_next_monthly_payment do
next_payment_time = calculate_next_month_in_milliseconds()
Process.send_after(self(), :next_pay, next_payment_time)
endThis ensures the GenServer wakes itself up a month later to perform the same billing operation again.
GenServer billing works well when:
- You need background workers that run on a schedule
- You want predictable recurring billing
- You are managing state within a single process
- You’re building subscription-based features
- You want a simple prototype before integrating full payment services
For production systems, you can extend this with:
- Database storage
- Error handling and retries
- Supervision trees
- Real payment APIs (M-Pesa, Stripe, PayPal, etc.)
/lib
billing/
billing_server.ex # The GenServer implementation
/test
billing_server_test.exs # Optional tests
mix.exs # Project dependencies
README.md # Documentation
Clone the repo:
git clone <your-repo-url>
cd <repo-folder>Install dependencies:
mix deps.getRun an interactive Elixir shell:
iex -S mixStart the GenServer:
{:ok, pid} = BillingServer.start_link(%{user: "Demo User"})
BillingServer.process_payment(pid)Here are a few simple ways to grow the project:
- Add a real payment module with API integrations
- Store transaction history in a database
- Use Phoenix LiveView to build a dashboard
- Handle failed payments or retries
- Support multiple users with a Registry or Dynamic Supervisor
GenServer is perfect for this kind of problem because it gives you:
- A process to hold billing state
- A predictable lifecycle
- Built-in message handling
- Easy scheduling
- Clear concurrency semantics
It’s a great introduction to building real-world background processes in Elixir.
Feel free to open issues, submit pull requests, or suggest improvements.