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:
co-authored by
Claude Sonnet 4.6
parent
6e4cc7436d
commit
1c2f0ca71a
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user