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