Billing notifications: who hears about an invoice or a payment, and when.
Sends go through core's notification layer, so each recipient's own per-type preferences and delivery channels (in-app inbox, email, Telegram, digest cadence) apply. This module only decides the audience and the copy.
Recipients
admin_recipients/1 unions three sources, because no single one is
complete:
- holders of the relevant permission key (
users_with_permission/1) - Owner-role holders, whose access is implicit and who therefore have no permission rows at all
- holders of the
"*"superadmin key, likewise absent from a key-specific query
A resolver that queried only the first would miss the primary operator of a default install — the person most likely to want the notification.
Money copy carries no PII
Notification text names an invoice or order NUMBER and an amount, never a customer name, address or email: a notification lands in someone's inbox and can be routed onward to email or Telegram.
Failure is never fatal
Every send is wrapped. A notification reports a committed fact — a payment that has been recorded, an invoice that has been issued — and must never be able to undo it.
Summary
Functions
Everyone who should hear about billing operations: holders of key,
unioned with Owner-role holders and "*" superadmins.
An invoice was issued: tells billing operators, and the customer.
A payment attempt failed.
A payment was recorded against an invoice.
Functions
Everyone who should hear about billing operations: holders of key,
unioned with Owner-role holders and "*" superadmins.
An invoice was issued: tells billing operators, and the customer.
Separate sub-types, so an operator muting the invoice firehose does not also silence their own receipts.
A payment attempt failed.
Admin-only: the customer already saw the failure in the checkout flow, and a second channel telling them their card was declined is noise at best. Operators need it, because a failed payment is work.
A payment was recorded against an invoice.
paid is the amount actually recorded. It matters: quoting the invoice
total on a PARTIAL payment tells an operator 500 arrived when 50 did.
Falls back to the invoice total when a caller has no amount to hand.