Class: OrderService
Defined in: server/checkout/order-service.ts:173
Extends
BaseCheckoutService
Constructors
Constructor
new OrderService(deps: CheckoutFeatureDeps): OrderService;Defined in: server/checkout/base-checkout-service.ts:43
Parameters
| Parameter | Type |
|---|---|
deps | CheckoutFeatureDeps |
Returns
OrderService
Inherited from
BaseCheckoutService.constructorProperties
deps
protected readonly deps: CheckoutFeatureDeps;Defined in: server/checkout/base-checkout-service.ts:43
Inherited from
BaseCheckoutService.depsAccessors
actorId
Get Signature
get protected actorId(): string | null;Defined in: server/checkout/base-checkout-service.ts:45
Returns
string | null
Inherited from
BaseCheckoutService.actorIdMethods
cancelOrder()
cancelOrder(orderId: string): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>;Defined in: server/checkout/order-service.ts:487
Imperative cancellation of a pending order — the one exception to
derive-from-facts (proposal §Derived status: there is no ledger fact
for abandonment; §Tender cancellation & abandonment: cancelOrder
"also voids any uncaptured auth, emitting a void txn"). There is no
canceledBy column; the actor lands in statusUpdatedBy and the
order.canceled payload.
The status gate runs FIRST: only pending orders are ever cancelable,
so a non-pending order gets OrderNotCancelableError regardless of its
tenders.
- Phase 1 — NO ambient transaction. Every live tender (see
isLiveTender) is canceled throughPaymentService.cancelTender, each running its own two-phase op (a provider void for an uncaptured auth happens with no lock held — the two-phase discipline is why this phase must sit OUTSIDE any transaction; callers must not wrapcancelOrderin their own ambient transaction). When at least one live tender exists, an ambient-transaction guard throws immediately (before any provider I/O) to prevent row-lock escalation. A pre-flight pass then folds every live tender's ledger and throwsTenderNotCancelableErrorBEFORE voiding any of them if one already carries a success capture (money moved — the order should not be pending; surface loudly rather than swallow). Checking up front keeps the common stale-projection case all-or-nothing: no tender is voided at the provider only to have a later uncancelable tender abort the loop. - Phase 2 — one transaction. Lock the order; re-verify the status is
still
pending(a regression in the phase-1 window — e.g. a late capture paying the order off mid-cancel — throwsOrderNotCancelableErrorwith the regressed status); assert no NEW live tender appeared (operators racing tender creation against cancellation is a consumer wiring bug, not a domain condition — plainErrornaming the tender); then the canceled write +order.canceledevent (writeCanceled, shared with the tenderless path, which skips straight here).
Parameters
| Parameter | Type |
|---|---|
orderId | string |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>
createOrder()
createOrder(priced: PricedCart, opts: CreateOrderOptions): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>;Defined in: server/checkout/order-service.ts:190
Create an order from a PricedCart snapshot (proposal §finalize
step 4 + §Idempotency & errors + §Order code). In one transaction:
insert the order (pending, placedAt = clock()), the line items and
adjustments, project paid immediately when the customer total is 0,
and append the order.placed (+ order.paid) domain events.
Code-collision retry only operates when createOrder OWNS the
transaction. When a caller invokes it inside an ambient
repos.transaction(...), TransactionManager.startOrUseTransaction
JOINS that transaction, so a P2002 poisons the caller's PG transaction —
re-running the inserts would execute on an aborted transaction. In that
case the create runs exactly once and any P2002 propagates; ambient
callers must wrap their own retry around their whole transaction
(stage-7 finalize does exactly that).
Parameters
| Parameter | Type |
|---|---|
priced | PricedCart |
opts | CreateOrderOptions |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>
get()
get(orderId: string): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>;Defined in: server/checkout/order-service.ts:252
Fetch by id; missing → CheckoutNotFoundError.
Parameters
| Parameter | Type |
|---|---|
orderId | string |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>
getByCode()
getByCode(organizationId: string, code: string): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>;Defined in: server/checkout/order-service.ts:259
Human-readable reference lookup; missing → CheckoutNotFoundError.
Parameters
| Parameter | Type |
|---|---|
organizationId | string |
code | string |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>
list()
list(organizationId: string, filters?: OrderListFilters): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>[]>;Defined in: server/checkout/order-service.ts:269
Thin delegation to the repo — settledOnly ANDs the disputed overlay in
(disputedTotalCents: 0) so consumers never hand-roll it.
Parameters
| Parameter | Type |
|---|---|
organizationId | string |
filters? | OrderListFilters |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>[]>
recompute()
recompute(orderId: string): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>;Defined in: server/checkout/order-service.ts:296
THE single write path for the denormalized buckets (proposal §recompute
— stage 5 reuses this verbatim). In one transaction: lock the order row,
load the tender facts, run the shared pure fold (recomputeOrder), and
persist:
- the six buckets, always;
status/authorizeStatus/chargeStatusfrom the computation, withstatusUpdatedAt/statusUpdatedBytouched only when the projection actually changed;anomalyAt: set on the null→anomalous transition (never overwriting an existing timestamp), cleared when the anomaly resolves.
Events, same transaction: order.status_changed when the projection
changed; one order.anomaly per cause in each anomaly cycle. Independent
causes remain visible even when anomalyAt was set by another cause.
Deliberately NOT here (stage-5 call sites own them): closedAt,
order.paid, and per-payment bucket persistence.
Parameters
| Parameter | Type |
|---|---|
orderId | string |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>
recomputeTenderFromLedger()
protected recomputeTenderFromLedger(
repos: CheckoutRepositories,
payment: PaymentRow,
overrides?: Partial<Pick<TenderFacts, "clientActionRequired" | "clientActionRequiredAt">>
): Promise<{
fold: TenderComputation;
patch: TenderBucketPatch;
}>;Defined in: server/checkout/base-checkout-service.ts:94
Re-project a tender from its persisted ledger without writing it.
Explicit clientActionRequiredAt: null clears the stored timestamp.
Parameters
| Parameter | Type |
|---|---|
repos | CheckoutRepositories |
payment | PaymentRow |
overrides? | Partial<Pick<TenderFacts, "clientActionRequired" | "clientActionRequiredAt">> |
Returns
Promise<{
fold: TenderComputation;
patch: TenderBucketPatch;
}>
Inherited from
BaseCheckoutService.recomputeTenderFromLedgerreconcileClosedAt()
protected reconcileClosedAt(
repos: CheckoutRepositories,
order: OrderRow,
now: Date
): Promise<void>;Defined in: server/checkout/base-checkout-service.ts:121
Reconcile closedAt and the once-per-order order.paid event after a
money write. The caller must hold the order lock in an ambient transaction.
Parameters
| Parameter | Type |
|---|---|
repos | CheckoutRepositories |
order | OrderRow |
now | Date |
Returns
Promise<void>
Inherited from
BaseCheckoutService.reconcileClosedAtrecordCanonicalTransactions()
protected recordCanonicalTransactions(
repos: CheckoutRepositories,
context: RecordTxnContext,
transactions: CanonicalTxn[]
): Promise<RecordedCanonicalTxn[]>;Defined in: server/checkout/base-checkout-service.ts:61
Record canonical transactions sequentially and identify both fresh inserts and pending rows resolved in place. Callers own the resulting side effects.
Parameters
| Parameter | Type |
|---|---|
repos | CheckoutRepositories |
context | RecordTxnContext |
transactions | CanonicalTxn[] |
Returns
Promise<RecordedCanonicalTxn[]>
Inherited from
BaseCheckoutService.recordCanonicalTransactionsredactContact()
redactContact(orderId: string): Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>;Defined in: server/checkout/order-service.ts:555
GDPR/CCPA redaction (proposal §Ledger of record): contact snapshots are
recompute-irrelevant and redactable. Nulls customerEmail and nothing
else; no events. Missing order → the repo's CheckoutNotFoundError
(thrown by WriteRepository.update's existence check).
Parameters
| Parameter | Type |
|---|---|
orderId | string |
Returns
Promise<BigIntsAsNumbers<{
anomalyAt: Date | null;
applicationFeeTotalCents: bigint | null;
authorizedTotalCents: bigint;
authorizeStatus: OrderAuthorizeStatus;
capturedTotalCents: bigint;
cartId: string | null;
chargeStatus: OrderChargeStatus;
closedAt: Date | null;
code: string;
createdAt: Date;
createdBy: string | null;
currencyCode: string;
customerEmail: string | null;
customerId: string | null;
customerTotalCents: bigint;
disputedTotalCents: bigint;
externalRef: string | null;
id: string;
idempotencyKey: string | null;
metadata: JsonValue;
notes: string | null;
operatorId: string | null;
organizationId: string;
orgBorneTotalCents: bigint;
origin: string;
placedAt: Date | null;
platformBorneTotalCents: bigint;
processingFeeTotalCents: bigint | null;
refundedTotalCents: bigint;
requestHash: string | null;
status: OrderStatus;
statusUpdatedAt: Date;
statusUpdatedBy: string | null;
subtotalCents: bigint;
updatedAt: Date;
updatedBy: string | null;
}>>
transaction()
protected transaction<T>(fn: (deps: CheckoutFeatureDeps) => Promise<T>): Promise<T>;Defined in: server/checkout/base-checkout-service.ts:49
Type Parameters
| Type Parameter |
|---|
T |
Parameters
| Parameter | Type |
|---|---|
fn | (deps: CheckoutFeatureDeps) => Promise<T> |
Returns
Promise<T>
Inherited from
BaseCheckoutService.transaction