archetecture security fix

This commit is contained in:
root
2026-06-11 03:22:12 -04:00
parent 6def9993da
commit 9483750161
3126 changed files with 177194 additions and 37211 deletions
+32
View File
@@ -0,0 +1,32 @@
export declare function findByCompany(companyId: string): any;
export declare function findByReservation(reservationId: string, companyId: string): any;
export declare function findByAmanpay(transactionId: string): any;
export declare function findByPaypal(captureId: string): any;
export declare function findByPaypalForCompany(paypalOrderId: string, companyId: string): any;
export declare function findPaymentOrThrow(paymentId: string, companyId: string, reservationId: string): any;
export declare function findReservationOrThrow(id: string, companyId: string): any;
export declare function findReservation(id: string, companyId: string): any;
export declare function markPaymentSucceeded(id: string): any;
export declare function markPaymentFailed(query: {
amanpayTransactionId?: string;
paypalCaptureId?: string;
}): any;
export declare function incrementReservationPaid(reservationId: string, amount: number): any;
export declare function createPayment(data: {
companyId: string;
reservationId: string;
amount: number;
currency: string;
status: string;
type: string;
paymentProvider: string;
amanpayTransactionId?: string | null;
paypalCaptureId?: string | null;
paymentMethod?: string;
paidAt?: Date;
}): any;
export declare function updatePaypalCapture(id: string, captureId: string): any;
export declare function setReservationPaidAmount(reservationId: string, paidAmount: number, paymentStatus: string): any;
export declare function setReservationRefunded(reservationId: string): any;
export declare function setPaymentRefunded(id: string, partial: boolean): any;
//# sourceMappingURL=payment.repo.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"payment.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/payments/payment.repo.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,OAO9C;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAEzE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,OAElD;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,OAE7C;AAED,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAE9E;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,OAE7F;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAKnE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAE5D;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,OAE9C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,OAEnG;AAED,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAE7E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;IAC1E,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;IACrD,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,IAAI,CAAA;CAC7G,OAEA;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAEhE;AAED,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,OAExG;AAED,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,OAE3D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,OAE9D"}
+76
View File
@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findByCompany = findByCompany;
exports.findByReservation = findByReservation;
exports.findByAmanpay = findByAmanpay;
exports.findByPaypal = findByPaypal;
exports.findByPaypalForCompany = findByPaypalForCompany;
exports.findPaymentOrThrow = findPaymentOrThrow;
exports.findReservationOrThrow = findReservationOrThrow;
exports.findReservation = findReservation;
exports.markPaymentSucceeded = markPaymentSucceeded;
exports.markPaymentFailed = markPaymentFailed;
exports.incrementReservationPaid = incrementReservationPaid;
exports.createPayment = createPayment;
exports.updatePaypalCapture = updatePaypalCapture;
exports.setReservationPaidAmount = setReservationPaidAmount;
exports.setReservationRefunded = setReservationRefunded;
exports.setPaymentRefunded = setPaymentRefunded;
const prisma_1 = require("../../lib/prisma");
function findByCompany(companyId) {
return prisma_1.prisma.rentalPayment.findMany({
where: { companyId },
include: { reservation: { include: { customer: true, vehicle: true } } },
orderBy: { createdAt: 'desc' },
take: 100,
});
}
function findByReservation(reservationId, companyId) {
return prisma_1.prisma.rentalPayment.findMany({ where: { reservationId, companyId }, orderBy: { createdAt: 'desc' } });
}
function findByAmanpay(transactionId) {
return prisma_1.prisma.rentalPayment.findFirst({ where: { amanpayTransactionId: transactionId } });
}
function findByPaypal(captureId) {
return prisma_1.prisma.rentalPayment.findFirst({ where: { paypalCaptureId: captureId } });
}
function findByPaypalForCompany(paypalOrderId, companyId) {
return prisma_1.prisma.rentalPayment.findFirstOrThrow({ where: { paypalCaptureId: paypalOrderId, companyId } });
}
function findPaymentOrThrow(paymentId, companyId, reservationId) {
return prisma_1.prisma.rentalPayment.findFirstOrThrow({ where: { id: paymentId, companyId, reservationId } });
}
function findReservationOrThrow(id, companyId) {
return prisma_1.prisma.reservation.findFirstOrThrow({
where: { id, companyId },
include: { vehicle: true, customer: true },
});
}
function findReservation(id, companyId) {
return prisma_1.prisma.reservation.findFirstOrThrow({ where: { id, companyId } });
}
function markPaymentSucceeded(id) {
return prisma_1.prisma.rentalPayment.update({ where: { id }, data: { status: 'SUCCEEDED', paidAt: new Date() } });
}
function markPaymentFailed(query) {
return prisma_1.prisma.rentalPayment.updateMany({ where: query, data: { status: 'FAILED' } });
}
function incrementReservationPaid(reservationId, amount) {
return prisma_1.prisma.reservation.update({ where: { id: reservationId }, data: { paymentStatus: 'PAID', paidAmount: { increment: amount } } });
}
function createPayment(data) {
return prisma_1.prisma.rentalPayment.create({ data: data });
}
function updatePaypalCapture(id, captureId) {
return prisma_1.prisma.rentalPayment.update({ where: { id }, data: { status: 'SUCCEEDED', paidAt: new Date(), paypalCaptureId: captureId } });
}
function setReservationPaidAmount(reservationId, paidAmount, paymentStatus) {
return prisma_1.prisma.reservation.update({ where: { id: reservationId }, data: { paidAmount, paymentStatus: paymentStatus } });
}
function setReservationRefunded(reservationId) {
return prisma_1.prisma.reservation.update({ where: { id: reservationId }, data: { paymentStatus: 'REFUNDED' } });
}
function setPaymentRefunded(id, partial) {
return prisma_1.prisma.rentalPayment.update({ where: { id }, data: { status: partial ? 'PARTIALLY_REFUNDED' : 'REFUNDED' } });
}
//# sourceMappingURL=payment.repo.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"payment.repo.js","sourceRoot":"","sources":["../../../src/modules/payments/payment.repo.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
declare const router: import("express-serve-static-core").Router;
export default router;
//# sourceMappingURL=payment.routes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"payment.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/payments/payment.routes.ts"],"names":[],"mappings":"AAaA,QAAA,MAAM,MAAM,4CAAW,CAAA;AA6EvB,eAAe,MAAM,CAAA"}
+139
View File
@@ -0,0 +1,139 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const requireCompanyAuth_1 = require("../../middleware/requireCompanyAuth");
const requireTenant_1 = require("../../middleware/requireTenant");
const requireSubscription_1 = require("../../middleware/requireSubscription");
const requireRole_1 = require("../../middleware/requireRole");
const validate_1 = require("../../http/validate");
const respond_1 = require("../../http/respond");
const webhooks_1 = require("../../http/webhooks");
const amanpay = __importStar(require("../../services/amanpayService"));
const paypal = __importStar(require("../../services/paypalService"));
const service = __importStar(require("./payment.service"));
const payment_schemas_1 = require("./payment.schemas");
const router = (0, express_1.Router)();
// ─── Webhooks (no auth) ────────────────────────────────────────
router.post('/webhooks/amanpay', async (req, res, next) => {
try {
const rawBody = (0, webhooks_1.getRawBodyString)(req);
const payload = (0, webhooks_1.parseRawJsonBody)(req);
const signature = req.headers['x-amanpay-signature'] ?? '';
if (!amanpay.isConfigured() || !amanpay.verifyWebhookSignature(rawBody, signature)) {
return res.status(401).json({ error: 'invalid_signature' });
}
await service.handleAmanpayWebhook(payload, rawBody);
res.json({ received: true });
}
catch (err) {
next(err);
}
});
router.post('/webhooks/paypal', async (req, res, next) => {
try {
const rawBody = (0, webhooks_1.getRawBodyString)(req);
const payload = (0, webhooks_1.parseRawJsonBody)(req);
const isValid = await paypal.verifyWebhookEvent(req.headers, rawBody);
if (!paypal.isConfigured() || !isValid)
return res.status(401).json({ error: 'invalid_signature' });
await service.handlePaypalWebhook(payload, rawBody);
res.json({ received: true });
}
catch (err) {
next(err);
}
});
// ─── Authenticated ────────────────────────────────────────────
router.use(requireCompanyAuth_1.requireCompanyAuth, requireTenant_1.requireTenant, requireSubscription_1.requireSubscription);
router.get('/company', async (req, res, next) => {
try {
(0, respond_1.ok)(res, await service.listByCompany(req.companyId));
}
catch (err) {
next(err);
}
});
router.get('/reservations/:id', async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(payment_schemas_1.reservationParamSchema, req);
(0, respond_1.ok)(res, await service.listByReservation(id, req.companyId));
}
catch (err) {
next(err);
}
});
router.post('/reservations/:id/charge', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(payment_schemas_1.reservationParamSchema, req);
const body = (0, validate_1.parseBody)(payment_schemas_1.chargeSchema, req);
(0, respond_1.ok)(res, await service.initCharge(id, req.companyId, body));
}
catch (err) {
next(err);
}
});
router.post('/reservations/:id/capture-paypal', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(payment_schemas_1.reservationParamSchema, req);
const { paypalOrderId } = (0, validate_1.parseBody)(payment_schemas_1.capturePaypalSchema, req);
(0, respond_1.ok)(res, await service.capturePaypal(id, req.companyId, paypalOrderId));
}
catch (err) {
next(err);
}
});
router.post('/reservations/:id/manual', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { id } = (0, validate_1.parseParams)(payment_schemas_1.reservationParamSchema, req);
const body = (0, validate_1.parseBody)(payment_schemas_1.manualPaymentSchema, req);
(0, respond_1.ok)(res, await service.recordManualPayment(id, req.companyId, body));
}
catch (err) {
next(err);
}
});
router.post('/reservations/:reservationId/payments/:paymentId/refund', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
try {
const { reservationId, paymentId } = (0, validate_1.parseParams)(payment_schemas_1.paymentParamSchema, req);
const { amount, reason } = (0, validate_1.parseBody)(payment_schemas_1.refundSchema, req);
(0, respond_1.ok)(res, await service.refundPayment(reservationId, paymentId, req.companyId, amount, reason));
}
catch (err) {
next(err);
}
});
exports.default = router;
//# sourceMappingURL=payment.routes.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"payment.routes.js","sourceRoot":"","sources":["../../../src/modules/payments/payment.routes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAgC;AAChC,4EAAwE;AACxE,kEAA8D;AAC9D,8EAA0E;AAC1E,8DAA0D;AAC1D,kDAA4D;AAC5D,gDAAuC;AACvC,kDAAwE;AACxE,uEAAwD;AACxD,qEAAsD;AACtD,2DAA4C;AAC5C,uDAAoJ;AAEpJ,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAA;AAEvB,kEAAkE;AAElE,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAK,IAAA,2BAAgB,EAAC,GAAG,CAAC,CAAA;QACvC,MAAM,OAAO,GAAK,IAAA,2BAAgB,EAAC,GAAG,CAAC,CAAA;QACvC,MAAM,SAAS,GAAI,GAAG,CAAC,OAAO,CAAC,qBAAqB,CAAY,IAAI,EAAE,CAAA;QACtE,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;YACnF,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAA;QAC7D,CAAC;QACD,MAAM,OAAO,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACpD,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,2BAAgB,EAAC,GAAG,CAAC,CAAA;QACrC,MAAM,OAAO,GAAG,IAAA,2BAAgB,EAAC,GAAG,CAAC,CAAA;QACrC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAiC,EAAE,OAAO,CAAC,CAAA;QAC/F,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAA;QACnG,MAAM,OAAO,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACnD,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,iEAAiE;AAEjE,MAAM,CAAC,GAAG,CAAC,uCAAkB,EAAE,6BAAa,EAAE,yCAAmB,CAAC,CAAA;AAElE,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,IAAA,sBAAW,EAAC,wCAAsB,EAAE,GAAG,CAAC,CAAA;QACvD,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAA;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,IAAA,yBAAW,EAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvF,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAK,IAAA,sBAAW,EAAC,wCAAsB,EAAE,GAAG,CAAC,CAAA;QACzD,MAAM,IAAI,GAAO,IAAA,oBAAS,EAAC,8BAAY,EAAE,GAAG,CAAC,CAAA;QAC7C,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAA;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE,IAAA,yBAAW,EAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC/F,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAY,IAAA,sBAAW,EAAC,wCAAsB,EAAE,GAAG,CAAC,CAAA;QAChE,MAAM,EAAE,aAAa,EAAE,GAAG,IAAA,oBAAS,EAAC,qCAAmB,EAAE,GAAG,CAAC,CAAA;QAC7D,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;IACxE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,IAAA,yBAAW,EAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvF,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,IAAA,sBAAW,EAAC,wCAAsB,EAAE,GAAG,CAAC,CAAA;QACvD,MAAM,IAAI,GAAK,IAAA,oBAAS,EAAC,qCAAmB,EAAE,GAAG,CAAC,CAAA;QAClD,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAA;IACrE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,IAAI,CAAC,yDAAyD,EAAE,IAAA,yBAAW,EAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACtH,IAAI,CAAC;QACH,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,IAAA,sBAAW,EAAC,oCAAkB,EAAE,GAAG,CAAC,CAAA;QACzE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAa,IAAA,oBAAS,EAAC,8BAAY,EAAE,GAAG,CAAC,CAAA;QACjE,IAAA,YAAE,EAAC,GAAG,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,aAAa,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAAC,CAAC;AAC7B,CAAC,CAAC,CAAA;AAEF,kBAAe,MAAM,CAAA"}
+71
View File
@@ -0,0 +1,71 @@
import { z } from 'zod';
export declare const chargeSchema: z.ZodObject<{
provider: z.ZodEnum<["AMANPAY", "PAYPAL"]>;
type: z.ZodDefault<z.ZodEnum<["CHARGE", "DEPOSIT"]>>;
currency: z.ZodDefault<z.ZodLiteral<"MAD">>;
successUrl: z.ZodString;
failureUrl: z.ZodString;
}, "strip", z.ZodTypeAny, {
type: "CHARGE" | "DEPOSIT";
provider: "AMANPAY" | "PAYPAL";
currency: "MAD";
successUrl: string;
failureUrl: string;
}, {
provider: "AMANPAY" | "PAYPAL";
successUrl: string;
failureUrl: string;
type?: "CHARGE" | "DEPOSIT" | undefined;
currency?: "MAD" | undefined;
}>;
export declare const manualPaymentSchema: z.ZodObject<{
amount: z.ZodNumber;
currency: z.ZodDefault<z.ZodLiteral<"MAD">>;
type: z.ZodDefault<z.ZodEnum<["CHARGE", "DEPOSIT"]>>;
paymentMethod: z.ZodEnum<["CASH", "CHECK", "BANK_TRANSFER", "CARD", "PAYPAL"]>;
}, "strip", z.ZodTypeAny, {
type: "CHARGE" | "DEPOSIT";
currency: "MAD";
amount: number;
paymentMethod: "PAYPAL" | "CASH" | "CHECK" | "BANK_TRANSFER" | "CARD";
}, {
amount: number;
paymentMethod: "PAYPAL" | "CASH" | "CHECK" | "BANK_TRANSFER" | "CARD";
type?: "CHARGE" | "DEPOSIT" | undefined;
currency?: "MAD" | undefined;
}>;
export declare const refundSchema: z.ZodObject<{
amount: z.ZodOptional<z.ZodNumber>;
reason: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
reason?: string | undefined;
amount?: number | undefined;
}, {
reason?: string | undefined;
amount?: number | undefined;
}>;
export declare const capturePaypalSchema: z.ZodObject<{
paypalOrderId: z.ZodString;
}, "strip", z.ZodTypeAny, {
paypalOrderId: string;
}, {
paypalOrderId: string;
}>;
export declare const reservationParamSchema: z.ZodObject<{
id: z.ZodString;
}, "strip", z.ZodTypeAny, {
id: string;
}, {
id: string;
}>;
export declare const paymentParamSchema: z.ZodObject<{
reservationId: z.ZodString;
paymentId: z.ZodString;
}, "strip", z.ZodTypeAny, {
reservationId: string;
paymentId: string;
}, {
reservationId: string;
paymentId: string;
}>;
//# sourceMappingURL=payment.schemas.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"payment.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/payments/payment.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;EAMvB,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAA;AAEF,eAAO,MAAM,YAAY;;;;;;;;;EAGvB,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;EAE9B,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;EAEjC,CAAA;AAEF,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAA"}
+32
View File
@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.paymentParamSchema = exports.reservationParamSchema = exports.capturePaypalSchema = exports.refundSchema = exports.manualPaymentSchema = exports.chargeSchema = void 0;
const zod_1 = require("zod");
exports.chargeSchema = zod_1.z.object({
provider: zod_1.z.enum(['AMANPAY', 'PAYPAL']),
type: zod_1.z.enum(['CHARGE', 'DEPOSIT']).default('CHARGE'),
currency: zod_1.z.literal('MAD').default('MAD'),
successUrl: zod_1.z.string().url(),
failureUrl: zod_1.z.string().url(),
});
exports.manualPaymentSchema = zod_1.z.object({
amount: zod_1.z.number().int().positive(),
currency: zod_1.z.literal('MAD').default('MAD'),
type: zod_1.z.enum(['CHARGE', 'DEPOSIT']).default('CHARGE'),
paymentMethod: zod_1.z.enum(['CASH', 'CHECK', 'BANK_TRANSFER', 'CARD', 'PAYPAL']),
});
exports.refundSchema = zod_1.z.object({
amount: zod_1.z.number().int().positive().optional(),
reason: zod_1.z.string().optional(),
});
exports.capturePaypalSchema = zod_1.z.object({
paypalOrderId: zod_1.z.string(),
});
exports.reservationParamSchema = zod_1.z.object({
id: zod_1.z.string().min(1),
});
exports.paymentParamSchema = zod_1.z.object({
reservationId: zod_1.z.string().min(1),
paymentId: zod_1.z.string().min(1),
});
//# sourceMappingURL=payment.schemas.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"payment.schemas.js","sourceRoot":"","sources":["../../../src/modules/payments/payment.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IACnC,QAAQ,EAAI,OAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,EAAQ,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC3D,QAAQ,EAAI,OAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAC3C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAC5B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;CAC7B,CAAC,CAAA;AAEW,QAAA,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC1C,MAAM,EAAS,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC1C,QAAQ,EAAO,OAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAC9C,IAAI,EAAW,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC9D,aAAa,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC5E,CAAC,CAAA;AAEW,QAAA,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAA;AAEW,QAAA,mBAAmB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC1C,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE;CAC1B,CAAC,CAAA;AAEW,QAAA,sBAAsB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC7C,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACtB,CAAC,CAAA;AAEW,QAAA,kBAAkB,GAAG,OAAC,CAAC,MAAM,CAAC;IACzC,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,SAAS,EAAM,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACjC,CAAC,CAAA"}
+29
View File
@@ -0,0 +1,29 @@
export declare function listByCompany(companyId: string): any;
export declare function listByReservation(reservationId: string, companyId: string): any;
export declare function handleAmanpayWebhook(event: any, rawBody?: string | Buffer): Promise<{
duplicate: boolean;
result?: void | undefined;
}>;
export declare function handlePaypalWebhook(event: any, rawBody?: string | Buffer): Promise<{
duplicate: boolean;
result?: void | undefined;
}>;
export declare function initCharge(reservationId: string, companyId: string, body: {
provider: 'AMANPAY' | 'PAYPAL';
type: 'CHARGE' | 'DEPOSIT';
currency: 'MAD';
successUrl: string;
failureUrl: string;
}): Promise<{
payment: any;
checkoutUrl: string;
}>;
export declare function capturePaypal(reservationId: string, companyId: string, paypalOrderId: string): Promise<any>;
export declare function recordManualPayment(reservationId: string, companyId: string, body: {
amount: number;
currency: string;
type: string;
paymentMethod: string;
}): Promise<any>;
export declare function refundPayment(reservationId: string, paymentId: string, companyId: string, amount?: number, reason?: string): Promise<any>;
//# sourceMappingURL=payment.service.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"payment.service.d.ts","sourceRoot":"","sources":["../../../src/modules/payments/payment.service.ts"],"names":[],"mappings":"AAMA,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,OAE9C;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAEzE;AA8BD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,GAAE,MAAM,GAAG,MAA8B;;;GAQtG;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,GAAE,MAAM,GAAG,MAA8B;;;GAQrG;AAED,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IAC/E,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC1D,QAAQ,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CACxD;;;GAiCA;AAED,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,gBAOlG;AAED,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IACxF,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CACtE,gBAUA;AAED,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,gBAkBhI"}
+178
View File
@@ -0,0 +1,178 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.listByCompany = listByCompany;
exports.listByReservation = listByReservation;
exports.handleAmanpayWebhook = handleAmanpayWebhook;
exports.handlePaypalWebhook = handlePaypalWebhook;
exports.initCharge = initCharge;
exports.capturePaypal = capturePaypal;
exports.recordManualPayment = recordManualPayment;
exports.refundPayment = refundPayment;
const errors_1 = require("../../http/errors");
const amanpay = __importStar(require("../../services/amanpayService"));
const paypal = __importStar(require("../../services/paypalService"));
const repo = __importStar(require("./payment.repo"));
const webhookIdempotency_1 = require("../../security/webhookIdempotency");
function listByCompany(companyId) {
return repo.findByCompany(companyId);
}
function listByReservation(reservationId, companyId) {
return repo.findByReservation(reservationId, companyId);
}
async function applyAmanpayWebhook(event) {
const transactionId = event.transaction_id ?? event.id;
const status = event.status?.toUpperCase();
if (status === 'PAID' || status === 'SUCCEEDED') {
const payment = await repo.findByAmanpay(transactionId);
if (payment && payment.status !== 'SUCCEEDED') {
await repo.markPaymentSucceeded(payment.id);
await repo.incrementReservationPaid(payment.reservationId, payment.amount);
}
}
else if (status === 'FAILED') {
await repo.markPaymentFailed({ amanpayTransactionId: transactionId });
}
}
async function applyPaypalWebhook(event) {
const eventType = event.event_type;
if (eventType === 'PAYMENT.CAPTURE.COMPLETED') {
const captureId = event.resource?.id;
const payment = await repo.findByPaypal(captureId);
if (payment && payment.status !== 'SUCCEEDED') {
await repo.markPaymentSucceeded(payment.id);
await repo.incrementReservationPaid(payment.reservationId, payment.amount);
}
}
else if (eventType === 'PAYMENT.CAPTURE.DENIED') {
await repo.markPaymentFailed({ paypalCaptureId: event.resource?.id });
}
}
async function handleAmanpayWebhook(event, rawBody = JSON.stringify(event)) {
return (0, webhookIdempotency_1.processWebhookOnce)({
provider: 'amanpay:payments',
providerEventId: (0, webhookIdempotency_1.getWebhookEventId)('amanpay', event),
eventType: String(event.status ?? 'unknown'),
rawBody,
handle: () => applyAmanpayWebhook(event),
});
}
async function handlePaypalWebhook(event, rawBody = JSON.stringify(event)) {
return (0, webhookIdempotency_1.processWebhookOnce)({
provider: 'paypal:payments',
providerEventId: (0, webhookIdempotency_1.getWebhookEventId)('paypal', event),
eventType: String(event.event_type ?? 'unknown'),
rawBody,
handle: () => applyPaypalWebhook(event),
});
}
async function initCharge(reservationId, companyId, body) {
const reservation = await repo.findReservationOrThrow(reservationId, companyId);
if (reservation.paymentStatus === 'PAID')
throw new errors_1.ConflictError('Reservation is already fully paid');
const amount = body.type === 'DEPOSIT' ? reservation.depositAmount : reservation.totalAmount;
const description = `${body.type === 'DEPOSIT' ? 'Deposit' : 'Rental'}: ${reservation.vehicle.make} ${reservation.vehicle.model}`;
const orderId = `${reservationId}-${body.type}-${Date.now()}`;
const webhookBase = process.env.API_URL ?? 'http://localhost:4000';
let checkoutUrl;
let amanpayTransactionId = null;
let paypalCaptureId = null;
if (body.provider === 'AMANPAY') {
if (!amanpay.isConfigured())
throw new errors_1.ValidationError('AmanPay is not configured');
const result = await amanpay.createCheckout({
amount, currency: body.currency, orderId, description,
customerEmail: reservation.customer.email,
customerName: `${reservation.customer.firstName} ${reservation.customer.lastName}`,
successUrl: body.successUrl, failureUrl: body.failureUrl,
webhookUrl: `${webhookBase}/api/v1/payments/webhooks/amanpay`,
});
checkoutUrl = result.checkoutUrl;
amanpayTransactionId = result.transactionId;
}
else {
if (!paypal.isConfigured())
throw new errors_1.ValidationError('PayPal is not configured');
const result = await paypal.createOrder({ amount, currency: body.currency, orderId, description, returnUrl: body.successUrl, cancelUrl: body.failureUrl });
checkoutUrl = result.approveUrl;
paypalCaptureId = result.orderId;
}
const payment = await repo.createPayment({ companyId, reservationId, amount, currency: body.currency, status: 'PENDING', type: body.type, paymentProvider: body.provider, amanpayTransactionId, paypalCaptureId });
return { payment, checkoutUrl };
}
async function capturePaypal(reservationId, companyId, paypalOrderId) {
const payment = await repo.findByPaypalForCompany(paypalOrderId, companyId);
const capture = await paypal.captureOrder(paypalOrderId);
const captureId = capture.purchase_units?.[0]?.payments?.captures?.[0]?.id ?? paypalOrderId;
const updated = await repo.updatePaypalCapture(payment.id, captureId);
await repo.incrementReservationPaid(payment.reservationId, payment.amount);
return updated;
}
async function recordManualPayment(reservationId, companyId, body) {
const reservation = await repo.findReservation(reservationId, companyId);
const remaining = Math.max(reservation.totalAmount - reservation.paidAmount, 0);
if (remaining <= 0)
throw new errors_1.ConflictError('Reservation is already fully paid');
if (body.amount > remaining)
throw new errors_1.ValidationError('Payment amount exceeds remaining balance');
const payment = await repo.createPayment({ companyId, reservationId, amount: body.amount, currency: body.currency, status: 'SUCCEEDED', type: body.type, paymentProvider: body.paymentMethod === 'PAYPAL' ? 'PAYPAL' : 'AMANPAY', paymentMethod: body.paymentMethod, paidAt: new Date() });
const newPaidAmount = reservation.paidAmount + body.amount;
await repo.setReservationPaidAmount(reservationId, newPaidAmount, newPaidAmount >= reservation.totalAmount ? 'PAID' : 'PARTIAL');
return payment;
}
async function refundPayment(reservationId, paymentId, companyId, amount, reason) {
const payment = await repo.findPaymentOrThrow(paymentId, companyId, reservationId);
if (payment.status !== 'SUCCEEDED')
throw new errors_1.ValidationError('Only succeeded payments can be refunded');
if (!payment.amanpayTransactionId && !payment.paypalCaptureId)
throw new errors_1.ValidationError('Manual payments must be refunded outside the online gateway flow');
const refundAmount = amount ?? payment.amount;
if (payment.paymentProvider === 'AMANPAY') {
if (!payment.amanpayTransactionId)
throw new Error('No AmanPay transaction ID');
await amanpay.refundTransaction(payment.amanpayTransactionId, refundAmount, reason);
}
else {
if (!payment.paypalCaptureId)
throw new Error('No PayPal capture ID');
await paypal.refundCapture(payment.paypalCaptureId, refundAmount, payment.currency, reason);
}
const isPartial = refundAmount < payment.amount;
const updated = await repo.setPaymentRefunded(payment.id, isPartial);
if (!isPartial)
await repo.setReservationRefunded(payment.reservationId);
return updated;
}
//# sourceMappingURL=payment.service.js.map
File diff suppressed because one or more lines are too long