102 lines
3.5 KiB
PL/PgSQL
102 lines
3.5 KiB
PL/PgSQL
-- Alter the activation_codes table to accept a 9 digit string for card_id instead of a UUID
|
|
ALTER TABLE activation_codes ALTER COLUMN card_id TYPE VARCHAR(9);
|
|
|
|
-- Alter the activation_logs table to accept a 9 digit string for card_id instead of a UUID
|
|
ALTER TABLE activation_logs ALTER COLUMN card_id TYPE VARCHAR(9);
|
|
|
|
-- Drop the old procedure which accepted UUID
|
|
DROP FUNCTION IF EXISTS activate_payment_card(UUID, TEXT, INET, JSONB);
|
|
|
|
-- Recreate it to accept VARCHAR(9)
|
|
CREATE OR REPLACE FUNCTION activate_payment_card(
|
|
p_card_id VARCHAR(9), -- 9 digit string de la tarjeta
|
|
p_input_code TEXT, -- El código que mete el usuario, puede ser incorrecto en este punto
|
|
p_ip_address INET, -- Datos de origen de la activación
|
|
p_device_info JSONB -- Datos del dispositivo de origen
|
|
)
|
|
RETURNS TABLE (
|
|
success BOOLEAN,
|
|
message TEXT
|
|
) AS $$
|
|
DECLARE
|
|
v_code_hash TEXT;
|
|
v_is_used BOOLEAN;
|
|
v_is_blocked BOOLEAN;
|
|
v_expires_at TIMESTAMPTZ;
|
|
v_failed_attempts INT;
|
|
v_max_attempts CONSTANT INT := 3;
|
|
BEGIN
|
|
-- 1. Obtener datos del código y bloquear la fila para actualización (FOR UPDATE)
|
|
-- sería raro que 2 clientes intentasen modificar la tarjeta a la vez.
|
|
SELECT code_hash, is_used, is_blocked, expires_at, failed_attempts
|
|
INTO v_code_hash, v_is_used, v_is_blocked, v_expires_at, v_failed_attempts
|
|
FROM activation_codes
|
|
WHERE card_id = p_card_id
|
|
FOR UPDATE;
|
|
|
|
-- 2. Validaciones:
|
|
-- 2.1 Si no existe ninguna tarjeta con ese card_id
|
|
IF NOT FOUND THEN
|
|
RETURN QUERY SELECT FALSE, 'CARD_NOT_FOUND';
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- 2.2 Si el código introducido ya ha sido usado
|
|
IF v_is_used THEN
|
|
RETURN QUERY SELECT FALSE, 'ALREADY_ACTIVATED';
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- 2.3 Si el código ya ha sido bloqueado o se ha intentado demasiadas veces
|
|
IF v_is_blocked OR v_failed_attempts >= v_max_attempts THEN
|
|
RETURN QUERY SELECT FALSE, 'CODE_BLOCKED';
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- 2.4 Si el código es demasiado viejo
|
|
IF v_expires_at < NOW() THEN
|
|
RETURN QUERY SELECT FALSE, 'CODE_EXPIRED';
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- 3. Verificación del Hash del codigo que ha introducido el usuario
|
|
-- En la bdd guardo el hash, al procedimiento se introduce el codigo per-se
|
|
-- Si el código NO coincide:
|
|
IF v_code_hash != crypt(p_input_code, v_code_hash) THEN
|
|
UPDATE activation_codes
|
|
-- 3.1 Control de intentos
|
|
SET failed_attempts = failed_attempts + 1,
|
|
is_blocked = (failed_attempts + 1 >= v_max_attempts)
|
|
WHERE card_id = p_card_id;
|
|
-- 3.2 Se loguea el fallo
|
|
INSERT INTO activation_logs (card_id, action_type, ip_address, device_info)
|
|
VALUES (p_card_id, 'FAILED_ATTEMPT', p_ip_address, p_device_info);
|
|
|
|
RETURN QUERY SELECT FALSE, 'INVALID_CODE';
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- 4. Si el código ES correcto:
|
|
-- Marcar código como usado
|
|
UPDATE activation_codes
|
|
SET is_used = TRUE
|
|
WHERE card_id = p_card_id;
|
|
|
|
-- Activar la tarjeta
|
|
UPDATE payment_cards
|
|
SET status = 'ACTIVE',
|
|
activated_at = NOW()
|
|
WHERE card_id = p_card_id;
|
|
|
|
-- Registrar éxito en auditoría
|
|
INSERT INTO activation_logs (card_id, action_type, ip_address, device_info)
|
|
VALUES (p_card_id, 'ACTIVATION_SUCCESS', p_ip_address, p_device_info);
|
|
|
|
RETURN QUERY SELECT TRUE, 'SUCCESS';
|
|
|
|
EXCEPTION WHEN OTHERS THEN
|
|
-- En caso de error inesperado, Postgres hace rollback automático
|
|
RETURN QUERY SELECT FALSE, 'INTERNAL_ERROR: ' || SQLERRM;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|