fix: strip leading + from phone number before sending to SMS provider

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-25 15:26:57 -05:00
co-authored by Claude Sonnet 4.6
parent 6e4cc7436d
commit 1c2f0ca71a
+71
View File
@@ -0,0 +1,71 @@
import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
const SMS_ENDPOINT = 'https://admin.u-site.app/api/sms/send';
const OTP_TTL_MS = 5 * 60 * 1000;
@Injectable()
export class SmsService {
private readonly logger = new Logger(SmsService.name);
private readonly otps = new Map<string, { code: string; expiresAt: number }>();
constructor(private prisma: PrismaService) {}
async getConfig(): Promise<{ api_key: string } | null> {
const setting = await this.prisma.settings.findUnique({ where: { key: 'sms_config' } });
return (setting?.value as { api_key: string }) ?? null;
}
async saveConfig(api_key: string): Promise<void> {
await this.prisma.settings.upsert({
where: { key: 'sms_config' },
create: { key: 'sms_config', value: { api_key } },
update: { value: { api_key } },
});
}
async send(numero: string, mensaje: string): Promise<{ ok: boolean; id?: string }> {
const config = await this.getConfig();
if (!config?.api_key) throw new BadRequestException('SMS API key no configurada');
// El proveedor espera el número sin + (ej: 573001234567)
const normalizedNumero = numero.startsWith('+') ? numero.slice(1) : numero;
const res = await fetch(SMS_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.api_key}`,
},
body: JSON.stringify({ numero: normalizedNumero, mensaje }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
this.logger.error(`SMS error ${res.status}: ${JSON.stringify(err)}`);
throw new BadRequestException(err.error || `Error al enviar SMS: ${res.status}`);
}
const result = await res.json();
this.logger.log(`SMS enviado a ${numero}`);
return result;
}
async sendOtp(phone: string): Promise<void> {
const code = Math.floor(100000 + Math.random() * 900000).toString();
this.otps.set(phone, { code, expiresAt: Date.now() + OTP_TTL_MS });
await this.send(phone, `Tu código de verificación ProsApp es: ${code}. Válido por 5 minutos.`);
}
verifyOtp(phone: string, code: string): boolean {
const entry = this.otps.get(phone);
if (!entry) return false;
if (Date.now() > entry.expiresAt) {
this.otps.delete(phone);
return false;
}
if (entry.code !== code) return false;
this.otps.delete(phone);
return true;
}
}