import * as db from '../../db/payment.js'; // #region Helpers function requireSelectedCompany(req) { const companyId = req.user?.selectedCompanyId; if (companyId == null) { return { error: 'No company selected' }; } return { companyId }; } function parsePositiveInt(value, field) { const id = Number(value); if (!Number.isInteger(id) || id <= 0) { return { error: `${field} is required` }; } return { id }; } function paymentErrorStatus(error) { if (error.message === 'No company selected') return 400; if (error.message === 'Not authorized') return 403; if (error.message?.endsWith(' not found')) return 404; if (error.message?.endsWith(' required')) return 400; if (error.message === 'Invalid payment amount') return 400; return 500; } // #endregion /** * Registers payment routes (payments belong to an order / manifest). * @param {import('express').Application} app * @param {string} [basePath=''] */ export function register(app, basePath = '') { /** * @openapi * /order/{id}/payment: * get: * tags: [Payment] * summary: List payments of an order * description: Lists payments of the order (manifest) for the selected company. * security: * - cookieAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: Order ID * responses: * 200: * description: List of payments * content: * application/json: * schema: * type: object * properties: * payments: * type: array * items: * $ref: '#/components/schemas/Payment' * 400: * description: Invalid ID or no company selected * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 401: * description: Unauthorized * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 404: * description: Order not found * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ // #region GET /order/:id/payment app.get(`${basePath}/order/:id/payment`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } const parsedId = parsePositiveInt(req.params.id, 'id'); if (parsedId.error) { return res.status(400).json({ error: parsedId.error }); } try { const payments = await db.listByManifestId(req.user.id, parsedId.id); res.status(200).json({ payments }); } catch (error) { const status = paymentErrorStatus(error); if (status === 500) console.error('List payments failed:', error); res.status(status).json({ error: error.message }); } }); // #endregion /** * @openapi * /order/{id}/payment: * post: * tags: [Payment] * summary: Add payment to an order * description: Adds a payment to the order using an account of the selected company. * security: * - cookieAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: Order ID * requestBody: * required: true * content: * application/json: * schema: * type: object * required: [accountId, type, amount] * properties: * accountId: * type: integer * type: * type: string * amount: * type: number * responses: * 201: * description: Created payment * content: * application/json: * schema: * type: object * properties: * payment: * $ref: '#/components/schemas/Payment' * 400: * description: Validation error or no company selected * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 401: * description: Unauthorized * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 403: * description: Not authorized * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 404: * description: Order or account not found * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ // #region POST /order/:id/payment app.post(`${basePath}/order/:id/payment`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } const parsedId = parsePositiveInt(req.params.id, 'id'); if (parsedId.error) { return res.status(400).json({ error: parsedId.error }); } try { const payment = await db.create(req.user.id, { ...(req.body ?? {}), manifestId: parsedId.id, }); res.status(201).json({ payment }); } catch (error) { const status = paymentErrorStatus(error); if (status === 500) console.error('Create payment failed:', error); res.status(status).json({ error: error.message }); } }); // #endregion /** * @openapi * /payment/{id}: * delete: * tags: [Payment] * summary: Delete payment * description: Deletes a payment of an order in the selected company. * security: * - cookieAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: Payment ID * responses: * 204: * description: Payment deleted * 400: * description: Invalid ID or no company selected * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 401: * description: Unauthorized * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 403: * description: Not authorized * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ // #region DELETE /payment/:id app.delete(`${basePath}/payment/:id`, async (req, res) => { const selected = requireSelectedCompany(req); if (selected.error) { return res.status(400).json({ error: selected.error }); } const parsedId = parsePositiveInt(req.params.id, 'id'); if (parsedId.error) { return res.status(400).json({ error: parsedId.error }); } try { await db.remove(req.user.id, parsedId.id); res.status(204).end(); } catch (error) { const status = paymentErrorStatus(error); if (status === 500) console.error('Delete payment failed:', error); res.status(status).json({ error: error.message }); } }); // #endregion }