import { FastifyInstance } from "fastify"; import {insertHistoryItem} from "../utils/history"; import axios from "axios" import dayjs from "dayjs" import {secrets} from "../utils/secrets"; export default async function bankingRoutes(server: FastifyInstance) { const goCardLessBaseUrl = secrets.GOCARDLESS_BASE_URL const goCardLessSecretId = secrets.GOCARDLESS_SECRET_ID const goCardLessSecretKey = secrets.GOCARDLESS_SECRET_KEY let tokenData = null const getToken = async () => { const res = await axios({ url: goCardLessBaseUrl + "/token/new/", method: "POST", data: { secret_id: goCardLessSecretId, secret_key: goCardLessSecretKey, }, }) tokenData = res.data tokenData.created_at = new Date().toISOString() server.log.info("Got new GoCardless token") } const checkToken = async () => { if (tokenData) { const expired = dayjs(tokenData.created_at) .add(tokenData.access_expires, "seconds") .isBefore(dayjs()) if (expired) { server.log.info("Token expired — refreshing…") await getToken() } } else { await getToken() } } // 🔹 Generate Link server.get("/banking/link/:institutionid", async (req, reply) => { await checkToken() const {institutionid} = req.params as {institutionid: string} try { const { data } = await axios({ url: `${goCardLessBaseUrl}/requisitions/`, method: "POST", headers: { Authorization: `Bearer ${tokenData.access}`, accept: "application/json", }, data: { redirect: "https://app.fedeo.de/settings/banking", institution_id: institutionid, user_language: "de", }, }) await server.supabase .from("bankrequisitions") .insert({ tenant: req.user.tenant_id, institutionId: institutionid, id: data.id, status: data.status, }) return reply.send({ link: data.link }) } catch (err) { server.log.error(err.response?.data || err.message) return reply.code(500).send({ error: "Failed to generate link" }) } }) // 🔹 Check Institution server.get("/banking/institutions/:bic", async (req, reply) => { const { bic } = req.params as {bic: string} if (!bic) return reply.code(400).send("BIC not provided") await checkToken() try { const { data } = await axios({ url: `${goCardLessBaseUrl}/institutions/?country=de`, method: "GET", headers: { Authorization: `Bearer ${tokenData.access}`, }, }) const bank = data.find((i) => i.bic.toLowerCase() === bic.toLowerCase()) if (!bank) return reply.code(404).send("Bank not found") return reply.send(bank) } catch (err) { server.log.error(err.response?.data || err.message) return reply.code(500).send("Failed to fetch institutions") } }) // 🔹 List Requisitions server.get("/banking/requisitions/:reqId", async (req, reply) => { const { reqId } = req.params as {reqId: string} if (!reqId) return reply.code(400).send("Requisition ID not provided") await checkToken() try { const { data } = await axios({ url: `${goCardLessBaseUrl}/requisitions/${reqId}`, method: "GET", headers: { Authorization: `Bearer ${tokenData.access}`, }, }) if (data.accounts) { data.accounts = await Promise.all( data.accounts.map(async (accId) => { const { data: accountData } = await axios({ url: `${goCardLessBaseUrl}/accounts/${accId}`, method: "GET", headers: { Authorization: `Bearer ${tokenData.access}`, accept: "application/json", }, }) return accountData }) ) } return reply.send(data) } catch (err) { server.log.error(err.response?.data || err.message) return reply.code(500).send("Failed to fetch requisition data") } }) //Create Banking Statement server.post("/banking/statements", async (req, reply) => { if (!req.user) { return reply.code(401).send({ error: "Unauthorized" }); } const body = req.body as { data: string }; console.log(body); const {data,error} = await server.supabase.from("statementallocations").insert({ //@ts-ignore ...body.data, tenant: req.user.tenant_id, }).select() await insertHistoryItem(server,{ entity: "bankstatements", //@ts-ignore entityId: data.id, action: "created", created_by: req.user.user_id, tenant_id: req.user.tenant_id, oldVal: null, newVal: data, text: `Buchung erstellt`, }); if(data && !error){ return reply.send(data) } }); //Delete Banking Statement server.delete("/banking/statements/:id", async (req, reply) => { if (!req.user) { return reply.code(401).send({ error: "Unauthorized" }); } const { id } = req.params as { id?: string } const {data} = await server.supabase.from("statementallocations").select().eq("id",id).single() const {error} = await server.supabase.from("statementallocations").delete().eq("id",id) if(!error){ await insertHistoryItem(server,{ entity: "bankstatements", entityId: id, action: "deleted", created_by: req.user.user_id, tenant_id: req.user.tenant_id, oldVal: data, newVal: null, text: `Buchung gelöscht`, }); return reply.send({success:true}) } else { return reply.code(500).send({error:"Fehler beim löschen"}) } }) }