Franchising and commissions module
- Empréstimo Sábio
- Functional analyst and developer
- PHP, MySQL, WordPress, React
- Live in production, still in active development
Commission isn't one number. It's a stack of numbers.
A collaborator's rate sits inside a franchising's rate. A team leader takes a cut of what their team produces. A payout is promised at one moment but only becomes due when a deal reaches a status the business defines – sometimes months later.
Once that stack is real, two things break. Nobody can say with certainty what rate applied to a specific deal after the tables have changed. And nobody can say which technically-won deals have actually been paid.
Spreadsheets survive the first few offices. They do not survive the fifth franchising, the third rate revision, or the first dispute over a commission that should have been settled three months ago.
What was built
Four connected concerns, shipped in phases against the client's already-live site rather than as a green-field rebuild. No downtime.
- Network and roles. Franchising registration, an onboarding landing page, a per-franchising dashboard. Roles – Admin, Master, Team Leader, Collaborator – with their own permissions. Monthly goals per collaborator and per franchising.
- Commission engine. A master commission table plus an independently configurable table for each franchising. Per-collaborator, per-bank percentages. A team-leader cut layered on top. Live calculation shown on every process.
- Pipeline and settlement. Bank, internal status, and status-specific detail fields on every process. Commission frozen automatically when a process reaches a payment-triggering status. An independent paid/pending flag with its own change history.
- Reporting and oversight. Filterable by franchising, collaborator and period, with Excel export. Pipeline versus booked amounts. Month-by-month financing evolution. Volume by credit typology and partner bank. Collaborator ranking against goal. A one-click view of everything pending settlement.
Architecture
The network already ran its public site and franchising portal on WordPress, and rebuilding was not on the table. The engine was therefore built as six cooperating modules rather than a monolith: a dedicated PHP API and MySQL database sitting behind the existing WordPress install, with a purpose-built React app mounted into it and reaching the API through WordPress's REST proxy.
React app
mounted in the client's site
REST proxy
auth and routing
below this line, nothing depends on the CMS
PHP API
commission maths and audit rules
MySQL
versioned history, uniqueness enforced by the database
The division keeps every concern testable and replaceable on its own. Commission maths and audit rules live entirely in the API and the database, independent of WordPress; the plugin and the React app only ever see the API's output. Nothing about the commission engine depends on the CMS underneath it.
Engineering decisions
Resolution with no silent fallback
The effective rate resolves collaborator → franchising, and stops. A missing or 0% franchising rate is never filled in from the master table – it means the commission does not apply.
A configuration gap surfaces as a visible N/A, never as a guessed number. A system that guesses produces wrong payouts that nobody catches.
Crystallization at the payment trigger
Commission is calculated and frozen the instant a process enters a payment-triggering status – company, master, team-leader and collaborator cuts, plus the exact percentages and the identities behind them.
A rate change next month never rewrites what was promised on a deal already committed. This is the decision that makes it possible to answer, months later, the question "what rate applied to this process?"
Two audit patterns, matched to what is actually versioned
Rate tables use full soft-delete-and-reinsert versioning, because the whole row is the thing with history.
A single settlement flag gets a slimmer append-only log instead – who flipped it, to what, when. Re-versioning a frozen commission snapshot because one boolean changed would be waste, and noise in the history.
Uniqueness enforced by the database
MySQL has no partial unique index. The rule "one active row per key, old rows stay for history" is enforced with a stored generated column that resolves to the key only while the row is live.
The database refuses a duplicate active row. The alternative is application code that can forget to check – and one day does.
Zero-downtime schema discipline
Live in production since phase three. Every change ships as its own dated, safe-to-reapply migration – never a hand edit, never a rebuild. The schema's full history is reviewable one migration at a time.
Who this is for
None of this is specific to credit intermediation. The shape is structural: selling through franchised offices, brokers or agents; rates that vary by office, person, product or partner and change over time; needing to answer months later what rate applied to a specific deal; payouts that happen in stages and eventually require someone to ask whether a given one was actually paid.
Adjacent sectors where the same shape turns up: mortgage brokerages, insurance MGAs, real-estate franchise brands, equipment and solar dealer networks, and tiered referral and affiliate programmes.