DueStatus
Owner context: partners | Entity: Due | Field: status | Values: 3
The lifecycle of a single affiliate commission owed to a partner. A Due starts open when a shift confirms, enters settling when it is grouped into a PaymentRun that begins withdrawing, and reaches paid when that run's outbound transfer confirms on-chain. Forward-only: there are no failure branches and paid is the only sink.
Transitions of a Due are driven by its parent PaymentRun, not by per-Due services. The repository exposes updateStatusByPaymentRunId(...) to bulk-update every due belonging to a run in one call. If you want to know why a due moved, look at what the run did.
States
| State | Terminal | Description | Entry condition |
|---|---|---|---|
open | No | Commission accrued, not yet grouped into a payout | DueCreator on ShiftConfirmedDomainEvent |
settling | No | Parent PaymentRun is in withdrawing — the payout is in flight | PaymentRunWithdrawInitiator bulk-update |
paid | Yes (sink) | The run's transfer confirmed; the partner has been paid | PaymentRunTransferCompletionChecker bulk-update |
Helpers: isOpen(), isSettling(), isPaid(). No isFinal(), no canTransition(), no TRANSITIONS matrix.
Transitions
Transition Table
| From | To | Trigger (service) | Invariant / guard | Side effects |
|---|---|---|---|---|
(new) → open | DueCreator subscribed to ShiftConfirmedDomainEvent | shift.status.isConfirmed() | Emits DueCreatedDomainEvent | |
open → settling | PaymentRunWithdrawInitiator | Parent PaymentRun enters withdrawing; vault has sufficient funds for the pre-payout trades | Bulk-updates every Due with paymentRunId = run.id; emits PaymentRunWithdrawInitiatedDomainEvent on the run | |
settling → paid | PaymentRunTransferCompletionChecker | Cumulative transfer amount ≥ payout amount and no failed transfers on the run | Bulk-updates every Due with paymentRunId = run.id; also moves the parent PaymentRun → paid |
There are no reverse transitions, no per-Due cancellations, and no failure state. A payout that stalls leaves the Due in settling until the run recovers or an operator intervenes directly on the repository.
Invariants
- Initial state is always
open—DueCreator.ts:73hardcodesDueStatus.openon creation. paidis the only sink — nothing moves a due out ofpaid. No reversal, no unpaid state.- No failure branches — the enum has three forward states and nothing else. A failed payout leaves the due in
settling; recovery is out-of-band. - State is a cascade from
PaymentRun— neithersettlingnorpaidare set by a per-Due service. They come from bulk updates driven by the parent run. Do not expect aDueStatusChangedDomainEvent— there is none. - Grouping into a
PaymentRunis what lets the due move — anopendue withpaymentRunId = nullwill stayopenforever. The grouping happens whenPaymentRunRateQuoterbuilds a new run; seePaymentRunStatus. kindis alwaysaffiliatetoday — theDueentity carries akindfield (DueKind), but no non-affiliate due is created anywhere in the codebase.
Relationship with PaymentRun
A Due tracks one commission on one confirmed shift. A PaymentRun groups many dues destined for the same partner for a given payout cycle. The paymentRunId field on Due is nullable — it is set when the run is built around the due, and from that moment the run's lifecycle drives the due's status.
PaymentRun.status | Effect on its Dues |
|---|---|
created, rated | Dues remain open — the run is still being assembled/priced |
withdrawing | PaymentRunWithdrawInitiator flips every grouped due to settling |
paid | PaymentRunTransferCompletionChecker flips every grouped due to paid |
Code Pointers
- Value object: packages/partners/domain/valueObjects/DueStatus.ts
- Entity: packages/partners/domain/entities/Due.ts
- Creation path: packages/partners/application/services/DueCreator.ts
open → settling: packages/partners/application/services/PaymentRunWithdrawInitiator.tssettling → paid: packages/partners/application/services/PaymentRunTransferCompletionChecker.ts- Bulk update repository method:
updateStatusByPaymentRunId(runId, status)on packages/partners/domain/repositories/DueRepository.ts - Created event: packages/partners/domain/events/DueCreatedDomainEvent.ts
Related
- Owner context:
partners - Parent state machine:
PaymentRunStatus— controls the transitions of this one - Sibling projection (also in
PaymentRun):PaymentRunTradeStatus - Upstream flow that creates
Dues:shift-lifecycle(step 9) - Flow that consumes
Dues and drives them topaid:payment-run(to be documented)
