Skip to content
Knowledge Hub

Ordering: a payment is declined in the middle of a saga Intermediate

Estimated reading time: 1 min read

Seat and hotel are already committed in their own services when the payment fails. There is no ROLLBACK across services: the orchestrator has to undo them with compensations, and the order matters.

Order the recovery steps below, from the moment PaymentDeclined arrives to the message the customer receives. Press Check to see which steps are out of place and what breaks when they are.

Ordering

Payment declined mid-saga: recover the tour booking in the right order

Saga saga-123 books a tour package through an orchestrator. T1 ReserveSeat held seat 12A, T2 ReserveHotelRoom held room 204, both committed in their own services. T3 ChargePayment just came back PaymentDeclined. T4 IssueTicket has not run.

The orchestrator keeps its state in saga_instance and sends commands through an outbox; the broker delivers at least once. Order the recovery steps.

Use ▲▼ to move each step, or tap a step’s ⠿ and then tap its new position. On a computer you can drag and drop.

  1. Stop forward steps: the guard only sends IssueTicket when state is RUNNING, so no forward command for saga-123 goes out
  2. Commit state = COMPENSATED together with a BookingFailed event in the outbox
  3. Send CancelHotelRoom with key saga-123:hotel:cancel, written to the outbox with the step change
  4. Send ReleaseSeat with key saga-123:seat:release, retried the same way until SeatReleased
  5. Notify the customer from BookingFailed: payment declined, nothing charged, seat and room released
  6. Retry CancelHotelRoom with the same key until HotelCancelled arrives ("no such hold" also counts as done)
  7. Commit state = COMPENSATING, failed_step = charge_payment to saga_instance

Step kinds, saga state and isolation countermeasures: Saga pattern.