Bitácora de crédito

por Halí - publicado: Julio 06, 2023
LaravelBackend

Hace algunos años que tengo una tarjeta de crédito, y me gusta bastante llevar un conteo exacto de que gasto y cuanto tengo disponible para pagarlo. Así que desde entonces he estado llevando una hoja de cálculo que se ve más o menos así

hoja de cálculo

Este proyecto surge como una mejora a esa hoja de cálculo (porque, ¿qué proyecto no inicia así?). Y con esto como excusa espero poder demostrar algunas bondades que tiene PHP y Laravel.

Espero que esta serie, que planeo salga cada jueves, sea de utilidad.

Índice

Arquitectura

Comenzaremos dando paso a algunas decisiones importantes de arquitectura, utilizaremos Laravel y nos ayuda bastante al saber, por ejemplo que usaremos el patrón MVC, sin embargo tenemos que tener en cuenta otras decisiones, por ejemplo como almacenaremos los datos, como iniciaran sesión los usuarios y cómo se gestionan las suscripciones (en caso de que la aplicación funcione).

Almacén de datos

Para este ejercicio utilizaremos una base de datos relacional, ya que nuestra aplicación tiene usuarios asociados a tarjetas de crédito, que a su vez tienen transacciones asociadas.

Debemos tener en cuenta también que al tratarse de datos financieros, debemos elegir un motor de base de datos que soporte cifrado de datos.

Nuestra elección es MariaDB (en parte porque ya tengo un servidor con ese motor de bases de datos), pero podemos utilizar PostgreSQL, MySQL, e incluso SQLite también.

Inicio de sesión

Para ello necesitaremos algunos datos como un nombre de usuario, una contraseña, también un correo electrónico (por si pierde su contraseña), además podemos almacenar otros datos necesarios para que la aplicación funcione bien como si el usuario ya ha verificado su correo, la fecha en la que se registró y un campo para validar el nivel de su subscripción.

La tabla users quedaría algo así:

Columna Tipo de dato Características
id uuid llave primaria
name varchar(255)  
username varchar(255) único
email varchar(255) único
email_verified_at timestamp(3) nullable
password varchar(255)  
subscription varchar(255) nullable
remember_token varchar(100) nullable
created_at timestamp(3)  
updated_at timestamp(3)  

¿Olvidó su contraseña?

También hace falta una tabla con datos necesarios para reestablecer la contraseña de un usuario que la olvidó (Laravel nos ayuda también con esta tabla password_reset_tokens) que se verá algo así:

Columna Tipo de dato Características
email varchar(255) llave primaria
token varchar(255)  
created_at timestamp(3) nullable

Tarjetas de crédito

No queremos guardar datos sensibles de las tarjetas, pero necesitamos asociar algunos datos a este tipo de entidades, así que haremos una tabla que guardará el nombre de una tarjeta, así como su fecha de corte y última fecha para pagar y también su límite de crédito. La tabla de tarjetas de crédito estará asociada a la tabla de usuarios por su id.

Las fechas de corte y última para pagar serán enteros, ya que se trata de un día solamente.

Dicen que no es bueno guardar cantidades de dinero como floats así que lo vamos a intentar con decimales para comenzar.

También guardaremos la taza de interés.

Y agregamos una columna para “borrar” las tarjetas pero poder restaurarlas si el usuario así lo desea.

La tabla credit_cards se verá algo así:

Columna Tipo de dato Características
id uuid llave primaria
user_id uuid llave foránea (users)
name varchar(100)  
due_date integer  
closing_date integer  
interest_rate decimal(5, 2) default: 0
limit decimal(19, 4)  
deleted_at timestamp(3) nullable
created_at timestamp(3)  
updated_at timestamp(3)  

Transacciones

También tenemos la tabla de transacciones, donde iremos anotando que gastos y que pagos se hacen. Ahí guardaremos la tarjeta a la que está asociada, también el monto de la transacción, la fecha, el concepto.

También necesitamos algunos campos para cuando hagamos compras a meses (sin y con intereses) plazo y porcentaje de interés, también, cuando agregemos un pago a meses, debemos separar cada uno de los plazos para tener en cuenta cuanto debemos en el periodo, para ello utilizaremos una referencia a la misma tabla donde guardemos que transacción es la “raíz”. Y un campo en caso de que haya una comisión por la compra.

La tabla transactions se verá así:

Columna Tipo de dato Características
id uuid llave primaria
credit_card_id uuid llave foránea (credit_cards)
concept varchar(255)  
datetime datetime  
amount decimal(19, 4)  
deadline_months integer nullable
commission decimal(19, 4) nullable
interest_rate decimal(6, 2) nullable
parent_transaction_id uuid llave foránea (transactions)
created_at timestamp(3)  
updated_at timestamp(3)  

Otras tablas

Laravel también nos ayuda a crear otras dos tablas que servirán para tener una lista de trabajos que no terminaron con éxito y otra para tokens personales que la aplicación nos podrá proporcionar para dar accesso a aplicaciones de terceros con ciertos permisos.

Código

Ya que planeamos el código que escribiremos, toca comenzar.

Crear proyecto

Vamos a crear el proyecto con Laravel, utilizaremos Laravel Sail, este sirve para tener un entorno igual al de producción pero sin tener que instalarle todo el software a nuestra computadora.

Requerimos tener php, composer, docker y docker-compose instalados.

Luego vamos a la siguiente url para instalar las dependencias requeridas, en este caso le pondremos credit-logbook al proyecto y le instalaremos mariadb (motor de base de datos), redis (para la caché) y mailpit (para pruebas de correos):

https://laravel.build/credit-logbook?with=mariadb,redis,mailpit

ejecutamos el código en bash y entramos al directorio que acaba de crear

cd credit-logbook

luego inicializamos un repo de git y añadimos todos los elementos al índice (esto es seguro pues laravel por defecto agrega un archivo .gitignore), después hacemos commit.

git init .
git add .
git commit -m "chore: initial commit"

Después haremos un repo en la nube, en este caso utilizaremos GitHub. Agregamos el remote y luego hacemos push para subir todos los archivos.

git remote add origin git@github.com:halivert/credit-logbook.git
git push

Modelos, migraciones y algo más

Usuarios y suscripciones

Laravel utiliza migraciones para actualizar la base de datos, actualizaremos la migración de la tabla de users y después el modelo User y la fábrica UserFactory (que nos servirá para pruebas automáticas).

Ver código  (GitHub)

Después tenemos que crear una enumeración para controlar las subscripciones y hacemos uso del Enum casting propio de Laravel.

Además agregamos valores por defecto al modelo User, ya que todos los usuarios serán registrados con la subscripción gratuita.

También actualizamos los datos en la fábrica UserFactory.

Ver código  (GitHub)

Tarjetas de crédito

Ahora tenemos que iniciar el contenedor para poder utilizar algunos comandos de Laravel que crean los modelos:

Nota: Para el siguiente comando es necesario que tengas un alias (así escribimos menos)

alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'

sail up

y una vez que esté ejecutandose, podemos utilizar otra terminal para crear los modelos, comenzamos con el de CreditCard, agregamos la migración, sedeer, fabrica, politicas, controlador, form requests y tests.

sail artisan make:model CreditCard -a --test

Laravel acomoda cada archivo dentro de ciertos directorios, pero vamos a reacomodar un poco esto, para este momento, deberíamos tener los siguientes nuevos archivos

app/Http/Controllers/CreditCardController.php
app/Http/Requests/StoreCreditCardRequest.php
app/Http/Requests/UpdateCreditCardRequest.php
app/Models/CreditCard.php
app/Policies/CreditCardPolicy.php
database/factories/CreditCardFactory.php
database/migrations/2023_07_05_143846_create_credit_cards_table.php
database/seeders/CreditCardSeeder.php
tests/Feature/Models/CreditCardTest.php

Pero queremos que estén organizados de una forma un poco diferente, así que moveremos el controlador, modelo, políticas, fabrica y form requests a los siguientes directorios. Además ya que vamos a versionar nuestra API, vamos a hacer el directorio para la primer versión.

app/API/CreditCard/CreditCard.php
app/API/CreditCard/CreditCardPolicy.php
app/API/CreditCard/CreditCardFactory.php
app/API/CreditCard/v1/CreditCardController.php
app/API/CreditCard/v1/StoreCreditCardRequest.php
app/API/CreditCard/v1/UpdateCreditCardRequest.php

Actualizamos el modelo, la fábrica, las políticas (para los permisos y demás), agregamos algunas cosas al controlador y a las solicitudes, de creación y de actualización, también actualizamos la migración para la tabla.

Ver código  (GitHub)

Transacciones

Para hacer el modelo, migraciones, controlador y demás de las transacciones, seguimos los mismos pasos que para las Tarjetas de crédito

sail artisan make:model Transaction -a --test

Ver código  (GitHub)

Fortify

También vamos a instalar el paquete Laravel Fortify, para que nos ayude con el inicio de sesión y la recuperación de las cuentas en caso de que el usuario pierda su contraseña.

sail composer require laravel/fortify
sail artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"

Actualizamos las opciones que queremos utilizar para Fortify en el archivo config/fortify.php

Añadimos las opciones de 2FA a la migración de usuarios (por eso todavía no ejecutamos las migraciones) y estamos listos para agregar el siguiente paquete.

Ver código  (GitHub)

Sanctum

Posteriormente configuramos el paquete Laravel Sanctum, para el control de acceso a la API.

Agregamos el middleware necesario en app/Http/Kernel.php

Ver código  (GitHub)

Ahora sí estamos listos para comenzar con el desarrollo de nuestra aplicación.

Podrás seguir leyendo esta serie cuando suba los demás posts aquí.