DEV Community

TECH WEB MANTRA
TECH WEB MANTRA

Posted on

MLM Software Company in Delhi: Engineering an Audit-Ready Commission Ledger

title: "MLM Software Company in Delhi: Engineering an Audit-Ready Commission Ledger"
published: true
description: "A developer-focused guide to designing traceable commission calculations using immutable events, idempotency, versioned rules, and reconciliation."
tags: softwarearchitecture, backend, database, testing

cover_image:

MLM Software Company in Delhi: Engineering an Audit-Ready Commission Ledger

Commission software is often presented as a collection of dashboards, genealogy trees, wallets, and payout reports. From an engineering perspective, however, the most difficult requirement is much less visual:

Can the system explain exactly why a financial entry exists?

A distributor may see a ₹500 credit, but the application should know which order produced it, which rule version was applied, which qualification conditions passed, when the calculation ran, and whether a later refund changed the result.

At Tech Web Mantra, we treat this as a ledger-design problem rather than a dashboard problem. This article explains how an MLM software company in Delhi can build a traceable commission engine without turning financial history into a collection of mutable balance fields.

The discussion is strictly about software architecture. It does not promote recruitment, promise earnings, or evaluate the legality or suitability of any business model.

Why a Mutable Wallet Balance Is Not Enough

A basic implementation may store the current wallet balance directly on the distributor record:

distributors
- id
- name
- wallet_balance
Enter fullscreen mode Exit fullscreen mode

Whenever the application calculates a commission, it increases wallet_balance. When a payout is processed, it decreases the same value.

This looks simple, but it creates an immediate audit problem. The current balance shows the result of earlier operations without preserving enough information to reconstruct them.

If the balance is incorrect, developers must search application logs, orders, payout records, and possibly manual database updates to understand what happened.

A safer model treats the displayed balance as the result of ledger entries:

wallet_ledger
- id
- distributor_id
- transaction_type
- amount
- status
- source_type
- source_id
- rule_version
- idempotency_key
- created_at
Enter fullscreen mode Exit fullscreen mode

The available balance can then be calculated from approved entries:

SELECT COALESCE(SUM(amount), 0) AS available_balance
FROM wallet_ledger
WHERE distributor_id = :distributor_id
  AND status = 'approved';
Enter fullscreen mode Exit fullscreen mode

For large datasets, the system may maintain a cached balance for performance. The ledger should still remain the source of truth.

Model Business Activity as Events

Commission calculations should respond to confirmed business events rather than assumptions.

Possible events include:

DistributorRegistered
DistributorVerified
OrderPlaced
PaymentConfirmed
OrderDelivered
OrderCancelled
RefundApproved
CommissionCalculated
CommissionApproved
PayoutCompleted
Enter fullscreen mode Exit fullscreen mode

Creating an order does not necessarily mean that a commission is eligible. The company’s documented policy may require successful payment, delivery, or completion of a return period.

The calculation engine should respond only to the event defined as eligible by the approved business rules.

A simplified event structure might look like this:

type BusinessEvent = {
  id: string;
  type:
    | "ORDER_PAID"
    | "ORDER_DELIVERED"
    | "ORDER_CANCELLED"
    | "REFUND_APPROVED";
  entityId: string;
  occurredAt: string;
  payload: Record<string, unknown>;
};
Enter fullscreen mode Exit fullscreen mode

This separation provides two benefits. First, it prevents unfinished transactions from entering commission calculations. Second, it preserves a timeline that developers and administrators can inspect later.

Make Every Calculation Idempotent

Payment providers, message queues, and webhook systems may deliver the same event more than once. If every delivery generates a new commission, the wallet will contain duplicate credits.

The calculation process must therefore be idempotent: processing the same event repeatedly should produce the same final state as processing it once.

An idempotency key can combine the event, beneficiary, and commission rule:

function createIdempotencyKey(
  eventId: string,
  distributorId: string,
  ruleId: string
): string {
  return `${eventId}:${distributorId}:${ruleId}`;
}
Enter fullscreen mode Exit fullscreen mode

The database should enforce uniqueness:

CREATE UNIQUE INDEX uq_wallet_ledger_idempotency
ON wallet_ledger (idempotency_key);
Enter fullscreen mode Exit fullscreen mode

Application-level checks are useful, but the database constraint provides the final protection against concurrent requests.

async function createCommissionEntry(input: CommissionInput) {
  const idempotencyKey = createIdempotencyKey(
    input.eventId,
    input.distributorId,
    input.ruleId
  );

  return database.walletLedger.insert({
    distributorId: input.distributorId,
    amount: input.amount,
    transactionType: "COMMISSION",
    status: "pending",
    sourceType: "order",
    sourceId: input.orderId,
    ruleVersion: input.ruleVersion,
    idempotencyKey
  });
}
Enter fullscreen mode Exit fullscreen mode

If two workers process the same event simultaneously, one insert succeeds and the other encounters the uniqueness constraint. The second operation should be treated as an already-processed event rather than a system failure.

Version Compensation Rules

Business rules change. A company may update qualification requirements, commission percentages, rank conditions, or payout limits.

Editing the existing rule in place creates a historical problem. When an administrator reviews a six-month-old entry, the current configuration may no longer explain the original calculation.

Rules should therefore be versioned:

commission_rules
- id
- rule_code
- version
- effective_from
- effective_until
- configuration
- created_at
Enter fullscreen mode Exit fullscreen mode

A ledger entry stores the exact version used:

{
  "ruleCode": "LEVEL_COMMISSION",
  "ruleVersion": 3,
  "sourceOrderId": "ord_10482",
  "eligibleVolume": 5000,
  "rate": 0.05,
  "calculatedAmount": 250
}
Enter fullscreen mode Exit fullscreen mode

Old rule versions should become inactive, not overwritten or deleted. This allows the application to reproduce the original result during an audit or support investigation.

Store the Explanation with the Result

A commission engine should not return only an amount. It should also return an explanation of the calculation.

type CalculationResult = {
  eligible: boolean;
  amount: number;
  currency: string;
  ruleCode: string;
  ruleVersion: number;
  sourceEventId: string;
  sourceOrderId: string;
  evaluatedConditions: Array<{
    name: string;
    passed: boolean;
    observedValue: string | number | boolean;
  }>;
};
Enter fullscreen mode Exit fullscreen mode

A result might contain:

{
  "eligible": true,
  "amount": 250,
  "currency": "INR",
  "ruleCode": "LEVEL_COMMISSION",
  "ruleVersion": 3,
  "sourceEventId": "evt_8021",
  "sourceOrderId": "ord_10482",
  "evaluatedConditions": [
    {
      "name": "account_active",
      "passed": true,
      "observedValue": true
    },
    {
      "name": "minimum_sales_volume",
      "passed": true,
      "observedValue": 5000
    },
    {
      "name": "order_status",
      "passed": true,
      "observedValue": "delivered"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The distributor dashboard does not need to expose every internal field. It can provide a plain-language summary. Authorized administrators can access the detailed explanation when investigating a question.

Use Reversals Instead of Deleting History

Suppose an eligible order generates a commission and is later refunded. Deleting the original ledger entry removes part of the financial history.

A better approach preserves the original credit and creates a linked reversal:

Original entry:  +₹250
Refund reversal: -₹250
Enter fullscreen mode Exit fullscreen mode

The reversal record should reference the original entry:

wallet_ledger
- id
- reversal_of_entry_id
- source_type
- source_id
- amount
- reason_code
Enter fullscreen mode Exit fullscreen mode

Example:

{
  "transactionType": "REVERSAL",
  "amount": -250,
  "reversalOfEntryId": "ledger_551",
  "sourceType": "refund",
  "sourceId": "refund_118",
  "reasonCode": "ORDER_REFUNDED"
}
Enter fullscreen mode Exit fullscreen mode

This makes the final balance correct without hiding what occurred earlier.

The same principle should apply to authorized manual corrections. Instead of modifying historical entries, the platform creates a new adjustment containing a reason and the identity of the approving administrator.

Separate Calculation from Approval

A calculated commission is not necessarily ready for payout.

A useful state model could be:

pending → approved → payable → paid
              ↘ rejected
approved → reversed
payable  → payout_failed
Enter fullscreen mode Exit fullscreen mode

These states should be represented explicitly rather than compressed into a single wallet balance.

For example:

type LedgerStatus =
  | "pending"
  | "approved"
  | "payable"
  | "paid"
  | "rejected"
  | "reversed";
Enter fullscreen mode Exit fullscreen mode

Separating calculation from approval allows the finance team to review exceptions without changing the underlying rule engine. It also prevents pending or conditional amounts from being displayed as completed payments.

Every transition should record who or what initiated it:

ledger_status_history
- id
- ledger_entry_id
- previous_status
- new_status
- changed_by_type
- changed_by_id
- reason
- changed_at
Enter fullscreen mode Exit fullscreen mode

Apply Role-Based Access Control

Transparency does not mean giving every user access to all records.

A support employee may need to view a calculation explanation without permission to alter it. A finance user may approve payouts but should not be able to edit compensation rules. A developer may inspect technical logs without accessing unnecessary identity documents.

Permissions should be based on capabilities:

const permissions = {
  SUPPORT_AGENT: [
    "distributor.read",
    "commission.read",
    "order.read"
  ],
  FINANCE_REVIEWER: [
    "commission.read",
    "commission.approve",
    "payout.read",
    "payout.process"
  ],
  RULE_ADMIN: [
    "commission_rule.read",
    "commission_rule.create_version"
  ]
};
Enter fullscreen mode Exit fullscreen mode

Sensitive actions should require stronger authentication and generate audit records.

Avoid relying only on hidden buttons in the interface. Authorization must be enforced by the backend for every protected operation.

Keep Audit Logs Separate from Business Ledgers

A wallet ledger records financial changes. An audit log records who performed administrative or system actions. Although related, they solve different problems.

An audit record might include:

{
  "actorType": "admin",
  "actorId": "usr_221",
  "action": "COMMISSION_APPROVED",
  "entityType": "wallet_ledger",
  "entityId": "ledger_551",
  "requestId": "req_9372",
  "ipAddress": "[protected]",
  "occurredAt": "2026-08-14T10:30:00Z"
}
Enter fullscreen mode Exit fullscreen mode

Audit records should be append-only and protected from ordinary administrative editing.

Care is also required when logging personal data. Logs should contain enough information for security and operational review without becoming an uncontrolled copy of sensitive KYC or banking details.

Test Rules as Business Scenarios

Unit tests for formulas are necessary, but they are not sufficient. The system should also be tested using complete business scenarios.

A scenario can be written in a form that business and engineering teams both understand:

describe("commission reversal after refund", () => {
  it("creates a linked debit without deleting the original credit", async () => {
    const order = await createDeliveredOrder({
      total: 5000,
      distributorId: "dist_101"
    });

    const credit = await calculateCommission(order);

    await approveCommission(credit.id);
    await approveRefund(order.id);

    const entries = await getLedgerEntries("dist_101");

    expect(entries).toContainEqual(
      expect.objectContaining({
        id: credit.id,
        amount: 250,
        status: "approved"
      })
    );

    expect(entries).toContainEqual(
      expect.objectContaining({
        reversalOfEntryId: credit.id,
        amount: -250,
        reasonCode: "ORDER_REFUNDED"
      })
    );
  });
});
Enter fullscreen mode Exit fullscreen mode

Other important scenarios include duplicate webhook delivery, an inactive distributor, a rank change during a calculation period, an unsuccessful payout, concurrent calculation workers, and a rule-version change.

Regression tests should run whenever calculation or order-processing code changes.

Reconcile the Ledger Regularly

Even a carefully designed system needs reconciliation.

The application should compare related datasets and report inconsistencies. Examples include approved ledger entries without a valid source order, completed payouts without corresponding ledger debits, or refunds whose commission reversals were not created.

A reconciliation job can produce exceptions without silently changing records:

type ReconciliationIssue = {
  code:
    | "MISSING_SOURCE_ORDER"
    | "MISSING_REFUND_REVERSAL"
    | "PAYOUT_LEDGER_MISMATCH";
  entityId: string;
  detectedAt: string;
  details: Record<string, unknown>;
};
Enter fullscreen mode Exit fullscreen mode

Administrators can then investigate each exception using the associated records.

Automatic correction may be appropriate for carefully defined cases, but unexplained background changes can damage the audit trail. Detection and correction should remain distinct operations.

Design for Observability

Financial workflows need better observability than ordinary page views.

Every calculation should have a correlation or request identifier connecting the source event, calculation job, ledger entry, and notification.

Useful metrics may include:

commission_calculation_duration
commission_calculation_failures
duplicate_event_attempts
ledger_reconciliation_issues
refund_reversal_delay
payout_processing_failures
Enter fullscreen mode Exit fullscreen mode

Alerts should focus on operational risk. A single invalid input may require investigation, while a sudden increase in duplicate events or missing reversals could indicate a broader system problem.

Logs, metrics, and traces should help engineers diagnose failures without exposing sensitive distributor information.

Consider Performance Without Sacrificing Traceability

Calculating balances from millions of ledger entries on every request may become expensive. Performance optimization is necessary, but it should not remove the underlying transaction history.

Possible strategies include maintaining materialized balance summaries, processing calculations through queues, partitioning large ledger tables, and generating heavy reports asynchronously.

A cached balance can be updated transactionally:

1. Insert the ledger entry.
2. Update the balance summary.
3. Commit both operations.
Enter fullscreen mode Exit fullscreen mode

The platform should periodically compare the cached balance with the sum of ledger entries. If the values differ, the reconciliation system creates an exception.

The cache improves speed; the ledger preserves correctness and explainability.

Architecture Cannot Validate the Business Model

A well-engineered platform can apply configured rules accurately. It cannot determine whether the underlying compensation structure, marketing practices, or business model is legally or ethically appropriate.

Software teams should avoid presenting technical implementation as validation of a business opportunity. Companies remain responsible for genuine products or services, customer policies, privacy obligations, taxation, distributor communication, and applicable regulations.

Qualified professionals should review these areas. The software should implement an approved operating process without promising recruitment, income, returns, or commercial success.

Final Thoughts

The hardest part of building a commission platform is not displaying a wallet balance. It is preserving the evidence behind that balance.

An audit-ready system uses immutable ledger entries, idempotent event processing, versioned rules, explicit statuses, linked reversals, role-based permissions, reproducible tests, and regular reconciliation.

For an MLM software company in Delhi, these practices turn a calculation engine from a black box into an explainable business system.

A dashboard can always be redesigned. Historical trust is much harder to rebuild after records become unclear. That is why traceability should be part of the architecture from the first database migration—not an optional reporting feature added after launch.

This article was prepared for the Tech Web Mantra engineering publication with AI-assisted drafting and human review recommended before publication. It discusses software architecture only and does not promote an MLM opportunity, recruitment, investment, or guaranteed earnings.

Top comments (0)