MLM & Affiliate System 6.1.8

11 August 2026

This release has upgrade notes. Read them before updating — they describe behaviour changes that need your attention.

AFFILIATEMLMREWARDSCOMMISSIONSCRONCURRENCYADMINLICENSINGPERFORMANCENETWORKDIRECTBINARYUNILEVELTREE

MLM & Affiliate v6.1.8

Release Date: August 11, 2026 Tags: AFFILIATE, MLM, REWARDS, COMMISSIONS, CRON, CURRENCY, ADMIN, LICENSING, PERFORMANCE, NETWORK, DIRECT, BINARY, UNILEVEL, TREE

Overview

This release corrects what the programme pays, and to whom, in both halves of the reward engine. The half that pays the instant a referred customer acts applied its percentage to the raw transaction figure and never converted it into the currency the reward pays in — the pairing a new install ships — so a commission on an ETH sale or a naira deposit bore no relation to the activity's worth. The hourly evaluator was paying members commission on their own volume, freezing a percentage at the first volume it saw in the period, and paying twice for a transaction stamped on a period boundary. No wrong figure has been paid unless it was claimed — read the Upgrade Notes and inspect existing rewards.

Two admin doors were also closed: changing a live condition's reward currency or payout wallet silently re-denominated every reward already earned under it, and it now refuses unless the operator confirms. An already-paid reward can no longer be marked unclaimed, which used to manufacture a reward the affiliate could see, press, and never collect.

The network tree is also much faster to draw, and public affiliate figures no longer carry a dollar sign.

Requires Core v6.6.3.

Update Instructions

pnpm updator

Then restart the backend:

pm2 restart all

Upgrade Notes

  • Percentage commissions accrued on activity in a currency other than the one the condition pays in are the wrong size, and those rows are still in the table. Until this release the percentage was taken from the raw transaction figure with no conversion between the two currencies, so the figure accrued had no relation to what the activity was worth — far too small where the activity currency is the dearer of the two, far too large where it is the cheaper. None of it has moved on its own: a reward only moves money when the affiliate claims it, so every one of these still unclaimed is recoverable if you go and look now. List your percentage conditions and inspect the unclaimed rewards under them before they are claimed. Every install is in scope, including an untouched one — the conditions the installer seeds pay in USD while the platform's markets are denominated in USDT, so their rewards are out by a fraction of a percent. The ones worth your time are the conditions paying in a currency your members do not actually transact in, where the figure is out by orders of magnitude. Anything already claimed was paid at the wrong figure and cannot be recovered from here.
  • Rewards already paid on a member's own volume stay where they are. The evaluator stops creating them, but every row it created before tonight is still in the table, still counted in the sponsor's unclaimed balance, and still claimable. Look for rewards where the sponsor and the referred member are the same person, and decide whether to remove them before they are claimed. Binary and Unilevel registration writes one of these rows for any sponsor who has nobody above them, and on Binary one is also written the first time a member opens their own network page — and they stay in the table if the programme is later switched to Direct.
  • Check for pairs of rewards in adjacent periods. Until this release, a qualifying transaction timestamped exactly on a period boundary was counted into both the closing period and the opening one, producing two reward rows for one transaction. Two rewards for the same sponsor and the same referred member in two consecutive periods, both of them near the boundary, is that defect.
  • Percentage rewards that were short-paid in earlier periods stay short. Each run re-evaluates only the period in progress and the one just closed, so the new top-up repairs those two and nothing older. If a large deposit landed late in a period before this update, the commission on it was calculated against the volume seen earlier that period and has to be corrected by hand.
  • Changing a condition's reward currency or payout wallet while unclaimed rewards exist under it is now refused. The admin screen asks for confirmation and shows how many rewards and what they total; anything driving the API directly gets a refusal naming the acknowledgement to re-send with, and must be updated to send it.
  • Support can no longer reverse a claim that was actually paid. Where a payout genuinely failed to reach a customer, issue a new reward rather than marking the old one unclaimed.

Added

A guard on changing a live condition's reward currency

A reward row records a number and nothing else — no currency, no wallet type. The claim reads both from the condition at the moment it is claimed, so editing them on a condition that already has rewards accrued against it re-denominated all of them: two hundred unclaimed rewards of 50 each, earned as 50 USD, become 50 BTC apiece the moment the programme is switched to crypto, with nothing anywhere recording what they were earned in.

  • Added a refusal when a condition's reward currency or payout wallet type is changed while unclaimed rewards exist under it. The refusal names how many rewards are outstanding and what they total under the current settings, and an operator who has decided to re-denominate anyway can confirm and proceed. Re-sending a field unchanged is not a change, so unrelated edits to a condition's name or image are unaffected.
  • Added a confirmation prompt on the reward-condition screen for that refusal, and for any future one of the same shape. The screen previously recognised only the overlapping-condition warning, so a second guard would have reached the operator as a plain error with no way past it.

Changed

The affiliate network tree

  • Changed the downline to be fetched a generation at a time instead of a member at a time. Building the tree previously issued one database query for every single person in it, in sequence, so a downline of a thousand people meant a thousand round trips and the page took as long as that took. It now issues two queries per generation — one for that generation's referrals, one to count their rewards — so a seven-level tree costs roughly fourteen queries rather than one per person. A generation wider than a thousand members is read in batches and costs a few queries more — still counted per generation, never per person. The same members come back in the same order, with the same reward and team counts. The one difference in shape is that a member sitting under two sponsors at once — which only an operator-created referral can produce — is now attached to the shallower of them rather than to whichever branch happened to be walked first.
  • Changed each member's reward count to be counted by the database rather than assembled in memory. A row was previously pulled back for every reward of every member, on a page that only ever prints how many there are.
  • Applies to the Unilevel system only. A programme running Direct or Binary builds its tree by another route, which is unchanged here.

The dashboard's reward history

  • Changed each row of the dashboard's reward history to report the currency it is denominated in and whether the figure is an amount or a percentage. The list returned bare numbers, so an install paying one commission in USDT and another in naira handed back two different currencies in one list with nothing to tell them apart. The web dashboard only charts these rows, so this is visible to anything reading the endpoint directly.

Fixed

Members earning commission on their own money

Registration deliberately records a member as their own sponsor when they sit at the top of a tree and have nobody above them, and the network page writes the same row for older accounts the first time they open it. Those rows exist so the tree has a root; they do not describe anyone having referred anyone. The hourly evaluator loaded every active referral and never filtered them out, so those members were paid commission on their own deposits and their own trading volume — every hour, against every active condition. The event-driven half of the engine has always refused this; the periodic half never got the same treatment, and it pays on all matching conditions where the event path pays on one.

  • Fixed the evaluator to skip referrals where the sponsor and the referred member are the same person, and to write the number skipped into the run log so an operator can see the rows exist and that they were not paid.

Percentage commissions frozen at the first hour of the period

A percentage reward is a percentage of the period's volume, and the period is still running when the first run of the day writes the row. Every later run that day found the reward already there and moved on, so the amount stayed at whatever volume the job happened to see first. A referred customer who deposited 100 at 00:10 and 50,000 at 02:00 paid their sponsor commission on the 100 — and no later run could ever correct it, because the same key that proved the reward existed was the key that stopped it being recalculated.

  • Fixed the evaluator to raise an existing reward as the period's volume grows. It stays one reward per sponsor, member and period — that is what keeps anyone from being paid twice — and the amount only ever moves upward, so a refunded transaction or a rate lowered mid-period never claws back a figure the affiliate has already been shown.
  • Fixed the handling of a period that earns more after its reward has already been claimed: the money has moved under a payment key that cannot be reused, so the run now logs the shortfall loudly for an operator to credit by hand instead of raising a figure the ledger will never match.

One transaction paying two commissions on a period boundary

The two windows the job evaluates share an endpoint — the closing period ends exactly where the opening one begins — and both were matched inclusively at both ends. Transaction timestamps are whole seconds, so a qualifying transaction stamped exactly on the boundary was counted into both windows in the same run, under two different period keys, and two reward rows were written for one transaction.

  • Fixed both windows to be half-open, so they meet at a single instant with no overlap and no gap.

Activity in the last second of a run earning nothing until the next one

The period in progress was measured up to the instant the run started. Timestamps are stored to the whole second and the comparison is made at whole-second precision, so every transaction created in the same second the evaluator began fell outside the window — not intermittently, but for the whole of that second. The reward was short until a later run picked that activity up. With the top-up above in place the gap matters more rather than less: a sponsor who claims inside it is paid the short figure, and the run can then only log the difference for an operator to credit by hand.

  • Fixed the current window to end where the next period begins rather than at the moment of the run, so the two windows read exactly as the period keys do.

A percentage commission taken from the transaction figure rather than its value

The half of the engine that pays the moment a referred customer does something — a deposit, a trade, a P2P release, an NFT purchase, an investment — took the condition's percentage of the raw transaction figure and wrote that as the reward. The reward is then paid in the currency the condition names, and the two currencies were never reconciled. A 5% condition paying USDT collected 0.1 USDT on a 2 ETH sale, about ten cents on a sale worth thousands, while a 10% condition paying USDT on a 1,000 naira deposit wrote 100 USDT for a deposit worth well under a dollar. The error ran in both directions and its size was the distance between the two currencies: a condition paying USD against USDT activity was out by a fraction of a percent, one paying USDT against BTC or naira activity by orders of magnitude. Binary and Unilevel then split that wrong figure down the upline, so one transaction wrote a wrong reward for every level it reached.

  • Fixed the percentage to be applied to the referred activity expressed in the condition's reward currency, on Direct, Binary and Unilevel alike. Rewards stay denominated in the currency the operator configured — that is what the claim has always credited — and a condition already paying in the currency its activity is denominated in is unchanged. That is not what a new install ships, though: the seeded conditions pay in USD while the platform's markets are denominated in USDT, so a stock install has been accruing every percentage commission a fraction of a percent out. The pairings that are badly wrong are the ones an operator set to a currency their members do not transact in.
  • Fixed a percentage reward being written when the platform cannot price one of the two currencies, in both halves of the engine. The reward is skipped and the currency named in the log, with the conditions it affects, because paying the unconverted figure is what turned a sub-dollar deposit into a 100 USDT commission. A fixed-amount condition is unaffected — its figure is the operator's own and is already in the currency it pays in.

The same referred deposit was worth two different commissions

A condition names the currency it pays in, and a referred customer's activity is denominated in whatever they actually moved. Valuing one against the other is a conversion, and the hourly evaluator did it through a second set of rules rather than the platform's own. Those rules disagreed with the platform's in three ways: they ignored the live quote between the exchange's own unit and the dollar, they could not price the dollar itself, and they went on pricing a currency an operator had switched off. So the same referred purchase earned a different commission depending on which half of the engine paid it — 200 USDT valued at $200.00 by one and $199.81 by the other, on rewards sitting side by side in the same affiliate's history.

  • Fixed the evaluator to price activity through the platform's own conversion rates, which Core v6.6.3 now exports as the single source for this. The second set of rules is gone, so both halves of the engine value the same activity identically.

Rewards recorded at a precision their currency cannot pay

The evaluator values a referred customer's volume in the currency the reward pays in, so a percentage of it rarely lands on a figure that currency can actually pay. The reward row happily held 9.9904 while the claim credited the wallet 9.99 — the record and the payment disagreeing, with the difference landing nowhere and no entry saying where it went.

  • Fixed a percentage reward to be rounded to the reward currency's precision before it is written, both when it is first created and when it is topped up, so the number the affiliate is shown, the number recorded and the number paid are one number. A fixed-amount reward is untouched — it is written exactly as the operator configured it.

An admin un-claim that made a reward permanently uncollectable

Marking a paid reward unclaimed did not give it back. The payout goes out under a payment key that can only be used once, so flipping the reward back to unclaimed produced one the affiliate could see, could press, and could never collect — every attempt failed and rolled itself back. The amount rejoined their unclaimed balance in the meantime, and support got a ticket about a button that errors.

  • Fixed the admin reward status endpoint to refuse reversing a reward that was genuinely paid out, and to say that a new reward must be issued instead. Marking a reward claimed by hand is still allowed — that is the "I already paid this one myself" case the endpoint exists for.
  • Fixed the same endpoint reporting success for a reward id that matches nothing. It now reports the reward as not found.

The reward-condition editor was served without a licence check

That screen hides the admin navigation because it is shaped like a settings page, and the shortcut that hid the navigation dropped the licence gate along with it. Every other affiliate admin screen is gated, and this is the one screen in the addon that writes reward conditions.

  • Fixed the reward-condition editor to sit behind the same licence check as the rest of the affiliate admin.

Public affiliate figures presented as dollars

The landing page's headline totals and the top-affiliate leaderboard are sums of every reward paid, and each reward is denominated in whatever currency its own condition names — an operator-editable setting that differs from condition to condition. Adding a BTC reward to a naira one produces a number that is not a quantity of anything, and it was printed with a dollar sign in front of it, on a page open to everybody.

  • Fixed the headline totals and the leaderboard to print the magnitude without a currency mark. The figures themselves are unchanged.
  • Fixed the live activity feed to print each reward in its own currency, which the page already received and discarded — a 0.001 BTC reward was publicly advertised as "+$0.00".