diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 25bd9e0..7dda15a 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -218,9 +218,12 @@ export function createApp() { // Webhooks must use raw body BEFORE express.json(); signature verification // must never reconstruct the payload with JSON.stringify(req.body). + const removedOnlinePaymentWebhooks = (_req: Request, res: Response) => { + res.status(404).json({ error: 'not_found', message: 'Online payment webhooks have been removed', statusCode: 404 }) + } app.use(`${v1}/webhooks`, webhookLimiter, express.raw({ type: 'application/json', limit: '1mb' }), webhookRouter) - app.use(`${v1}/payments/webhooks`, webhookLimiter, express.raw({ type: 'application/json', limit: '1mb' })) - app.use(`${v1}/subscriptions/webhooks`, webhookLimiter, express.raw({ type: 'application/json', limit: '1mb' })) + app.use(`${v1}/payments/webhooks`, webhookLimiter, express.raw({ type: 'application/json', limit: '1mb' }), removedOnlinePaymentWebhooks) + app.use(`${v1}/subscriptions/webhooks`, webhookLimiter, express.raw({ type: 'application/json', limit: '1mb' }), removedOnlinePaymentWebhooks) // Let /storage responses manage CORP explicitly so missing files still return // a normal cross-origin 404 instead of being blocked by Helmet's default diff --git a/apps/api/src/modules/payments/payment.repo.edge.test.ts b/apps/api/src/modules/payments/payment.repo.edge.test.ts index ac3b871..eb718e3 100644 --- a/apps/api/src/modules/payments/payment.repo.edge.test.ts +++ b/apps/api/src/modules/payments/payment.repo.edge.test.ts @@ -42,37 +42,54 @@ describe('payment.repo edge queries', () => { }) }) - it('marks a payment as succeeded with a fresh paidAt timestamp', async () => { + it('loads reservation payments for a company-scoped booking', async () => { + await repo.findByReservation('reservation_1', 'company_1') + + expect(prisma.rentalPayment.findMany).toHaveBeenCalledWith({ + where: { reservationId: 'reservation_1', companyId: 'company_1' }, + orderBy: { createdAt: 'desc' }, + }) + }) + + it('creates a succeeded manual payment with the provided method', async () => { vi.useFakeTimers() vi.setSystemTime(new Date('2026-08-01T12:00:00.000Z')) - await repo.markPaymentSucceeded('payment_1') + await repo.createPayment({ + companyId: 'company_1', + reservationId: 'reservation_1', + amount: 2500, + currency: 'MAD', + status: 'SUCCEEDED', + type: 'CHARGE', + paymentProvider: 'MANUAL', + paymentMethod: 'BANK_TRANSFER', + paidAt: new Date('2026-08-01T12:00:00.000Z'), + }) - expect(prisma.rentalPayment.update).toHaveBeenCalledWith({ - where: { id: 'payment_1' }, - data: { status: 'SUCCEEDED', paidAt: new Date('2026-08-01T12:00:00.000Z') }, + expect(prisma.rentalPayment.create).toHaveBeenCalledWith({ + data: { + companyId: 'company_1', + reservationId: 'reservation_1', + amount: 2500, + currency: 'MAD', + status: 'SUCCEEDED', + type: 'CHARGE', + paymentProvider: 'MANUAL', + paymentMethod: 'BANK_TRANSFER', + paidAt: new Date('2026-08-01T12:00:00.000Z'), + }, }) vi.useRealTimers() }) - it('recomputes paid amount from successful charge payments', async () => { - await repo.incrementReservationPaid('reservation_1', 2500) + it('updates reservation paid amount and payment status', async () => { + await repo.setReservationPaidAmount('reservation_1', 2500, 'PARTIAL') - expect(prisma.$transaction).toHaveBeenCalled() - }) - - it('maps partial refunds to the correct payment status', async () => { - await repo.setPaymentRefunded('payment_1', true) - await repo.setPaymentRefunded('payment_2', false) - - expect(prisma.rentalPayment.update).toHaveBeenNthCalledWith(1, { - where: { id: 'payment_1' }, - data: { status: 'PARTIALLY_REFUNDED' }, - }) - expect(prisma.rentalPayment.update).toHaveBeenNthCalledWith(2, { - where: { id: 'payment_2' }, - data: { status: 'REFUNDED' }, + expect(prisma.reservation.update).toHaveBeenCalledWith({ + where: { id: 'reservation_1' }, + data: { paidAmount: 2500, paymentStatus: 'PARTIAL' }, }) }) }) diff --git a/apps/api/src/swagger/openapi.boundary.test.ts b/apps/api/src/swagger/openapi.boundary.test.ts index e351e6b..f663d4f 100644 --- a/apps/api/src/swagger/openapi.boundary.test.ts +++ b/apps/api/src/swagger/openapi.boundary.test.ts @@ -12,7 +12,7 @@ describe('OpenAPI document boundary contract', () => { EmployeeLogin: expect.any(Object), VehicleInput: expect.any(Object), ReservationCreate: expect.any(Object), - PaymentCharge: expect.any(Object), + ManualPayment: expect.any(Object), }), })) expect(openApiDocument.tags).toEqual(expect.arrayContaining([