Main context for PhoenixKit Billing system.
Provides comprehensive billing functionality including currencies, billing profiles, orders, and invoices with manual bank transfer payments (Phase 1).
Features
- Currencies: Multi-currency support with exchange rates
- Billing Profiles: User billing information (individuals & companies)
- Orders: Order management with line items and status tracking
- Invoices: Invoice generation with receipt functionality
- Bank Payments: Manual bank transfer workflow
System Enable/Disable
# Check if billing is enabled
PhoenixKitBilling.enabled?()
# Enable/disable billing system
PhoenixKitBilling.enable_system()
PhoenixKitBilling.disable_system()Order Workflow
# Create order
{:ok, order} = Billing.create_order(user, %{...})
# Confirm order
{:ok, order} = Billing.confirm_order(order)
# Generate invoice
{:ok, invoice} = Billing.create_invoice_from_order(order)
# Send invoice
{:ok, invoice} = Billing.send_invoice(invoice)
# Mark as paid (generates receipt)
{:ok, invoice} = Billing.mark_invoice_paid(invoice)
Summary
Functions
Returns list of available payment methods for manual recording. Bank transfer is always available, plus any enabled providers (Stripe/PayPal/Razorpay).
Calculates the total paid amount for an invoice from all transactions.
Calculates the current receipt status based on invoice state and transactions.
Cancels an order.
Cancels a subscription.
Returns a changeset for billing profile form.
Returns an order changeset for form building.
Returns a changeset for tracking payment option changes.
Changes a subscription's type.
Confirms an order.
Creates a billing profile.
Creates a checkout session for paying an invoice.
Creates a currency.
Creates a standalone invoice (without order).
Creates an invoice from an order.
Creates an order from attributes (user_uuid included in attrs).
Creates an order for a user.
Creates a payment method record.
Creates a new payment option.
Creates a setup session for saving a payment method.
Creates a new subscription for a user.
Creates a subscription type.
Deletes a billing profile.
Deletes a currency.
Deletes an order (only drafts).
Deletes a payment option.
Deletes a subscription type.
Disables the billing system.
Enables the billing system.
Checks if the billing system is enabled.
Formats company address from a company_info map for document printing.
Generates a receipt for an invoice.
Generates a unique transaction number.
Gets a billing profile by ID or UUID, returns nil if not found.
Gets a billing profile by ID or UUID, raises if not found.
Returns the company info map used by the printable document views (invoice, receipt, credit note, payment confirmation).
Returns the current billing configuration.
Gets a currency by ID or UUID.
Gets a currency by ID or UUID, raises if not found.
Gets a currency by code.
Returns dashboard statistics.
Gets the default billing profile for a user.
Gets the default currency.
Gets the default payment method for a user.
Gets an invoice by ID or UUID with optional preloads.
Gets an invoice by ID or UUID.
Gets an invoice by invoice number.
Gets the remaining amount for an invoice.
Gets an order by ID or UUID with optional preloads.
Gets an order by ID or UUID.
Gets an order by order number.
Gets an order by UUID with optional preloads. Used for public-facing URLs to prevent ID enumeration.
Gets a payment method by ID or UUID.
Gets a payment option by ID.
Gets a payment option by code.
Gets a subscription by ID or UUID.
Gets a subscription by ID or UUID, raises if not found.
Gets a subscription type by ID or UUID.
Gets a subscription type by slug.
Returns the default tax rate as a Decimal (e.g., Decimal.new("0.20") for 20%).
Returns the default tax rate as integer percentage (e.g., 20 for 20%).
Gets a transaction by ID or UUID.
Gets a transaction by ID or UUID, raises if not found.
Gets a transaction by number.
Lists active payment options for checkout.
Lists billing profiles with optional filters.
Lists billing profiles with count for pagination.
Lists all currencies with optional filters.
Lists enabled currencies.
Gets transactions for a specific invoice.
Lists all invoices with optional filters.
Lists invoices for a specific order.
Lists invoices with count for pagination.
Lists all orders with optional filters.
Lists orders with count for pagination.
Lists saved payment methods for a user.
Lists all payment options.
Lists all subscription types.
Lists all subscriptions for a user.
Lists all transactions with optional filters.
Lists transactions with count for pagination.
Lists billing profiles for a user (shorthand).
Lists invoices for a specific user.
Lists orders for a specific user.
Lists all subscriptions for a specific user.
Marks an invoice as paid (generates receipt).
Marks an order as paid.
Marks an order as refunded.
Marks overdue invoices.
Returns stats for the module card on the admin Modules page.
Notification types this module contributes, collected by core's
Notifications.Types via function_exported?/3.
Pauses a subscription.
Checks if a payment option requires a billing profile.
Records a payment for an invoice.
Records a refund for an invoice.
Removes a payment method.
Resumes a paused subscription.
Sends a credit note email for a refund transaction.
Sends credit note email to the customer.
Sends an invoice (marks as sent and sends email).
Sends invoice email to the customer.
Sends a payment confirmation email for an individual payment transaction.
Sends payment confirmation email to the customer.
Sends receipt for a paid invoice.
Sends receipt email to the customer.
Sets a billing profile as default.
Sets a currency as default.
Sets a payment method as the default for a user.
The configured snapshot-refresh policy: pending_only | never | always.
Whether an order's billing snapshot may still be refreshed from the live profile.
Returns whether tax is enabled in billing settings.
Toggles the active status of a payment option.
Updates a billing profile.
Updates a currency.
Updates an invoice.
Updates an invoice's paid_amount based on its transactions.
Updates an order.
Updates a payment option.
Updates the receipt status based on current invoice state. Call this after refunds to update the receipt status.
Updates a subscription with the given attributes.
Updates a subscription type.
Voids an invoice.
Functions
Returns list of available payment methods for manual recording. Bank transfer is always available, plus any enabled providers (Stripe/PayPal/Razorpay).
Examples
iex> Billing.available_payment_methods()
["bank"] # Only bank if no providers enabled
iex> Billing.available_payment_methods()
["bank", "stripe", "paypal"] # Bank + enabled providers
Calculates the total paid amount for an invoice from all transactions.
Calculates the current receipt status based on invoice state and transactions.
Cancels an order.
Cancels a subscription.
Options
immediately: true- Cancel immediately instead of at period end
Examples
Billing.cancel_subscription(subscription)
Billing.cancel_subscription(subscription, immediately: true)
Returns a changeset for billing profile form.
Returns an order changeset for form building.
Returns a changeset for tracking payment option changes.
Changes a subscription's type.
By default, the new type takes effect at the next billing cycle.
Confirms an order.
Creates a billing profile.
Creates a checkout session for paying an invoice.
Returns the checkout URL to redirect the user to.
Parameters
invoice- The invoice to payprovider- Payment provider atom (:stripe, :paypal, :razorpay)opts- Options forwarded to the provider::success_url- URL to redirect after success (required):cancel_url- URL to redirect if cancelled (defaults to:success_url):customer_email,:save_payment_method,:metadata- optional, provider-dependent
Examples
{:ok, url} = Billing.create_checkout_session(invoice, :stripe, success_url: "/success")
Creates a currency.
Creates a standalone invoice (without order).
Creates an invoice from an order.
Creates an order from attributes (user_uuid included in attrs).
Creates an order for a user.
Creates a payment method record.
Usually called after a successful setup session webhook.
Creates a new payment option.
Creates a setup session for saving a payment method.
Returns the setup URL to redirect the user to.
Parameters
user_uuid- The user saving the payment methodprovider- Payment provider atomopts- Options (success_url required)
Creates a new subscription for a user.
This creates the master subscription record. The first payment should be processed separately via checkout session.
Parameters
user_uuid- The user creating the subscription (UUID)attrs- Subscription attributes::subscription_type_uuid- Required: subscription type UUID:billing_profile_uuid- Optional: billing profile UUID to use:payment_method_uuid- Optional: saved payment method UUID for renewals:trial_days- Optional: override type's trial days:plan_uuid- Alternative: can use:plan_uuidinstead of:subscription_type_uuid
Examples
Billing.create_subscription(user.uuid, %{subscription_type_uuid: type.uuid})
Billing.create_subscription(user.uuid, %{subscription_type_uuid: type.uuid, trial_days: 14})
# Using plan_uuid parameter
Billing.create_subscription(user.uuid, %{plan_uuid: type.uuid})
Creates a subscription type.
Deletes a billing profile.
Deletes a currency.
The default currency and currencies referenced by orders cannot be deleted.
Deletes an order (only drafts).
Deletes a payment option.
Deletes a subscription type.
Types with active subscriptions cannot be deleted.
Disables the billing system.
Enables the billing system.
Checks if the billing system is enabled.
Formats company address from a company_info map for document printing.
The map is required (callers pass the result of
Organization.get_company_info/0), keeping this function pure.
Generates a receipt for an invoice.
Receipts can be generated:
- When invoice is fully paid (status: "paid")
- When invoice has any payment (paid_amount > 0) - partial receipt
Receipt status:
- "paid" - fully paid
- "partially_paid" - partial payment received
- "refunded" - fully refunded after payment
Generates a unique transaction number.
Gets a billing profile by ID or UUID, returns nil if not found.
Gets a billing profile by ID or UUID, raises if not found.
Returns the company info map used by the printable document views (invoice, receipt, credit note, payment confirmation).
Combines organization company details and bank details into a single map of formatted, print-ready strings.
Returns the current billing configuration.
Gets a currency by ID or UUID.
Gets a currency by ID or UUID, raises if not found.
Gets a currency by code.
Returns dashboard statistics.
Gets the default billing profile for a user.
Gets the default currency.
Gets the default payment method for a user.
Gets an invoice by ID or UUID with optional preloads.
Gets an invoice by ID or UUID.
Gets an invoice by invoice number.
Gets the remaining amount for an invoice.
Gets an order by ID or UUID with optional preloads.
Gets an order by ID or UUID.
Gets an order by order number.
Gets an order by UUID with optional preloads. Used for public-facing URLs to prevent ID enumeration.
Gets a payment method by ID or UUID.
Gets a payment option by ID.
Gets a payment option by code.
Gets a subscription by ID or UUID.
Options
:preload- list of associations to preload (default: [])
Gets a subscription by ID or UUID, raises if not found.
Gets a subscription type by ID or UUID.
Gets a subscription type by slug.
Returns the default tax rate as a Decimal (e.g., Decimal.new("0.20") for 20%).
Uses the billing settings value. When company country is configured, the suggested rate from BeamLabCountries can be applied via billing settings UI.
Returns the default tax rate as integer percentage (e.g., 20 for 20%).
Gets a transaction by ID or UUID.
Gets a transaction by ID or UUID, raises if not found.
Gets a transaction by number.
Lists active payment options for checkout.
Lists billing profiles with optional filters.
Options
:user_uuid- Filter by user UUID:type- Filter by type ("individual" or "company"):search- Search in name/email/company fields:page- Page number:per_page- Items per page:preload- Associations to preload
Lists billing profiles with count for pagination.
Lists all currencies with optional filters.
Options
:enabled- Filter by enabled status:order_by- Custom ordering
Lists enabled currencies.
Gets transactions for a specific invoice.
Lists all invoices with optional filters.
Lists invoices for a specific order.
Lists invoices with count for pagination.
Lists all orders with optional filters.
Lists orders with count for pagination.
Lists saved payment methods for a user.
Lists all payment options.
Lists all subscription types.
Options
:active_only- Only return active types (default: true)
Lists all subscriptions for a user.
Options
:status- Filter by status (e.g., "active", "cancelled"):preload- Associations to preload (default: [:subscription_type])
Examples
Billing.list_subscriptions(user_uuid)
Billing.list_subscriptions(user_uuid, status: "active")
Lists all transactions with optional filters.
Options
:invoice_uuid- Filter by invoice UUID:user_uuid- Filter by user who created the transaction:payment_method- Filter by payment method:type- Filter by type: "payment" (amount > 0) or "refund" (amount < 0):search- Search by transaction number:limit- Limit results:offset- Offset for pagination:preload- Associations to preload
Examples
Billing.list_transactions(invoice_uuid: "some-uuid")
Billing.list_transactions(type: "payment", limit: 10)
Lists transactions with count for pagination.
Lists billing profiles for a user (shorthand).
Lists invoices for a specific user.
Lists orders for a specific user.
Lists all subscriptions for a specific user.
Options
:status- filter by status (e.g., "active", "cancelled"):preload- list of associations to preload (default: [:subscription_type])
Examples
Billing.list_user_subscriptions(user.uuid)
Billing.list_user_subscriptions(user.uuid, status: "active")
Marks an invoice as paid (generates receipt).
Marks an order as paid.
Options
:payment_method- The payment method used (e.g., "bank", "stripe", "paypal")
Marks an order as refunded.
Marks overdue invoices.
Returns stats for the module card on the admin Modules page.
Runs get_config/0, which issues three count queries (orders,
invoices, currencies). This is invoked once per render of the admin
Modules card, so the cost is bounded; it is not cached.
Notification types this module contributes, collected by core's
Notifications.Types via function_exported?/3.
Admin and customer audiences are separate sub-types on purpose: an operator muting the invoice firehose must not also silence their own receipts, and a customer's invoice notice must not be governed by an admin-facing preference.
⚠️ These are the NOTIFY actions. Audit rows use DIFFERENT action strings
(billing.invoice_issued_audit and friends): Activity.log/1
auto-derives notifications from registered actions, so an audit row
written with a notify action delivers a duplicate on top of the explicit
fan-out.
Pauses a subscription.
Paused subscriptions don't renew until resumed.
Checks if a payment option requires a billing profile.
Records a payment for an invoice.
Creates a transaction with positive amount and updates invoice's paid_amount. If paid_amount >= total, marks invoice as paid and generates receipt.
Parameters
invoice- The invoice to payattrs- Transaction attributes including :amount, :payment_method, :descriptionadmin_user- The admin user recording the payment
Examples
{:ok, transaction} = Billing.record_payment(invoice, %{amount: "100.00", payment_method: "bank"}, admin)
Records a refund for an invoice.
Creates a transaction with negative amount and updates invoice's paid_amount.
Parameters
invoice- The invoice to refundattrs- Transaction attributes including :amount (positive value), :description (reason)admin_user- The admin user recording the refund
Examples
{:ok, transaction} = Billing.record_refund(invoice, %{amount: "50.00", description: "Partial refund"}, admin)
Removes a payment method.
Marks as removed in database. Should also delete from provider.
Resumes a paused subscription.
Sends a credit note email for a refund transaction.
Parameters
invoice- The invoice associated with the refundtransaction- The refund transactionopts- Options::to_email- Override recipient email:credit_note_url- URL to view credit note online
Examples
{:ok, invoice} = Billing.send_credit_note(invoice, transaction, credit_note_url: "https://...")
Sends credit note email to the customer.
Sends an invoice (marks as sent and sends email).
Options:
:send_email- Whether to send email (default: true):invoice_url- URL to view invoice online (optional)
Sends invoice email to the customer.
Sends a payment confirmation email for an individual payment transaction.
Parameters
invoice- The invoice associated with the paymenttransaction- The payment transactionopts- Options including::to_email- Override recipient email address:payment_url- URL to view payment confirmation online:send_email- Whether to send email (default: true)
Sends payment confirmation email to the customer.
Sends receipt for a paid invoice.
Options:
:send_email- Whether to send email (default: true):to_email- Override recipient email address:receipt_url- URL to view receipt online (optional)
Sends receipt email to the customer.
Sets a billing profile as default.
Sets a currency as default.
Sets a payment method as the default for a user.
Unsets any existing default.
The configured snapshot-refresh policy: pending_only | never | always.
Whether an order's billing snapshot may still be refreshed from the live profile.
The snapshot is the record of who an order was billed to, so rewriting it after the fact makes history mutable. The line is drawn at money changing hands:
"draft"/"pending"— refreshable"confirmed"/"paid"/"refunded"/"cancelled"— frozen
Operators who want the stricter or looser rule set
billing_snapshot_policy to "never" (freeze from creation) or
"always" (the pre-2026-08 behaviour, refresh whenever the order's profile
changes). Fails closed to the default on a settings-layer error.
Scope
This governs a profile SWITCH — the order pointing at a different
billing profile than before. Editing the fields of the profile an order
already points at never refreshes the snapshot, at any status or policy;
maybe_update_billing_snapshot/2 only reconsiders when the incoming
billing_profile_uuid differs (or the snapshot is empty). Refreshing on
in-place profile edits would need the snapshot compared field by field,
which is not implemented.
Returns whether tax is enabled in billing settings.
Toggles the active status of a payment option.
Updates a billing profile.
Updates a currency.
Updates an invoice.
Updates an invoice's paid_amount based on its transactions.
Updates an order.
Updates a payment option.
Updates the receipt status based on current invoice state. Call this after refunds to update the receipt status.
Updates a subscription with the given attributes.
Useful for administrative adjustments such as extending the billing
period or correcting plan details. Status/lifecycle fields are not
updatable here — use the dedicated cancel_subscription/1,
pause_subscription/1, and resume_subscription/1 functions instead,
so their broadcasts and bookkeeping always fire.
Updates a subscription type.
Voids an invoice.