Bancos
Ruta:
/owner/settings/bank-connectionsConecta bancos mediante Open Banking (TrueLayer) y gestiona cuentas para facturas.
Vista general
Sección titulada «Vista general»El módulo de Bancos permite conectar cuentas bancarias reales mediante Open Banking o añadirlas manualmente para:
- Importar transacciones automáticamente desde tu banco
- Conciliar gastos con tus transacciones bancarias
- Mostrar datos bancarios en facturas emitidas
- Sincronizar saldos en tiempo real
Tipos de cuentas
Sección titulada «Tipos de cuentas»| Tipo | Descripción | Funciones |
|---|---|---|
| Open Banking | Conectada vía TrueLayer | Sincronización automática, transacciones, saldos |
| Manual | Añadida por el usuario | Solo datos para facturas (IBAN, BIC, etc.) |
Conexiones bancarias (Open Banking)
Sección titulada «Conexiones bancarias (Open Banking)»Estados de conexión
Sección titulada «Estados de conexión» ┌──────────┐ │ Pending │ ← Esperando autenticación └────┬─────┘ │ OAuth exitoso ┌────▼─────┐ │ Active │ ← Tokens válidos, sincronizando └────┬─────┘ │ Token expira / revocado ┌────▼─────┐ │ Expired │ ← Requiere reconexión └────┬─────┘ │ ┌────▼─────┐ │ Revoked │ ← Usuario revocó acceso desde banco └──────────┘Flujo de conexión
Sección titulada «Flujo de conexión»- Verificar límite: Comprueba plan del usuario (Free: 1, Basic: 3, Premium: Ilimitadas)
- Generar link OAuth: Edge function
truelayer-authgenera URL autorización - Autenticación: Usuario autoriza en popup del banco
- Intercambio código: Convierte código OAuth en access/refresh tokens
- Almacenar tokens: Tokens encriptados en PostgreSQL vía RPC
update_bank_connection_tokens - Configurar sync: Wizard post-conexión para rango histórico y frecuencia
- Primera sincronización: Importa cuentas, transacciones y saldos
Frecuencias de sincronización
Sección titulada «Frecuencias de sincronización»| Frecuencia | Descripción | Recomendado para |
|---|---|---|
every_6h | Cada 6 horas | Negocios activos con muchas transacciones |
daily | Una vez al día | Uso estándar, balance diario |
weekly | Cada lunes | Freelancers, bajo volumen |
manual | Solo bajo demanda | Control total, eventos puntuales |
Cuentas bancarias para facturas
Sección titulada «Cuentas bancarias para facturas»Las cuentas bancarias (tabla business_bank_accounts) se unifican para:
- Seleccionarlas en facturas emitidas
- Mostrar IBAN/BIC al cliente
- Configurar cuenta predeterminada
Campos requeridos para facturas
Sección titulada «Campos requeridos para facturas»| Campo | Obligatorio | Descripción |
|---|---|---|
bank_name | Sí | Nombre del banco (ej: Santander) |
iban | Sí | Código IBAN formateado |
bic | No | Código BIC/SWIFT para transferencias internacionales |
bank_address | No | Dirección completa del banco |
beneficiary_name | No | Nombre del titular de la cuenta |
Importación automática desde Open Banking
Sección titulada «Importación automática desde Open Banking»Cuando conectas un banco vía Open Banking, las cuentas se importan automáticamente a business_bank_accounts:
- Nuevas cuentas: Se crean con
source: 'open_banking' - Cuentas existentes: Si existe IBAN manual, se convierte a Open Banking
- Cuenta predeterminada: La primera cuenta se marca como predeterminada si no hay ninguna
Funciones
Sección titulada «Funciones»| Función | Descripción | Documentación |
|---|---|---|
| Conectar Banco | Conecta cuenta mediante TrueLayer OAuth | Ver |
| Añadir Cuenta Manual | Crea cuenta con datos IBAN/BIC | Ver |
| Sincronizar Transacciones | Importa transacciones desde banco | Ver |
| Reconectar Banco | Renueva tokens expirados/revocados | Ver |
| Configurar Cuenta | Edita IBAN, BIC, dirección bancaria | Ver |
| Marcar Predeterminada | Establece cuenta por defecto para facturas | Ver |
Límites por plan
Sección titulada «Límites por plan»| Plan | Conexiones bancarias | Auto-sync | Retención transacciones |
|---|---|---|---|
| Free | 1 banco | Diaria | 90 días |
| Basic | 3 bancos | Cada 6h | 1 año |
| Premium | Ilimitadas | Cada 6h | Ilimitado |
Referencia técnica
Sección titulada «Referencia técnica»Tablas de base de datos
bank_connections
Sección titulada «bank_connections»Almacena conexiones OAuth con bancos vía TrueLayer.
| Columna | Tipo | Descripción |
|---|---|---|
id | UUID | ID único de conexión |
business_id | UUID | FK a businesses |
user_id | UUID | FK a auth.users |
connection_id | TEXT | ID interno de TrueLayer |
provider_name | TEXT | Nombre del banco (ej: “Santander”) |
account_ids | TEXT[] | Array de IDs de cuentas TrueLayer |
access_token_encrypted | TEXT | Token OAuth encriptado |
refresh_token_encrypted | TEXT | Refresh token encriptado |
token_expires_at | TIMESTAMPTZ | Expiración del access token |
status | TEXT | pending, active, expired, revoked, disconnected |
auto_sync_enabled | BOOLEAN | Sincronización automática habilitada |
sync_frequency | TEXT | every_6h, daily, weekly, manual |
last_sync_at | TIMESTAMPTZ | Última sincronización exitosa |
requires_reauth | BOOLEAN | Requiere reconexión manual |
consecutive_failures | INTEGER | Contador de fallos consecutivos |
last_error | TEXT | Último mensaje de error |
Constraints:
CHECK (status IN ('pending', 'active', 'expired', 'revoked', 'error', 'disconnected'))CHECK ((status = 'active' AND access_token_encrypted IS NOT NULL) OR status != 'active')bank_accounts
Sección titulada «bank_accounts»Cuentas importadas desde Open Banking (solo para transacciones).
| Columna | Tipo | Descripción |
|---|---|---|
id | UUID | ID único |
connection_id | UUID | FK a bank_connections |
business_id | UUID | FK a businesses |
truelayer_account_id | TEXT | ID TrueLayer de la cuenta |
account_type | TEXT | Tipo de cuenta (ej: “TRANSACTION”) |
display_name | TEXT | Nombre mostrado al usuario |
iban | TEXT | Código IBAN |
current_balance | NUMERIC(12,2) | Saldo actual |
available_balance | NUMERIC(12,2) | Saldo disponible |
currency | TEXT | Moneda (default: EUR) |
is_active | BOOLEAN | Cuenta activa |
last_balance_update | TIMESTAMPTZ | Última actualización de saldo |
business_bank_accounts
Sección titulada «business_bank_accounts»Cuentas unificadas para facturas (manuales + Open Banking).
| Columna | Tipo | Descripción |
|---|---|---|
id | UUID | ID único |
business_id | UUID | FK a businesses |
bank_connection_id | UUID | FK opcional a bank_connections |
truelayer_account_id | TEXT | ID TrueLayer si es Open Banking |
source | TEXT | manual o open_banking |
bank_name | TEXT | Nombre del banco |
bank_address | TEXT | Dirección del banco |
iban | TEXT | Código IBAN |
bic | TEXT | Código BIC/SWIFT |
beneficiary_name | TEXT | Nombre del beneficiario |
is_default | BOOLEAN | Cuenta predeterminada para facturas |
show_in_invoices | BOOLEAN | Mostrar en selector de facturas |
is_active | BOOLEAN | Cuenta activa |
Constraints:
UNIQUE (business_id, iban)CHECK (source IN ('manual', 'open_banking'))bank_transactions
Sección titulada «bank_transactions»Transacciones importadas desde bancos.
| Columna | Tipo | Descripción |
|---|---|---|
id | UUID | ID único |
bank_account_id | UUID | FK a bank_accounts |
business_id | UUID | FK a businesses |
truelayer_transaction_id | TEXT | ID TrueLayer |
transaction_date | TIMESTAMPTZ | Fecha de transacción |
description | TEXT | Descripción bancaria |
amount | NUMERIC(12,2) | Importe absoluto |
currency | TEXT | Moneda |
transaction_type | TEXT | DEBIT o CREDIT |
merchant_name | TEXT | Nombre del comercio |
reconciliation_status | TEXT | pending, matched, ignored, manual |
matched_expense_id | UUID | FK opcional a expenses |
Constraints:
CHECK (transaction_type IN ('DEBIT', 'CREDIT'))CHECK (reconciliation_status IN ('pending', 'matched', 'ignored', 'manual'))UNIQUE (truelayer_transaction_id, bank_account_id)bank_sync_history
Sección titulada «bank_sync_history»Histórico de sincronizaciones.
| Columna | Tipo | Descripción |
|---|---|---|
id | UUID | ID único |
connection_id | UUID | FK a bank_connections |
user_id | UUID | FK a auth.users |
sync_type | TEXT | accounts, transactions, balances |
status | TEXT | pending, success, failed |
records_synced | INTEGER | Número de registros sincronizados |
error_message | TEXT | Mensaje de error si falló |
started_at | TIMESTAMPTZ | Inicio de sincronización |
completed_at | TIMESTAMPTZ | Fin de sincronización |
Políticas RLS
bank_connections
Sección titulada «bank_connections»-- SELECTPOLICY "Users can view their business bank connections" USING (business_id IN ( SELECT business_id FROM user_businesses WHERE user_id = auth.uid() ));
-- INSERTPOLICY "Users can insert their business bank connections" WITH CHECK (business_id IN ( SELECT business_id FROM user_businesses WHERE user_id = auth.uid() ));business_bank_accounts
Sección titulada «business_bank_accounts»-- SELECTPOLICY "Users can view their business bank accounts" USING (business_id IN ( SELECT business_id FROM user_businesses WHERE user_id = auth.uid() ));
-- INSERT, UPDATE, DELETE-- Similar isolation por business_idEdge Functions
truelayer-auth
Sección titulada «truelayer-auth»Ruta: supabase/functions/truelayer-auth
Acciones:
generate_auth_link: Genera URL OAuth de TrueLayerexchange_code: Intercambia código OAuth por tokens
Parámetros (generate_auth_link):
{ action: 'generate_auth_link', redirect_uri: string, state?: string, scopes?: string[], business_id: string}Respuesta:
{ auth_url: string // URL para popup OAuth}Parámetros (exchange_code):
{ action: 'exchange_code', code: string, redirect_uri: string, business_id: string}Respuesta:
{ success: boolean, provider_name: string, connection_id: string, accounts_count: number, is_reconnection?: boolean}truelayer-sync-transactions
Sección titulada «truelayer-sync-transactions»Ruta: supabase/functions/truelayer-sync-transactions
Función: Sincroniza cuentas, saldos y transacciones desde TrueLayer.
Parámetros:
{ connection_id: string, business_id: string, from_date?: string, // YYYY-MM-DD to_date?: string, account_id?: string // Para sincronizar cuenta específica}Proceso:
- Desencripta tokens con RPC
get_bank_connection_tokens - Verifica expiración y refresca token si es necesario
- Fetch cuentas desde TrueLayer API
/data/v1/accounts - Fetch saldos desde
/data/v1/accounts/{id}/balance - Fetch transacciones desde
/data/v1/accounts/{id}/transactions - Upsert en
bank_accountsybank_transactions - Preserva estado de reconciliación en transacciones existentes
- Actualiza
bank_sync_history
Respuesta:
{ success: boolean, accounts_synced: number, transactions_imported: number, transactions_updated: number, accounts_total: number, accounts_failed: number}refresh-bank-tokens
Sección titulada «refresh-bank-tokens»Ruta: supabase/functions/refresh-bank-tokens
Función: Cron job que refresca tokens próximos a expirar.
Trigger: Cada 6 horas vía pg_cron
Componentes React principales
BankConnectionWizard
Sección titulada «BankConnectionWizard»Archivo: src/features/bank/components/BankConnectionWizard.tsx
Función: Wizard post-conexión para configurar preferencias de sincronización.
Pasos:
- Seleccionar rango de importación (
30_days,90_days,1_year,all) - Seleccionar frecuencia de sync (
every_6h,daily,weekly,manual) - Confirmación y inicio de primera sincronización
Props:
interface BankConnectionWizardProps { open: boolean; onOpenChange: (open: boolean) => void; providerName: string; connectionId: string; accountsCount: number; onComplete: (preferences: SyncPreferences) => Promise<void>;}BankAccountFormDialog
Sección titulada «BankAccountFormDialog»Archivo: src/features/bank/components/BankAccountFormDialog.tsx
Función: Formulario para crear/editar cuentas bancarias manuales.
Validaciones:
- IBAN: Formato internacional con checksum
- BIC/SWIFT: 8 o 11 caracteres
- Auto-formateo con espacios para IBAN
useBankConnection Hook
Sección titulada «useBankConnection Hook»Archivo: src/features/bank/hooks/useBankConnection.ts
Función: Gestiona flujo OAuth completo con TrueLayer.
Estados:
type ConnectionStatus = | 'idle' | 'checking_limit' | 'generating_link' | 'waiting_auth' | 'exchanging_code' | 'success' | 'error';API:
interface UseBankConnectionReturn { isConnecting: boolean; connectionStatus: ConnectionStatus; error: TrueLayerError | null; initiateConnection: () => Promise<void>; handleCallback: (code: string) => Promise<void>; cleanup: () => void; syncAfterConnection: (connectionId: string, options?: SyncOptions) => Promise<any>; lastConnection: { provider_name: string; connection_id: string; accounts_count: number } | null; showWizard: boolean; setShowWizard: (show: boolean) => void;}Schemas Zod / TypeScript
BusinessBankAccount
Sección titulada «BusinessBankAccount»interface BusinessBankAccount { id: string; business_id: string; bank_connection_id: string | null; truelayer_account_id: string | null; source: 'manual' | 'open_banking'; bank_name: string; bank_address: string | null; iban: string; bic: string | null; beneficiary_name: string | null; is_default: boolean; show_in_invoices: boolean; is_active: boolean; created_at: string; updated_at: string;}TrueLayerAuthResponse
Sección titulada «TrueLayerAuthResponse»interface TrueLayerAuthResponse { success: boolean; provider_name: string; connection_id: string; accounts_count: number;}SyncOptions
Sección titulada «SyncOptions»interface SyncOptions { fromDate?: string; toDate?: string; importRange?: '30_days' | '90_days' | '1_year' | 'all'; syncFrequency?: 'every_6h' | 'daily' | 'weekly' | 'manual';}Queries de diagnóstico
Conexiones activas de un negocio:
SELECT bc.id, bc.provider_name, bc.status, bc.auto_sync_enabled, bc.last_sync_at, bc.requires_reauth, bc.consecutive_failures, ARRAY_LENGTH(bc.account_ids, 1) as accounts_countFROM bank_connections bcWHERE bc.business_id = '<business_id>' AND bc.status = 'active'ORDER BY bc.created_at DESC;Cuentas importadas desde conexión:
SELECT ba.display_name, ba.iban, ba.current_balance, ba.available_balance, ba.currency, ba.is_active, ba.last_balance_updateFROM bank_accounts baWHERE ba.connection_id = '<connection_id>' AND ba.business_id = '<business_id>'ORDER BY ba.display_name;Transacciones pendientes de conciliación:
SELECT bt.transaction_date, bt.description, bt.merchant_name, bt.amount, bt.transaction_type, bt.reconciliation_status, ba.display_name as account_nameFROM bank_transactions btJOIN bank_accounts ba ON bt.bank_account_id = ba.idWHERE bt.business_id = '<business_id>' AND bt.reconciliation_status = 'pending'ORDER BY bt.transaction_date DESCLIMIT 50;Histórico de sincronizaciones con errores:
SELECT bsh.started_at, bsh.completed_at, bsh.sync_type, bsh.status, bsh.records_synced, bsh.error_message, bc.provider_nameFROM bank_sync_history bshJOIN bank_connections bc ON bsh.connection_id = bc.idWHERE bsh.status = 'failed' AND bsh.started_at > NOW() - INTERVAL '7 days'ORDER BY bsh.started_at DESC;Verificar tokens próximos a expirar:
SELECT id, provider_name, token_expires_at, token_expires_at - NOW() as time_until_expiry, status, requires_reauthFROM bank_connectionsWHERE status = 'active' AND token_expires_at < NOW() + INTERVAL '1 day'ORDER BY token_expires_at;Errores comunes
Sección titulada «Errores comunes»| Error | Causa | Solución |
|---|---|---|
invalid_token | Token OAuth expirado o revocado | Reconectar banco desde UI |
LIMIT_REACHED | Plan no permite más conexiones | Actualizar plan o eliminar conexión existente |
TOKEN_STORAGE_FAILED | Error al encriptar tokens | Verificar RPC update_bank_connection_tokens |
refresh_failed | Refresh token inválido | Requiere reautenticación manual |
TIMEOUT | Popup OAuth tardó >5 minutos | Reintentar conexión |