Merge pull request #1124 from Tria-plc/freight/nati-2

Audit log and presistent cbe pnr
This commit is contained in:
Nathnael Wondisha
2026-08-05 17:23:54 +03:00
committed by GitHub
28 changed files with 839 additions and 294 deletions

View File

@@ -24,7 +24,11 @@ describe("IntentsService CBE_BILL", () => {
let repository: jest.Mocked<
Pick<
IntentsRepository,
"create" | "findById" | "findByIdempotencyKey" | "update"
| "create"
| "findById"
| "findByIdempotencyKey"
| "findAllByReference"
| "update"
>
>;
let billReferenceService: { generate: jest.Mock };
@@ -46,6 +50,7 @@ describe("IntentsService CBE_BILL", () => {
create: jest.fn(async (data) => ({ id: "intent-1", ...data })),
findById: jest.fn(),
findByIdempotencyKey: jest.fn().mockResolvedValue(null),
findAllByReference: jest.fn().mockResolvedValue([]),
update: jest.fn(),
} as never;
billReferenceService = {
@@ -79,6 +84,47 @@ describe("IntentsService CBE_BILL", () => {
);
});
it("reuses the open bill instead of minting a second reference", async () => {
repository.findAllByReference.mockResolvedValue([
{
id: "intent-1",
provider: ProviderMethod.CBE_BILL,
status: ProviderPaymentStatus.REQUIRES_ACTION,
billReference: "000100000015",
amountMinor: 1500,
currency: "ETB",
clientAction: {
type: "SHOW_BILL_REFERENCE",
billReference: "000100000015",
},
},
] as never);
const snapshot = await service.initiate(request);
expect(snapshot.billReference).toBe("000100000015");
expect(billReferenceService.generate).not.toHaveBeenCalled();
expect(repository.create).not.toHaveBeenCalled();
});
it("mints a new bill when the amount changed", async () => {
repository.findAllByReference.mockResolvedValue([
{
id: "intent-1",
provider: ProviderMethod.CBE_BILL,
status: ProviderPaymentStatus.REQUIRES_ACTION,
billReference: "000100000015",
amountMinor: 900,
currency: "ETB",
},
] as never);
billReferenceService.generate.mockResolvedValue("000100000023");
const snapshot = await service.initiate(request);
expect(snapshot.billReference).toBe("000100000023");
});
it("rejects non-ETB currency (plan D8)", async () => {
await expect(
service.initiate({ ...request, currency: "DJF" }),

View File

@@ -163,6 +163,32 @@ export class IntentsService {
);
}
// The bill reference is issued ONCE per order: the domain app persists it (freight stores it
// as the booking's PNR) and the payer may already have written it down, so re-initiating the
// same open bill must hand back the same number. A different amount/currency means a
// different debt — /cbe/payment verifies the debited amount against the intent — so that
// case mints a fresh bill instead of silently repricing an outstanding one.
const open = (
await this.intentsRepository.findAllByReference(
request.service,
request.referenceType,
request.referenceId,
)
).find(
(i) =>
i.provider === ProviderMethod.CBE_BILL &&
i.status === ProviderPaymentStatus.REQUIRES_ACTION &&
!!i.billReference &&
i.amountMinor === request.amountMinor &&
i.currency === request.currency,
);
if (open) {
this.logger.log(
`intent ${open.id} reused for ${request.service}/${request.referenceType}/${request.referenceId} via CBE_BILL (bill ${open.billReference})`,
);
return this.toSnapshot(open);
}
const merchantOrderId = createMerchantOrderId();
const billReference = await this.billReferenceService.generate();
// expiresAt is the BOOKING's payment deadline passed by the domain app — never a provider