import { FastifyPluginAsync } from 'fastify' import { db } from '../db' type SlugParam = { Params: { slug: string } } type SlugIdParam = { Params: { slug: string; id: string } } const paymentMethods: FastifyPluginAsync = async (fastify) => { const getHotelId = async (slug: string): Promise => { const { rows } = await db.query('SELECT id FROM hotels WHERE slug = $1', [slug]) return rows[0]?.id ?? null } const canAccess = (userSlug: string | null, role: string, slug: string) => role === 'super_admin' || userSlug === slug const isManager = (role: string) => ['super_admin', 'hotel_admin', 'manager'].includes(role) // ── GET /api/hotels/:slug/payment-methods ───────────────────────────────── fastify.get( '/api/hotels/:slug/payment-methods', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) { return reply.code(403).send({ error: 'Forbidden' }) } const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) const { rows } = await db.query( `SELECT * FROM hotel_payment_methods WHERE hotel_id = $1 ORDER BY sort_order, name`, [hotelId], ) return rows }, ) // ── POST /api/hotels/:slug/payment-methods ──────────────────────────────── fastify.post( '/api/hotels/:slug/payment-methods', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug) || !isManager(request.user.role)) { return reply.code(403).send({ error: 'Forbidden' }) } const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) const { name, currency = 'RUB', type = 'cash', sort_order = 0 } = request.body if (!name?.trim()) return reply.code(400).send({ error: 'Name required' }) const { rows } = await db.query( `INSERT INTO hotel_payment_methods (hotel_id, name, currency, type, sort_order) VALUES ($1, $2, $3, $4, $5) RETURNING *`, [hotelId, name.trim(), currency, type, sort_order], ) return reply.code(201).send(rows[0]) }, ) // ── PATCH /api/hotels/:slug/payment-methods/:id ─────────────────────────── fastify.patch( '/api/hotels/:slug/payment-methods/:id', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, id } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug) || !isManager(request.user.role)) { return reply.code(403).send({ error: 'Forbidden' }) } const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) const allowed = ['name', 'currency', 'type', 'sort_order', 'is_active'] const updates: string[] = [] const values: unknown[] = [] let idx = 1 const body = request.body as Record for (const key of allowed) { if (body[key] !== undefined) { updates.push(`${key} = $${idx}`) values.push(body[key]) idx++ } } if (updates.length === 0) return reply.code(400).send({ error: 'Nothing to update' }) values.push(id, hotelId) const { rows } = await db.query( `UPDATE hotel_payment_methods SET ${updates.join(', ')} WHERE id = $${idx} AND hotel_id = $${idx + 1} RETURNING *`, values, ) if (!rows[0]) return reply.code(404).send({ error: 'Not found' }) return rows[0] }, ) // ── DELETE /api/hotels/:slug/payment-methods/:id ────────────────────────── fastify.delete( '/api/hotels/:slug/payment-methods/:id', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug, id } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug) || !isManager(request.user.role)) { return reply.code(403).send({ error: 'Forbidden' }) } const hotelId = await getHotelId(slug) if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) await db.query( 'DELETE FROM hotel_payment_methods WHERE id = $1 AND hotel_id = $2', [id, hotelId], ) return reply.code(204).send() }, ) // ── GET /api/hotels/:slug/payment-settings ──────────────────────────────── fastify.get( '/api/hotels/:slug/payment-settings', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug)) { return reply.code(403).send({ error: 'Forbidden' }) } const { rows } = await db.query( `SELECT require_payment_checkin FROM hotels WHERE slug = $1`, [slug], ) if (!rows[0]) return reply.code(404).send({ error: 'Hotel not found' }) return { requirePaymentCheckin: rows[0].require_payment_checkin ?? 'none' } }, ) // ── PATCH /api/hotels/:slug/payment-settings ────────────────────────────── fastify.patch( '/api/hotels/:slug/payment-settings', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (!canAccess(request.user.hotelSlug, request.user.role, slug) || !isManager(request.user.role)) { return reply.code(403).send({ error: 'Forbidden' }) } const { require_payment_checkin } = request.body if (!['none', 'soft', 'hard'].includes(require_payment_checkin)) { return reply.code(400).send({ error: 'Invalid value' }) } await db.query( 'UPDATE hotels SET require_payment_checkin = $1 WHERE slug = $2', [require_payment_checkin, slug], ) return { requirePaymentCheckin: require_payment_checkin } }, ) } export default paymentMethods