/** * Refund Service * Handles financial.refunds operations with approval flow */ import { db } from '../../../shared/database'; import type { Refund, RefundRow, RequestRefundInput, RefundStatus } from '../types/financial.types'; // ============================================================================ // Transform Functions // ============================================================================ function transformRefund(row: RefundRow): Refund { return { id: row.id, paymentId: row.payment_id, amount: parseFloat(row.amount), currency: row.currency, reason: row.reason, reasonCode: row.reason_code, notes: row.notes, status: row.status as RefundStatus, stripeRefundId: row.stripe_refund_id, stripeFailureReason: row.stripe_failure_reason, requestedBy: row.requested_by, approvedBy: row.approved_by, approvedAt: row.approved_at ? new Date(row.approved_at) : null, rejectedAt: row.rejected_at ? new Date(row.rejected_at) : null, rejectionReason: row.rejection_reason, createdAt: new Date(row.created_at), processedAt: row.processed_at ? new Date(row.processed_at) : null, completedAt: row.completed_at ? new Date(row.completed_at) : null, failedAt: row.failed_at ? new Date(row.failed_at) : null, }; } // ============================================================================ // Query Functions // ============================================================================ export async function getRefundById(id: string): Promise { const result = await db.query( 'SELECT * FROM financial.refunds WHERE id = $1', [id] ); return result.rows[0] ? transformRefund(result.rows[0]) : null; } export async function getRefundsForPayment(paymentId: string): Promise { const result = await db.query( 'SELECT * FROM financial.refunds WHERE payment_id = $1 ORDER BY created_at DESC', [paymentId] ); return result.rows.map(transformRefund); } export async function getPendingRefunds(): Promise { const result = await db.query( `SELECT * FROM financial.refunds WHERE status = 'pending' ORDER BY created_at ASC` ); return result.rows.map(transformRefund); } export async function getRefundsByStatus(status: RefundStatus): Promise { const result = await db.query( 'SELECT * FROM financial.refunds WHERE status = $1 ORDER BY created_at DESC', [status] ); return result.rows.map(transformRefund); } export async function getRefundsByUser(userId: string): Promise { const result = await db.query( 'SELECT * FROM financial.refunds WHERE requested_by = $1 ORDER BY created_at DESC', [userId] ); return result.rows.map(transformRefund); } // ============================================================================ // Mutation Functions // ============================================================================ export async function requestRefund( requestedBy: string, input: RequestRefundInput ): Promise { const result = await db.query( `INSERT INTO financial.refunds (payment_id, amount, reason, reason_code, notes, requested_by, status) VALUES ($1, $2, $3, $4, $5, $6, 'pending') RETURNING *`, [ input.paymentId, input.amount, input.reason || null, input.reasonCode || null, input.notes || null, requestedBy, ] ); return transformRefund(result.rows[0]); } export async function approveRefund( id: string, approvedBy: string ): Promise { const result = await db.query( `UPDATE financial.refunds SET status = 'processing', approved_by = $2, approved_at = NOW() WHERE id = $1 AND status = 'pending' RETURNING *`, [id, approvedBy] ); return result.rows[0] ? transformRefund(result.rows[0]) : null; } export async function rejectRefund( id: string, rejectedBy: string, reason: string ): Promise { const result = await db.query( `UPDATE financial.refunds SET status = 'cancelled', rejected_at = NOW(), rejection_reason = $3 WHERE id = $1 AND status = 'pending' RETURNING *`, [id, rejectedBy, reason] ); return result.rows[0] ? transformRefund(result.rows[0]) : null; } export async function completeRefund( id: string, stripeRefundId: string ): Promise { const result = await db.query( `UPDATE financial.refunds SET status = 'succeeded', stripe_refund_id = $2, completed_at = NOW(), processed_at = NOW() WHERE id = $1 RETURNING *`, [id, stripeRefundId] ); return result.rows[0] ? transformRefund(result.rows[0]) : null; } export async function failRefund( id: string, failureReason: string ): Promise { const result = await db.query( `UPDATE financial.refunds SET status = 'failed', stripe_failure_reason = $2, failed_at = NOW() WHERE id = $1 RETURNING *`, [id, failureReason] ); return result.rows[0] ? transformRefund(result.rows[0]) : null; } // ============================================================================ // Refund Service Object (Alternative Export) // ============================================================================ export const refundService = { getById: getRefundById, getForPayment: getRefundsForPayment, getPending: getPendingRefunds, getByStatus: getRefundsByStatus, getByUser: getRefundsByUser, request: requestRefund, approve: approveRefund, reject: rejectRefund, complete: completeRefund, fail: failRefund, };