feat: add drain tail to the payments

This commit is contained in:
Nathnael
2026-08-04 08:59:33 +00:00
parent a20498fd77
commit acd2cfbe8c
9 changed files with 379 additions and 22 deletions

View File

@@ -102,6 +102,10 @@ export class BookingInvoiceService {
);
switch (payload.type) {
case "PREPAID":
// Before advancing: if this invoice belonged to a partial offer that
// lapsed before the settlement landed, revive it, or the booking boards
// whole having paid only the offered part.
await this.bookingBatch.reviveOfferForInvoice(payload.invoiceId);
await this.advanceBookingOnPayment(payload.sourceId);
break;
default:
@@ -164,15 +168,24 @@ export class BookingInvoiceService {
// may have moved on or been terminated between invoicing and settlement.
// Only advance one that is still awaiting payment: no-op when already PAID,
// and refuse to advance a booking in a terminal/advanced status
// (CANCELLED/REJECTED/EXPIRED or already past the payment gate) so we never
// rewrite its status or re-run allocation.
// (CANCELLED/REJECTED or already past the payment gate) so we never rewrite
// its status or re-run allocation.
//
// EXPIRED is NOT in that list: settlement is async, so a payment can land
// after the pay window and its drain tail (relay backlog, payment-api
// restart, a CBE bill paid at a counter). The money was captured, so it gets
// exactly the same treatment as an in-window payment — the booking becomes
// PAID and ensurePaidBookingAllocated re-places it via
// replaceStrandedPaidBooking (a same-day train with room, or a manual-assign
// log). Leaving EXPIRED here debited the customer for nothing. CANCELLED and
// REJECTED stay: a person terminated those, so a payment against them is a
// refund case, not a boarding.
if (booking.paymentStatus === "PAID" || booking.status === "PAID") {
return;
}
const TERMINAL_OR_ADVANCED_STATUSES: string[] = [
"CANCELLED",
"REJECTED",
"EXPIRED",
"IN_TRANSIT",
"ARRIVED",
"COMPLETED",