Billing
Eight tools that configure and inspect automated monthly PIX billing. They
share every validation rule with the staff UI’s “Cobrança” panel — the same zod
schemas from @marques-boxing/billing-contracts, not a second copy of the same
rules — so an input the panel refuses is refused here with the same message.
Legal values
Section titled “Legal values”| Due day | 5, 10, 15 or 20 — nothing else. These four days exist in every month, February included, so a fixed due day never has to be clamped. |
| Amount | A positive decimal with at most two places. 0 is refused, and a third decimal place is refused rather than rounded. |
| Phone | A Brazilian mobile or landline, normalized to E.164 (+5511999998888). |
| First cycle | The first due day on or after today. No partial first month and no retroactive cycle. |
billing_preview_contract
Section titled “billing_preview_contract”Answers “what would automated billing do for this contract” with zero side effects: nothing is written, no charge is created and no message is sent.
Input: contract_id, due_day, agreed_amount, optional as_of and
timezone.
Returns the first cycle due date, the cycle month, the computed billing key
(<contract_id>:<YYYY-MM>), the amount in reais and centavos, the E.164
recipient, and eligibility_errors — stable codes such as
student_not_active, invalid_phone, contract_ended_before_due_date.
Run it before billing_enable_contract.
billing_enable_contract
Section titled “billing_enable_contract”Turns billing on for one contract. Requires an active student, an active
in-date contract and a valid phone; refuses with the eligibility codes
otherwise. confirm: true is mandatory — a missing field then fails validation
instead of quietly meaning “not confirmed”.
Sets billing_enabled, due_day, agreed_amount and billing_started_on
(the first due day on or after today), and clears any previous
billing_disabled_* note.
The agreed_amount is a snapshot of this contract, not a pointer to the
plan’s price. A later price change on the plan does not move it.
billing_disable_contract
Section titled “billing_disable_contract”Stops future cycles and pauses the reminders of the contract’s still-open
invoices. Issued debt is retained: no invoice is cancelled and no status
changes. The reason is mandatory — it is what staff read months later when
they ask why a student stopped being billed.
Returns the ids of the invoices whose reminders were paused.
billing_resume_reminders
Section titled “billing_resume_reminders”Clears reminders_paused on one invoice. An explicit action by design:
nothing resumes reminders automatically.
billing_record_manual_payment
Section titled “billing_record_manual_payment”Records a payment made outside the provider — cash, a direct PIX, a card
machine — through public.record_manual_payment. One call, one transaction:
it inserts the payment, marks the invoice paid once the recorded total covers
it, and stamps who recorded it. The staff UI’s payment form calls the same
function.
Input: invoice_id, amount, method (pix / credit_card / boleto),
paid_at (ISO 8601 with an offset).
billing_get_settings
Section titled “billing_get_settings”Reads this organization’s billing settings: the two independent kill
switches (charges_enabled, messages_enabled), the pilot recipient
allowlist, the provider environment, the charge expiry and the timezone.
The first read creates the row. A missing row and a row with both kill
switches off mean the same thing to the engine, but “not configured” invites the
reading “unknown”, and the one reading nobody may ever make about a system that
moves money is “unknown, therefore probably on”. After the first read there is a
row that says false out loud. It is created with org_id and the two kill
switches explicitly false; every other column takes the migration’s own
default, so a created row is indistinguishable from one Postgres would have made.
The create is idempotent — two callers racing on the first read end with one row.
Only owner and staff create it. An automation caller reads settings on
every scheduler tick and must never be the account that brings an organization’s
billing configuration into existence, so it gets the defaults reported — both
kill switches off — and no write.
billing_update_settings
Section titled “billing_update_settings”Owner only. Changes any of the settings above. Each kill switch stops its
own side effect on its own: charges off still allows messages, and messages off
still allows charges. While pilot_recipients_e164 is non-empty, only those
numbers receive a message.
billing_list_invoices
Section titled “billing_list_invoices”Lists this organization’s invoices, newest due date first. Filters:
contract_id, an exact status, or unpaid: true for pending or overdue —
use unpaid for “who owes us money”, since that question is two statuses.
Paginated like every other list tool (limit / offset, returns
{ items, total, has_more }).