Billing
A plan is something the installation sells, priced per month or per year, and
a subscription is a tenant’s enrollment in one: a tenant is the customer, so it
has one. The module promises that the price list is the operator’s and every
tenant reads the same one, that the price a customer agreed to is what it is
billed until the next period, and that money is never taken inside a database
transaction. The module is
modules/billing; this page states nothing that
tree does not.
The map
features/billing in the recordWhat it promises
Entity |
|
Events |
|
Permissions |
|
Service |
|
Routes and screens
| Method | Path | Does | Authorization | Publishes |
|---|---|---|---|---|
|
|
List plans |
|
— |
|
|
Create a plan |
|
|
|
|
Read a plan |
|
— |
|
|
Update a plan |
|
|
|
|
Delete a plan |
|
|
|
|
Read this tenant’s subscription |
|
— |
|
|
Cancel this tenant’s subscription |
|
|
|
|
Subscribe this tenant to a plan |
|
|
-
GET/POST /api/v1/billing/plansandGET/PATCH/DELETE /api/v1/billing/plans/{id}— the five routesrest.Spec.Mountregisters, with soft delete; the three writes carryhttpx.OperatorPermission, and the delete hookRefuseWhileSubscribedcounts live subscriptions across every tenant under system access and refuses, with409, a plan somebody is still on. -
GET /api/v1/billing/subscription— the tenant’s one subscription, fromrest.Singleton,404until it has subscribed; there is noPUT, because a customer must not write its own period or its own price. -
POST /api/v1/billing/subscription/subscribeand…/cancel— the two commands ininternal/handler.go, underbilling:manage: the same plan again changes nothing, a different plan takes effect at the next renewal and is refused while the subscription owes for a period, and the trial is issued once per tenant; cancelling ends the subscription at the end of the period or now, and now is refused while a period is owed for. -
Screens: the generated list, detail and forms under
/admin/billing/plans, writable only at the operator’s host, and the navigation entry "Billing" for anyone withbilling:read. The subscription registers no resource: a read-only singleton would be a screen whose forms nobody may submit. Hand-written pages: none.
Authorization
| Authorization | Who passes | Routes |
|---|---|---|
|
a member of the tenant |
|
|
the operator’s tenant only |
|
|
a member of the tenant |
|
Events, jobs and subscriptions
| Event | Published by | Handled by |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a job, hook or command in the module |
|
|
a job, hook or command in the module |
|
-
Publishes the seven events above; the two commands declare
billing.subscribedandbilling.cancelledso the boot gate can check them. -
Jobs:
Renew, namedbilling-renew, at a quarter past two every morning unlessDeps.RenewEverysays otherwise, throughjobs.PerTenant(internal/renew.go): one transaction asks the service what is owed, the charge goes to the provider with no transaction open, and a second transaction records the receipt throughSettle. A free plan renews itself, a cancelled subscription ends when its period does, and one past due for longer thanGraceDays(seven) is cancelled. -
Subscriptions: none, written out as a decision: what a plan entitles somebody to is the consuming module’s business.
What it needs
|
Interface |
Supplied by |
|
|
the tenant module’s |
|
|
|
|
|
zero, meaning the nightly schedule; a test sets an interval |
Configuration: none.
Who uses it
-
A private payment provider — its
Providerembedscontracts.PaymentProviderand it buildscontracts.Charge,ReceiptandKey, so one object isDeps.Paymentshere and the provider there; it satisfies this module more than it calls it. -
No public module takes its contracts; the applications do, for
billing.Manual()and to namebilling:catalogamong the operator grantsauth.SeedRolesseeds for a new tenant. -
The admin and the native shell reach it through
httpx.Resource, not throughcontracts/.
Verification
-
go test ./modules/billing/…— the fake and the real service pass one conformance suite: the trial once per tenant, a debt that survives a plan change, a cancel and a resubscribe, re-pricing from the next period, a pending receipt, the dunning ceiling and the anchored billing day. The internal tests prove the commands publish in the caller’s transaction, one subscription per tenant, that a rolled-back command leaves nothing, and that the renewal charges with no transaction open. The module tests prove the price list is the operator’s over HTTP and through the generated screen’s resource, that a plan somebody is on cannot be deleted, thatactiveis the caller’s to say, and that a module without a provider does not compose. -
No browser journey:
make e2eexercises tasks only. -
Not proven: a provider that speaks to a payment processor, which lives outside this repository; the schedule itself, which the test replaces with an interval.