Cesar Petrescu

Lab · Apps

  • Production
  • Commerce Platform

Billing Control Panel

Commercial control center replacing WHMCS: billing, provisioning, and customer portals for PhotonSpark

Commercial control center that replaces WHMCS for PhotonSpark. It owns customers, catalog, orders, subscriptions, invoices, payments, entitlements, provisioning orchestration, and audit across web, game, and email hosting. A Go modular monolith (separate API and worker processes) runs on PostgreSQL with a transactional outbox and job queue; Stripe is the recurring payment rail behind an internal ledger, with Romanian invoices and the asynchronous RO e-Factura workflow. Idempotent connectors provision the actual hosting platforms, and LIVE/TEST/DEMO stay separate data dimensions end to end.

  • Go
  • PostgreSQL
  • Next.js
  • React
  • TypeScript
  • MUI
  • Keycloak
  • Stripe
  • Stalwart
  • MinIO
  • Docker

Trace the systems, boundaries and handoffs

Choose a workflow to see its path through the system. Highlighted services own a step of it; hover, focus or tap any service to see its direct dependencies.

A commercial order becomes an active hosting entitlement

The synchronous request validates identity, billing profile, catalog version, price, accepted policies, and environment. Payment confirmation then releases an outbox-backed provisioning job; the connector’s external resource identifier is written back before the service is considered active.

Transaction boundary
Order, profile snapshot, policy snapshot, invoice intent, and outbox event commit together in PostgreSQL.
Asynchronous boundary
No hosting API is called inside checkout; workers execute external side effects after durable commit.
Commercial truth
The control center owns money, entitlement, lifecycle, and environment—not the provider connector.
Completion evidence
Provider resource ID, connector result, job attempt, service state, and audit actor are persisted.
  • IdentityPublic storefront → OIDC BFF → Customer Keycloak
  • Order + paymentPublic storefront → OIDC BFF → Go API → Commerce domains → Billing ledger → Stripe
  • Payment confirmationStripe → Webhook intake → Go API → Billing ledger → Outbox + job queue → Job worker → Web hosting
  • Database evidenceGo API → PostgreSQL
  • Audit evidenceJob worker → Audit history

A Stripe event is verified, deduplicated, and reconciled

Webhook delivery is treated as an untrusted, repeatable message. The intake verifies Stripe’s signature, stores the provider event identity, and applies an idempotent ledger transition. Duplicate or reordered delivery cannot issue a second entitlement or invoice effect.

Trust boundary
Signature, event type, account context, object identity, and environment are checked before mutation.
Idempotency key
Stripe event and provider object IDs prevent duplicate financial transitions across retries.
Ledger rule
Stripe confirms payment movement; the internal ledger remains the application’s commercial source of truth.
Downstream release
Only a committed paid or renewed decision creates the outbox work consumed by the provisioning worker.
  • Verified ingressStripe → Webhook intake → Go API → Billing ledger
  • Ledger persistenceGo API → PostgreSQL
  • Entitlement releaseBilling ledger → Outbox + job queue → Job worker
  • Audit evidenceJob worker → Audit history

One durable job contract drives every hosting backend

The worker claims a PostgreSQL job, loads the current entitlement and immutable order inputs, and selects a typed connector. Web, game, email, and manual services share lifecycle semantics while each provider adapter owns its API-specific request and read-back verification.

Claim model
Leases, attempt counters, deadlines, and retry policy prevent two workers from owning the same side effect.
Connector contract
Provision, inspect, suspend, resume, terminate, and rebuild return normalized evidence and external IDs.
Retry rule
Idempotency is provider-aware; ambiguous timeouts trigger inspection before another create request.
Runtime truth
The provider owns the actual resource; the control center stores the verified commercial and runtime linkage.
  • Authorized requestCustomer portal → OIDC BFF → Go API → Commerce domains
  • IdentityOIDC BFF → Customer Keycloak
  • Durable handoffGo API → Outbox + job queue → Job worker
  • Web connectorJob worker → Web hosting
  • Game connectorJob worker → Game hosting
  • Email connectorJob worker → Email hosting
  • Manual fulfillmentJob worker → Manual services
  • State evidenceJob worker → PostgreSQL
  • Audit evidenceJob worker → Audit history

Lifecycle state follows payment evidence and paid-through time

The scheduler finds due subscriptions, reconciles Stripe intent and ledger state, and emits lifecycle work only after durable decisions. Cancellation stops future renewal but preserves service until paid-through time; reprovisioning creates a fresh runtime instead of reviving stale provider state.

Scheduler input
Due timestamps, subscription state, environment, provider linkage, and previous attempts come from PostgreSQL.
Cancellation rule
Cancel-at-period-end and paid-through timestamps are explicit; provider deletion is not inferred from UI state.
Failure handling
Payment uncertainty remains reconcilable and does not silently suspend or duplicate the runtime.
Rebuild rule
A returning entitlement provisions a fresh provider resource with a new linkage and full audit history.
  • Due scanScheduler → PostgreSQL
  • Renewal decisionScheduler → Billing ledger → Stripe
  • Durable handoffScheduler → Outbox + job queue → Job worker
  • Web lifecycleJob worker → Web hosting
  • Game lifecycleJob worker → Game hosting
  • Email lifecycleJob worker → Email hosting
  • Audit evidenceJob worker → Audit history

Fiscal issuance is immutable; ANAF exchange is asynchronous

The ledger assigns a valid LIVE number series, renders and stores the immutable invoice artifact, then hands ANAF submission to a retryable worker. Submission, polling, acceptance, rejection, and response documents are tracked separately from whether the invoice was issued.

Series boundary
TEST documents are non-fiscal and can never consume or submit against a LIVE fiscal series.
Document evidence
Rendered invoice and exchange artifacts are immutable objects with database metadata and audit links.
Async exchange
ANAF availability cannot block the transaction that issued the invoice; retries and polling are durable jobs.
State separation
Issued, queued, submitted, accepted, and rejected are distinct states with timestamps and response evidence.
  • Immutable documentBilling ledger → MinIO documents
  • Fiscal submissionBilling ledger → Outbox + job queue → Job worker → ANAF e-Factura
  • Result persistenceJob worker → PostgreSQL
  • Audit evidenceJob worker → Audit history
  • Request / API
  • Domain / worker
  • Durable state
ClientsClientsEdge and IdentityEdge and IdentityCore Control CenterCore Control CenterEngines and ProvidersEngines and ProvidersData and EvidenceData and EvidencePublic storefrontcatalog + checkoutPublic storefront — catalog + checkoutCustomer portalservices + invoicesCustomer portal — services + invoicesStaff consoleadmin + financeStaff console — admin + financeOIDC BFFsession + API boundaryOIDC BFF — session + API boundaryCustomer Keycloakidentity + MFACustomer Keycloak — identity + MFAStaff Keycloakseparate realm + rolesStaff Keycloak — separate realm + rolesWebhook intakesignature + idempotencyWebhook intake — signature + idempotencyGo APIauthz + transaction boundaryGo API — authz + transaction boundaryCommerce domainscatalog + orders + servicesCommerce domains — catalog + orders + servicesBilling ledgersubscriptions + invoicesBilling ledger — subscriptions + invoicesJob workeridempotent side effectsJob worker — idempotent side effectsSchedulerrenewal + reconciliationScheduler — renewal + reconciliationStripepayment railStripe — payment railANAF e-Facturaasync fiscal exchangeANAF e-Factura — async fiscal exchangeWeb hostingsites + apps + dataWeb hosting — sites + apps + dataGame hostingPterodactyl servicesGame hosting — Pterodactyl servicesEmail hostingStalwart domains + mailboxesEmail hosting — Stalwart domains + mailboxesManual servicesoperator fulfillmentManual services — operator fulfillmentPostgreSQLcommercial system of recordPostgreSQL — commercial system of recordOutbox + job queuedurable async handoffOutbox + job queue — durable async handoffMinIO documentsimmutable fiscal artifactsMinIO documents — immutable fiscal artifactsAudit historyactor + request + resultAudit history — actor + request + result

Tap a service to see what it calls and what calls it.

Clients

Edge and Identity

Core Control Center

Engines and Providers

Data and Evidence

SOURCE OF TRUTH

Money stays internal

Stripe moves funds and reports provider state. Subscriptions, invoices, payments, entitlements, paid-through time, and fiscal identity belong to the internal ledger.

SIDE-EFFECT RULE

Commit before calling out

External APIs are never part of the database transaction. Desired work enters the transactional outbox and becomes retryable only after commercial state is durable.

RUNTIME OWNERSHIP

Providers own resources

Web hosting, Pterodactyl, and Stalwart remain authoritative for runtime existence and health; the control center owns the verified linkage and lifecycle intent.

IDENTITY BOUNDARY

Passwords stay in Keycloak

Customer and staff realms are separate. The BFF handles sessions and the application receives scoped identity without owning passwords, MFA secrets, or card data.

ENVIRONMENT SAFETY

LIVE / TEST / DEMO never blur

Environment is a first-class dimension on orders, invoices, payments, jobs, services, events, documents, and reports—not a UI filter.

RECOVERY EVIDENCE

Every retry explains itself

Jobs retain attempts, leases, errors, connector evidence, external IDs, actor and request context, and read-back results so recovery is observable rather than hopeful.

Why it exists

WHMCS owned billing but none of the real provisioning, so every product launch meant glue scripts and manual reconciliation.

One system of record for commerce: hosting platforms stay the source of truth for runtime resources, the control center owns money, customers, and orchestration.

Capabilities

Customer registration through Keycloak
the application never stores passwords, MFA secrets, or card data.
Quotes and manually provisioned custom services alongside the automated catalog.
Separate customer and staff OIDC realms with role-scoped admin and finance views.
LIVE, TEST, and DEMO as first-class data dimensions across orders, invoices, payments, services, jobs, events, artifacts, and reports.
Full audit trail across commerce and provisioning actions.

Components

Billing engine

  • Internal subscription, invoice, and payment ledger with Stripe as the payment rail only.
  • Romanian fiscal invoices with an asynchronous RO e-Factura (ANAF) submission workflow.
  • Number series discipline: TEST documents are non-fiscal and never touch a LIVE series.
  • Immutable generated documents stored in S3/MinIO-compatible storage.

Order and lifecycle flow

  • Billing profile must be complete at the transaction boundary before checkout (HTTP 428 PROFILE_REQUIRED).
  • Accepted profile and policy versions are copied into each order as immutable snapshots.
  • Order-wide cancellation with Stripe-confirmed renewal intent and paid-through termination.
  • Explicit fresh-runtime reprovisioning instead of resurrecting stale resources.

Provisioning connectors

  • Idempotent connectors for the web-hosting platform, the Pterodactyl game-hosting fork, Stalwart email, and manually provisioned services.
  • Worker and scheduler drain a PostgreSQL transactional outbox and job queue — no message broker to babysit.

Architecture

Portals
  • Public catalog and checkout
  • Customer portal
  • Admin and finance console (Next.js / React / MUI)
Control Center
  • Go modular monolith: API, OIDC BFF, webhooks
  • Customers · Catalog · Orders · Billing · Services · Audit
  • Worker and scheduler on a PostgreSQL transactional outbox + job queue
Billing
  • Internal subscription / invoice / payment ledger
  • Stripe as the recurring payment rail
  • Romanian invoices + async RO e-Factura (ANAF) workflow
  • Immutable documents in S3/MinIO storage
Identity
  • Separate customer and staff Keycloak OIDC realms
  • The app never owns passwords or MFA
  • Immutable policy-acceptance snapshots copied into each order
Provisioning
  • Idempotent connectors: web hosting, Pterodactyl game hosting, Stalwart email, manual services
  • Stripe-confirmed renewal intent, paid-through termination, fresh-runtime reprovisioning
  • LIVE / TEST / DEMO data dimensions across orders, invoices, jobs, and reports