Checkout code-review notes
Highest risks:
- No authentication/authorization or tenant scoping is visible; an arbitrary order ID may be charged/read.
- Stock is read then written without an atomic conditional update or lock, allowing lost updates and negative inventory.
- The payment call has no idempotency key. Client retry, timeout or process crash can double-charge.
- Database changes and payment cannot share a normal database transaction. The workflow needs persisted payment attempt/state, idempotent provider semantics and reconciliation/compensation.
- Client-supplied total is compared loosely and should not authorize price. Server-owned prices and decimal/integer-money semantics are required.
Further issues: missing validation and null handling; N+1 queries; synchronous mail on the critical path; no explicit failure/timeout handling; raw payment details may violate security/compliance boundaries; response exposes model representation; ambiguous state transitions; no transaction around inventory/order changes; and no structured audit trail.
A robust design depends on business rules. One possible direction is an authenticated order-scoped command that atomically transitions an eligible order and reserves inventory, persists a unique payment attempt/idempotency key, invokes the provider through a retry-safe boundary, then finalizes or compensates. Reconciliation handles unknown provider outcomes. Notifications consume a committed state transition asynchronously.
Do not claim a queue alone solves the problem: queues retry and therefore increase the need for idempotency.