Commercial architecture es una cadena, no una pricing page
La mayoría de equipos publica una pricing page y lo llama monetization. Luego añaden Stripe. Luego hardcodean “Pro gets feature X” en tres rutas de API. Luego ops necesita una cuenta complimentary y alguien abre un hotfix. Luego llega la factura del vendor y nadie sabe qué tenant quemó el margen.
Ese patrón es habitual porque el atajo de soft-launch — “pricing + FinOps + growth” — suena completo. Es necesario pero incompleto. Un modelo commercial serio no es un documento, una página o una integración de billing. Es un system of capabilities con owners nombrados, una secuencia clara y un único product catalog como source of truth.
Este artículo es project-agnostic: una commercial architecture portable que cualquier equipo SaaS o multi-tenant puede reutilizar. Misma idea que en architecture de dominio en este blog — boundaries first, enforce at the edge, no inventes una segunda verdad en el payment provider.
- Monetization es una chain: catalog → agreement → provision → entitlements → meter → bill → cash (+ FinOps cost loop).
- One catalog alimenta pricing, admin, entitlements y billing — nunca una segunda lista de plans en el MoR.
- Entitlements ≠ feature flags; deny con un upgrade path claro.
- Ship Encode (catalog + enforce + gates + admin) antes de Charge; legalize antes del primer pago real.
- Cost ≠ revenue — FinOps y pricing son ledgers separados.
- Quédate con el cheat sheet y la lista Start Monday al final.
Los stacks líderes de billing y entitlements (Stripe Billing / Chargebee-class, capas entitlement + meter, práctica FinOps en el lado del coste) tratan la monetization como one governed chain. Marketing puede empezar con una pricing page. Architecture empieza aquí:
Catalog (what we sell)
→ Quote / order / subscription (commercial agreement)
→ Provisioning (tenant exists)
→ Entitlements (what runtime may do)
→ Metering (what was used)
→ Rating & billing (what we invoice)
→ Cash & revenue ops (collected, recognized)
Cada flecha es un handoff. Si saltas un eslabón — por ejemplo billing sin entitlements, o entitlements sin catalog — el siguiente equipo inventa su propia verdad. Así acabas con tres listas de plans: una en la web, una en el folklore de admin, una en Stripe.
Matiz: entitlements y metering no son solo left-to-right. Los caps a menudo leen meters (“allow if usage is under the limit”), así que los meters alimentan entitlements, billing y FinOps. Mantén la chain de siete pasos como historia principal; el cap check es un feedback edge, no una excusa para fusionar “entitle + meter + bill” en un solo blob.
También hay un parallel cost loop. Revenue pregunta “¿qué pagaron?” Cost pregunta “¿qué pagamos nosotros por servirles?” Mezclar ambas es cómo los equipos underprice para siempre o cortan features en pánico sin datos.
---
header: Commercial architecture - revenue chain and FinOps cost loop
legend:
- color: "#3B82F6"
text: "Revenue chain"
- color: "#F59E0B"
text: "Cost loop (FinOps)"
---
flowchart TB
subgraph Revenue["Revenue chain"]
direction LR
A[Catalog] --> B[Agreement]
B --> C[Provision]
C --> D[Entitlements]
D --> E[Metering]
E --> F[Billing]
F --> G[Cash / RevOps]
E -.->|caps / fair use| D
end
subgraph Cost["Cost loop (FinOps)"]
direction LR
H[Allocate spend] --> I[Unit economics]
I --> J[Budgets / kill-switches]
J --> K[Price or package change]
end
E -.-> H
K -.-> A
classDef revenue fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
classDef cost fill:#F59E0B,stroke:#D97706,stroke-width:2px,color:#fff
class A,B,C,D,E,F,G revenue
class H,I,J,K cost
Los meters alimentan entitlements (caps), invoices (más tarde) y FinOps (siempre). Puedes meter para caps y fair use mucho antes de cobrar una tarjeta. Las flechas discontinuas son feedback: usage informa allow/deny y cost; cost informa packaging.
FinOps es solo un actor — y solo en el lado del coste. La chain falla cuando todo se vuelca en “pricing”, “billing” o “engineering will add a flag”. Nombra a los owners, aunque al principio una persona lleve tres hats.
| Actor | Owns in the chain |
|---|---|
| Product / packaging | Catalog: plans, SKUs, limits, trials |
| GTM / growth | Who may join; invite vs open; soft-launch vs paywall |
| Platform / engineering | Provisioning, entitlements at request time, metering events |
| Ops / admin | Live overrides, caps, complimentary access, kill-switches + audit |
| Billing / finance | Subscriptions, invoices, renewals, revenue recognition |
| Customer (buyer) | Plan, usage, invoices (portal) |
| FinOps | What we pay; margin; budgets; cost kill-switches |
| Legal / compliance | Tax, merchant-of-record vs Connect, entity — before “charge” go-live |
| RevOps / CS (later) | Activate, expand, churn, support load |
RevOps alinea sales, marketing y customer success alrededor del revenue lifecycle — quién posee activation, expansion, renewal y señales de churn. Suele importar después de Charge. Al principio suelen ser runbooks de founder + CS, no un equipo dedicado ni una superficie de producto.
Dos caminos, mismo sistema — ledgers distintos:
---
header: Actors on revenue vs cost ledgers
legend:
- color: "#3B82F6"
text: "Revenue ledger"
- color: "#F59E0B"
text: "Cost ledger"
---
flowchart LR
subgraph Rev["Revenue ledger"]
direction LR
P[Product] --> GTM[GTM] --> PL[Platform] --> OPS[Ops] --> BIL[Billing] --> CU[Customer]
end
subgraph Cost["Cost ledger"]
direction LR
U[Usage / infra] --> FO[FinOps] --> FB[Package / price / gates]
end
PL -.-> U
FB -.-> P
classDef revenue fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
classDef cost fill:#F59E0B,stroke:#D97706,stroke-width:2px,color:#fff
class P,GTM,PL,OPS,BIL,CU revenue
class U,FO,FB cost
Pricing / catalog / billing viven en el revenue ledger. FinOps vive en el cost ledger. Ninguno sustituye al otro. Cuando el margin falla, FinOps informa a Product; cuando un tenant supera el limit, Platform enforce y Ops puede override — con audit trail, no un DM de Slack y un deploy olvidado.
Estas rules son la diferencia entre “tenemos pricing” y “tenemos commercial architecture”. Si las saltas, reconstruirás el mismo lío cada seis meses. Bajo cada rule: una strategy que puedes shippear sin esperar al billing suite perfecto.
3.1 One product catalog
Plans, SKUs, limits y trial rules viven en una source of truth. Pricing page, admin y billing la leen todos.
Strategy: Pon el catalog en código versionado o una tabla DB pequeña — plan_id, display name, limits, trial days, module defaults. La /pricing pública y el copy i18n importan ese modelo; no redefinen tiers. Cuando llegue billing, crea productos MoR desde los catalog IDs (map 1:1), nunca al revés.
---
header: One catalog as source of truth
legend:
- color: "#10B981"
text: "Catalog SoT"
- color: "#3B82F6"
text: "Surfaces that read the catalog"
---
flowchart TB
CAT[(Catalog SoT)]
CAT --> WEB[Pricing page]
CAT --> ADM[Admin console]
CAT --> RES[Entitlement resolver]
CAT --> MOR[Billing MoR 1:1 SKUs]
classDef sot fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
classDef consumer fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
class CAT sot
class WEB,ADM,RES,MOR consumer
3.2 Entitlements ≠ feature flags
Flags = rollout. Entitlements = verdad commercial + security en request time.
Strategy: Un resolver: tenant → plan + overrides → { modules, caps }. Llámalo desde API middleware o una policy layer compartida en cada path de pago/coste. Reserva flags estilo LaunchDarkly para “¿está live este code path?”, no para “¿este tenant está en Pro?”.
---
header: Feature flags vs entitlement resolver
legend:
- color: "#3B82F6"
text: "Request / resolve inputs"
- color: "#8B5CF6"
text: "Decision"
- color: "#10B981"
text: "Allow"
- color: "#EF4444"
text: "Deny / not shipped"
---
flowchart TD
R[API request] --> F{Feature flag: code live?}
F -->|No| X[Not shipped]
F -->|Yes| V[Entitlement resolver]
V --> C[Catalog defaults]
V --> O[Tenant overrides]
V --> M[Monetization / kill-switch mode]
C --> D{Allowed?}
O --> D
M --> D
D -->|Yes| OK[Proceed + meter if needed]
D -->|No| DENY[Structured deny + upgrade CTA]
classDef ok fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
classDef deny fill:#EF4444,stroke:#DC2626,stroke-width:2px,color:#fff
classDef decide fill:#8B5CF6,stroke:#7C3AED,stroke-width:2px,color:#fff
classDef input fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
class OK ok
class DENY,X deny
class F,D decide
class R,V,C,O,M input
3.3 Denied UX forma parte del engine
Un fallo de entitlement devuelve un mensaje claro de upgrade / denied, nunca un silent fail.
Strategy: Deny payload estándar: code, limit, current, upgrade_to, cta_url opcional. La UI mapea codes a copy (“Storage full — upgrade to Plus”). Loguea denials con tenant + capability para product/FinOps. Nunca un 403 crudo con body vacío.
3.4 Meter before you bill
Incluso productos seat/subscription hacen metering para caps, fair use y FinOps.
Strategy: Emite usage events en los edges que cuestan dinero (email, storage, AI tokens, maps, outbound sync). Guarda tenant_id, metric, quantity, at. Empieza con events append-only + totales en admin — sin math de invoice todavía. Los caps leen los mismos meters después; billing los ratea después.
3.5 Overrides son first-class
Complimentary access, custom caps, extend trial — con audit.
Strategy: Modela overrides como data en el tenant: plan_override, cap_overrides, complimentary_until, notes. Admin UI escribe; cada write añade who / when / what / before / after. El resolver mergea catalog defaults ← overrides. Sin hotfix branches para “dales Plus”.
3.6 Growth is gated
Invite vs open, monetization mode, cost kill-switches — sin redeploy.
Strategy: Tabla de global settings (o config service): registration_mode (invite / open), monetization_mode (soft_launch / trial_enforced / paid_enforced), kill-switches por capability cara. Enforce en create-tenant y en features costosas. Defaults = soft-launch de hoy; pasar a paid es un settings change, no un release train.
3.7 Cost y revenue son ledgers separados
FinOps (lo que pagamos) nunca sustituye Pricing (lo que pagan ellos).
Strategy: Dos vistas: revenue (plan, MRR, invoices) y cost (vendor invoices + usage allocated). Unit economics = cost per tenant vs price per tenant. Cuando el margin se rompe, cambia package/price o kill-switch — no lo “arregles” fingiendo que el cost es una plan feature.
3.8 Legalize before charge
Entity, tax/VAT, MoR vs Connect (o equivalente) como policy escrita antes del billing go-live.
Strategy: Un go-live memo corto: quién vende, quién factura, tratamiento VAT, MoR vs marketplace Connect, cuándo empiezan los books a reconocer revenue. Bloquea checkout de production hasta que exista el memo. Product puede terminar Encode en paralelo; Charge espera al memo, no a un teatro legal perfecto para siempre.
Commercial object (general): a menudo el tenant / account / workspace es el SKU primario. Los seats pueden llegar después. Forzar “per user” como único modelo temprano encierra el packaging en una forma que puede no encajar con value (o cost). Strategy: price y entitle el tenant primero; añade seat SKUs cuando el cost o value por seat esté probado.
No necesitas diez product epics el día uno. Sí necesitas tres layers que sigan distintos en design, aunque el delivery se comprima.
| Layer | Capabilities | Question |
|---|---|---|
| Define | Product catalog & packaging | What SKUs/plans exist? Limits? Trial? |
| Enforce | Entitlements, growth/policy gates, admin overrides | What may this tenant do right now? Who may join? Can ops change it live? |
| Monetize & operate | Metering, billing, customer portal, FinOps, money-flow/compliance, RevOps | How do we charge, show usage, protect margin, stay legal? |
---
header: Capability layers - Define, Enforce, Monetize
---
flowchart TB
D["1. Define - catalog and packaging"]
E["2. Enforce - entitlements, gates, admin"]
M["3. Monetize - meter, bill, portal, FinOps"]
D --> E --> M
Define es aburrido a propósito: plans y limits versionables. Si /pricing es una tabla hecha a mano que discrepa del runtime, no tienes catalog — tienes marketing.
Define — solution: un módulo Catalog exportado a web, API y (luego) jobs de sync de billing. Snapshot o version ID en cada entitlement resolution para que el audit sepa qué catalog aplicó.
Enforce es donde la verdad commercial encuentra el request path. Resolve plan → modules/caps allowed → allow o deny. Soft-launch puede seguir siendo “pilots get broad access”, pero como override o mode explícito — no como “enforcement doesn’t exist yet”.
Enforce — solution: middleware / policy helper en cada ruta costosa; global growth settings + per-tenant overrides; pilot = complimentary o monetization_mode = soft_launch, no checks ausentes.
Monetize & operate es donde viven money y margin. Viene después de Define + Enforce salvo que la ley fuerce billing primero — y aun así, catalog + entitlements siguen siendo el product SoT.
Monetize — solution: productos MoR clonados desde catalog IDs; webhooks actualizan tenant.plan / paid flags; portal lee el mismo catalog + meters. FinOps empieza como spreadsheet + vendor dashboards; budgets in-app solo cuando existan meters.
Sequence rule: no implementes billing / portal antes de Define + Enforce. La legal / money-flow policy viene antes del billing go-live. Billing no debe inventar una segunda plan list.
After you charge: los subscription webhooks (o equivalente) son el SoT del paid state — actualizan plan / entitlements. El billing provider mapea 1:1 al catalog. Es SoT de si pagaron; no es SoT de lo que un plan significa.
---
header: Checkout - catalog meaning vs MoR paid state
---
sequenceDiagram
participant Buyer
participant App
participant MoR as Billing MoR
participant Cat as Catalog
participant Ent as Entitlements
Buyer->>App: Checkout
App->>Cat: Resolve plan SKU
App->>MoR: Create or confirm subscription
MoR-->>App: Webhook paid / renewed / canceled
App->>Ent: Update tenant plan from webhook
Note over Cat,Ent: MoR equals paid state - Catalog equals plan meaning
Si la chain es el target, estos atajos la rompen. Cada uno con un recovery move concreto:
| Anti-pattern | Fix |
|---|---|
| Pricing page as the product | Drive the page from the catalog; delete duplicate hard-coded tiers |
| Payment provider as second catalog | MoR SKUs = catalog IDs only; regenerate from catalog, never edit live in the dashboard as SoT |
| Feature flags as entitlements | Split: flags for rollout, entitlement resolver for commercial allow/deny |
| Hard-coded limits in random routes | Central caps table + one assertCap() / assertModule() helper |
| Billing before catalog + entitlements | Freeze checkout; finish Encode; then map MoR 1:1 |
| Silent denial | Structured deny codes + upgrade CTA on API and UI |
| Productizing everything early | Keep FinOps/RevOps as runbooks until meters and charge path exist |
Orden de recovery cuando ya tienes un lío:
---
header: Recovery order when commercial architecture is a mess
---
flowchart LR
A[Extract catalog] --> B[One resolver] --> C[Centralize checks] --> D[Admin overrides] --> E[Webhooks = paid SoT]
Extract desde la pricing page y dashboards MoR → wire one resolver → centraliza hard-coded checks → admin overrides → solo entonces confía en webhooks como paid-state SoT.
Architecture es abstracta hasta que ops puede moverse sin engineering.
World-class se siente así: un pilot necesita complimentary Plus treinta días — ops flips a flag, pone expiry, audit log who/why. Sube el vendor cost — alguien togglea un kill-switch en una integración cara sin deploy de viernes noche. Soft-launch cierra — cambian registration_mode y monetization_mode, y create-tenant / upgrade paths siguen al momento.
Si cualquiera de eso sigue pidiendo un hotfix branch, la chain está incompleta. Admin no es un UI nice-to-have; es cómo los overrides se vuelven first-class en lugar de excepciones tribales.
La capacidad es finita. Aun así puedes mantener boundaries aunque combines delivery. El error no es shippear Encode en un milestone — es shippear Encode + Stripe como un blob indiferenciado.
| Delivery slice | Keep separate in design (even if shipped together) |
|---|---|
| Encode | Catalog + entitlements + growth gates + admin overrides |
| Charge | Money-flow / compliance policy then billing (catalog ↔ subscriptions ↔ entitlements) |
| Scale | Metering depth + customer portal (+ optional in-app FinOps) |
---
header: Encode, Charge, Scale delivery path
legend:
- color: "#3B82F6"
text: "Encode"
- color: "#10B981"
text: "Charge"
- color: "#8B5CF6"
text: "Scale"
---
flowchart LR
subgraph Encode
A1[Catalog] --- A2[Entitlements] --- A3[Gates] --- A4[Admin]
end
subgraph Charge
B1[Legalize] --> B2[Subscribe] --> B3[Webhooks update plan]
end
subgraph Scale
C1[Meters] --> C2[Portal] --> C3[Optional FinOps UI]
end
Encode --> Charge --> Scale
classDef encode fill:#3B82F6,stroke:#1E40AF,stroke-width:2px,color:#fff
classDef charge fill:#10B981,stroke:#059669,stroke-width:2px,color:#fff
classDef scale fill:#8B5CF6,stroke:#7C3AED,stroke-width:2px,color:#fff
class A1,A2,A3,A4 encode
class B1,B2,B3 charge
class C1,C2,C3 scale
Encode es la barra de soft-launch que aún se ve industry-shaped: one catalog, enforce en el request path, gate growth, ops override con audit. Puede que aún no cobres — y está bien.
Encode — playbook: (1) catalog model + /pricing cableada, (2) entitlement resolver + tests Free vs Paid vs pilot, (3) global registration/monetization settings, (4) admin plan/override/audit UI. Cuatro workstreams en un milestone si hace falta — nunca un blob FR único.
Charge order: legalize → subscribe / renew / cancel → webhook updates plan. Nunca “crea products en el MoR primero y reverse-engineer el catalog después”.
Charge — playbook: go-live memo → clone catalog into MoR → checkout path → webhooks → UX thin “pay / manage”. El portal polished espera a Scale.
Scale es cuando customers ven usage e invoices in-product, los meters se enriquecen y FinOps puede pasar de spreadsheets a alerts. Optional — no es prerequisite para cobrar limpio.
Scale — playbook: deepen meters (admin first) → customer usage + invoices → optional budget alerts ligados a kill-switches.
Parallel ops track (not product): review mensual de vendor bills, disciplina de margin en el first customer, y playbooks RevOps pueden vivir como runbooks mientras Encode ships. No esperes a un FinOps UI in-app para practicar cost hygiene — y no confundas esos rituales con la commercial chain.
Úsalo como barra, no como vanity checklist. La commercial architecture es “real” cuando:
- Cambiar un plan limit no exige cazar hard-coded checks en rutas random
- Entitlements denied muestran upgrade / denied path claro — nunca silent fail
- Ops puede dar complimentary access o freeze a costly capability sin deploy
- Pricing page, admin y (luego) billing muestran el mismo catalog
- Registration / monetization mode / kill-switches son platform settings, no memoria tribal
- Money-flow policy (entity, tax, MoR) existe antes del primer charge real
- Paid state fluye billing events → plan / entitlements (sin segunda plan list en el MoR)
- Usage que cuesta dinero es visible antes de que la invoice te sorprenda
- Un FinOps soft ceiling puede disparar un kill-switch, no solo un pánico en Slack
Haz screenshot. Es el artículo entero en una card.
CHAIN
Catalog → Agreement → Provision → Entitlements → Meter → Bill → Cash
(+ meters → caps / FinOps → package or price)
LAYERS
1 Define catalog & packaging
2 Enforce entitlements · growth gates · admin overrides
3 Monetize meter · bill · portal · FinOps · compliance · RevOps
DELIVERY
Encode → Charge → Scale
(legalize before Charge; meters before fancy FinOps UI)
NEVER
· Second catalog in the payment provider
· Silent 403 with no upgrade path
· Billing before catalog + entitlements
· Soft-launch vs paid as tribal memory
Una pricing page vende la historia. Commercial architecture la hace enforceable.
Cinco moves para empezar Encode sin esperar a Stripe:
- Lista cada hard-coded limit — busca en routes nombres de plan, caps y checks “if Pro…”; ponlos en una página.
- Nombra el commercial object — tenant / account / workspace (seats después, salvo prueba).
- Draft catalog IDs — Free / Trial / Paid… con limits y trial rules; cablea
/pricingsolo a ese modelo. - Elige los knobs —
registration_mode,monetization_mode, y qué features caras llevan kill-switches. - Nombra quién puede override — un ops owner para complimentary / cap overrides + hábito de audit log (aunque sea un spreadsheet al inicio).
Cuando esas cinco son verdad, tienes el skeleton de Encode. Charge es una decisión posterior — no un sustituto de este trabajo.
Una pricing page vende la historia. Stripe (o cualquier MoR) mueve el dinero. Ninguno es la architecture.
Commercial architecture es la chain — catalog → agreement → provision → entitlements → meter → bill → cash — más el cost loop, más los actors que poseen cada eslabón. Construye Define y Enforce antes de celebrar Charge. Mantén one catalog. Trata overrides y growth gates como product, no como folklore.
Esa forma funciona en cualquier project. Los nombres de plans y providers cambiarán. La chain no debería.