The investment lifecycle

What happens between the debit and the payout — the four statuses, the terms snapshot, the exact settlement arithmetic, cancellation, and the two doors that can settle the same investment.

9 min readUpdated 3 August 2026lifecycle, settlement, payout, refund, cron

An AI investment has four statuses and only one of them is not terminal.

Status Meaning Money position
ACTIVE Running. The term has not expired, or has expired and has not yet been settled. Principal is out of the user's wallet
COMPLETED Settled at maturity. Payout credited
CANCELLED Ended early — by the user, or by an administrator. Full principal refunded
REJECTED Ended by an administrator, or rejected by settlement as unsettleable. Full principal refunded via the admin door; see the caveat below

ACTIVE is the default on creation. Nothing else can be set at creation through the user endpoint.

Opening an investment

The purchase panel posts the plan, the duration, the amount, the market's base and quote currency, and the wallet type. The route runs these checks in order, and any one of them refuses the whole request:

  1. The caller is signed in, and passes the invest_ai KYC feature gate if feature enforcement is on.

  2. The plan exists and is active. A deactivated plan is refused even if the user still has its id.

  3. The duration exists and is joined to that plan. The join is the contract — a duration that exists but is not attached to this plan is refused.

  4. The amount is within the plan's limits, inclusive at both ends.

  5. A wallet exists for this user, of the requested type, in the market's quote currency. On BTC/USDT that is the USDT wallet, not the BTC one. The row is locked for update.

  6. The balance covers the amount.

Only then does anything change. Inside one database transaction the addon creates the investment row and debits the wallet, so a failure at either step leaves nothing behind.

What is written on the row

symbol         BTC/USDT          base/quote, from the market being viewed
type           SPOT or ECO       which wallet the principal came from
amount         1000              in the QUOTE currency
status         ACTIVE
roiPercentage  5                 snapshot of plan.profitPercentage
result         WIN               snapshot of plan.defaultResult

roiPercentage and result are copied from the plan at purchase and are never re-read from the plan afterwards. Before this existed, settlement read the plan hours or days later — so an administrator changing a rate or an outcome silently rewrote the terms of every investment already running, and a user could be quoted one figure at purchase and paid another.

The snapshot is a server-side integrity guarantee. It is not something the investor may see: while an investment is ACTIVE, the result, roiPercentage and profit fields are nulled out of every user-facing response, because otherwise anyone with the network tab open could read the outcome days before it was due.

The debit is written as an AI_INVESTMENT transaction whose referenceId is the investment's own id, under the idempotency key investment_<id>. That transaction is the link back to the wallet the principal came from, and settlement uses it to decide where the payout goes.

After the transaction commits, three best-effort things happen — a NewAiInvestmentCreated email, an in-app notification, and the AI_INVESTMENT affiliate reward for the referrer. All three are wrapped so a failure is logged and never rolls back the investment.

While it runs

Nothing. No price is read, no position exists, no value moves. The row sits at ACTIVE until its term expires.

The user sees it in the Orders panel of the Pro trade workspace, showing the plan, the amount, the symbol and a dash where the result will be.

Settlement

Two things settle an investment automatically, and they run the identical code:

  • The hourly cron, processAiInvestments, which scans every ACTIVE investment on the install.
  • The user's own read endpoints. Listing investments or opening one runs settlement in-line for the caller's matured investments before returning.

The in-line path exists so a user who opens the tab is paid immediately rather than waiting for the top of the hour. Its errors are swallowed on purpose, so a settlement that cannot complete never blocks the page — which also means it will never tell you something is wrong.

Deriving the terms

Settlement resolves the ROI percentage from the first of these that is present:

  1. roiPercentage on the investment — the snapshot taken at purchase. This is the normal path for anything created since 6.1.1.

  2. profit on the investment — a deprecated column holding an absolute ROI amount, not a percentage. Used only by legacy rows. Reading it as a percentage was a real defect: a profit of 500 on a 1,000 principal became a 500% payout.

  3. The plan's profitPercentage, falling back to its defaultProfit. Used by pre-snapshot rows whose plan still exists.

  4. Zero. The plan is gone and nothing was recorded on the investment. No profit figure can be honestly derived from a plan that no longer exists.

The outcome is resolved the same way: the snapshot on the investment, then the plan's defaultResult, then DRAW.

DRAW is the conservative last resort. It hands the principal back and invents nothing, which is the only defensible answer when the plan that defined the outcome has been deleted. This precedence lives in one shared module used by both the cron and the admin door, because the two once disagreed on this last step — one fell back to DRAW and the other to WIN, so the same orphaned investment paid a different amount depending on which button was pressed.

The payout

With amount as the principal and roi as amount × roiPercentage / 100:

Result Credited to the wallet
WIN amount + roi
LOSS amount - roi, floored at 0
DRAW amount

A LOSS plan configured to take more than the whole principal cannot make a user owe you money. The floor at zero means the payout becomes nothing and no transaction is written at all.

The credit is an AI_INVESTMENT_ROI transaction with referenceId <id>_roi. The investment is then updated to COMPLETED, with result, roiPercentage and the absolute profit all written for later readers.

Where the payout goes

Settlement resolves the destination wallet in two attempts:

  1. The wallet referenced by the original AI_INVESTMENT funding transaction.
  2. Failing that, a wallet matching the user, the investment's type, and the quote half of its symbol.

If neither resolves, the settlement is rolled back, an ALERT is written to the log naming the investment, the user, the symbol and whether the funding transaction was found, and the investment is left ACTIVE for manual review. It is deliberately not marked REJECTED: the principal was really debited, and writing a terminal status without a refund would strand the money silently.

The one case that does write REJECTED from settlement is an investment whose amount is null or not positive. That row could not have taken any money in the first place.

The platform's own side

Every settlement books the platform's leg as well as the user's:

  • A WIN records the profit paid out as a platform loss, under reference <id>_payout.
  • A LOSS credits the forfeited amount to the Super Admin's wallet and records it as platform revenue, under reference <id>_house.
  • A DRAW records nothing. Nothing changed hands beyond the principal.

This is reporting, not a second payment to the user, and it never throws — a failure here cannot strand a settled investment. Settlements from before the 6.1.1 release are not backfilled, so the figures start from the day you upgraded. Note also that the AI Investment dashboard's own profit figure is computed from the investments themselves and is a separate number from platform profit and loss.

Cancellation

A user may cancel any ACTIVE investment from the Orders panel. There is no early-exit fee, no penalty and no partial return — the full principal goes back.

The refund is written as a REFUND transaction with referenceId <id>_refund. The suffix matters: transaction.referenceId carries a platform-wide unique index and the funding debit already claimed the bare investment id, so an earlier build's refund failed that constraint and made cancellation impossible for every investment on every install.

The investment is marked CANCELLED and then soft-deleted, in that order. The original funding transaction is preserved and annotated with a cancelled flag, a timestamp and the id of the refund — deleting it would leave a refund in the ledger with no matching charge.

A cancel arriving at the same moment as the maturity payout would refund a principal that had already been returned. Both paths re-read the investment under a row lock inside their transaction, so whichever commits second sees the other's status and stops. The user cannot be credited twice.

No email is sent on cancellation. The AiInvestmentCanceled template is seeded and the email helper accepts it, but nothing calls it.

Administrative settlement

An administrator can end an ACTIVE investment from the Investment Logs table, either one at a time or in bulk. Both routes run the same shared settlement authority as the cron.

Action Effect
Completed Settles on the derived terms, exactly as maturity would — including the platform's own leg. Ignores whether the term has expired.
Cancelled Refunds the full principal as a REFUND transaction.
Rejected Identical to Cancelled: full principal refunded.

Three transitions are refused outright: setting a status the investment already has, changing anything that is not currently ACTIVE, and — through the edit form rather than the status action — writing status as a plain column.

That last refusal is the important one. There were once two status doors and only one of them paid anything; the bulk door flipped ACTIVE investments to COMPLETED with no payout, the cron then skipped them forever because it only scans ACTIVE rows, and the record said the investment had been completed while the user had been paid nothing. Both doors now go through the same code, and the bulk action reports per-investment successes and failures rather than claiming a batch succeeded when none of it moved.

Completing a one-day-old investment on a 12 MONTH term pays the full profit immediately. There is no pro-rating anywhere in this product. If you are using the status action to clear a stuck row, check what it is going to pay first.

Deleting an investment

Deleting from the Investment Logs table refunds the principal only when it is genuinely still outstanding — that is, when the investment is ACTIVE and has not already been soft-deleted by the user's own cancel. A COMPLETED investment has already been paid, a CANCELLED one has already been refunded, and neither gets a second credit.

The refund uses reference <id>_admin_refund, and the funding transaction is preserved rather than destroyed.

Restore genuinely restores. The table is paranoid, so a deleted investment can be brought back, and the restore action no longer runs the delete path — an earlier build permanently destroyed the record and issued another refund when you pressed Restore on an already-cancelled investment.

Why a payout can never happen twice

Four independent mechanisms, worth knowing because they are what makes retrying safe:

Settlement re-reads the investment inside its transaction with a FOR UPDATE lock and abandons the run if the status is no longer ACTIVE. Two concurrent settlements of the same investment serialise, and the second one stops.

Every wallet movement carries a key. The cron payout uses ai_invest_cron_payout_<id>_<RESULT> — the outcome is part of the key because the amount varies by outcome — the admin payout uses ai_invest_admin_payout_<id>, the user refund investment_refund_<id>, and the admin refund ai_invest_admin_<status>_<id>.

transaction.referenceId is unique platform-wide, and every payout for an investment writes <id>_roi regardless of which door produced it. A second payout collides on that index even when the idempotency keys differ.

A collision on either the idempotency key or the reference is read as "this ROI was already paid". The settlement does not pay again and does not roll back: it commits the terminal COMPLETED status so the investment stops looping through every hourly run, and suppresses the completion email so nobody is told twice about a single payout.

Next: Admin screens — where all of this is visible, and which buttons move money.