/* |-------------------------------------------------------------------------- | Agência PSI — Edge Function: test-sms-channel |-------------------------------------------------------------------------- | Envia SMS de teste usando credenciais da plataforma. | Debita 1 crédito do tenant. | | POST body: { owner_id: string } | Retorna: { success: boolean, message: string, sid?: string } |-------------------------------------------------------------------------- */ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', 'Access-Control-Allow-Methods': 'POST, OPTIONS', } Deno.serve(async (req: Request) => { if (req.method === 'OPTIONS') { return new Response('ok', { headers: corsHeaders }) } if (req.method !== 'POST') { return new Response( JSON.stringify({ success: false, message: 'Método não permitido' }), { status: 405, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } try { const { owner_id } = await req.json() if (!owner_id) { return new Response( JSON.stringify({ success: false, message: 'owner_id é obrigatório' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } const supabase = createClient( Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! ) // 1. Busca telefone do owner const { data: profile } = await supabase .from('profiles') .select('phone, full_name, tenant_id') .eq('id', owner_id) .single() if (!profile?.phone) { return new Response( JSON.stringify({ success: false, message: 'Telefone não encontrado no seu perfil. Configure em Meu Perfil.' }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } const tenantId = profile.tenant_id || owner_id // 2. Verifica créditos SMS do tenant const { data: debitResult, error: debitErr } = await supabase.rpc('debit_addon_credit', { p_tenant_id: tenantId, p_addon_type: 'sms', p_description: `SMS teste para ${profile.phone}`, }) if (debitErr) { return new Response( JSON.stringify({ success: false, message: `Erro ao verificar créditos: ${debitErr.message}` }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } if (!debitResult?.success) { const reasonMap: Record = { no_credits: 'Sem créditos SMS. Adquira um pacote em Recursos Extras.', insufficient_balance: 'Saldo de créditos SMS insuficiente.', daily_limit_reached: 'Limite diário de envios atingido.', hourly_limit_reached: 'Limite por hora de envios atingido.', credits_expired: 'Seus créditos SMS expiraram.', } return new Response( JSON.stringify({ success: false, message: reasonMap[debitResult?.reason] || 'Sem créditos SMS disponíveis.' }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } // 3. Credenciais da plataforma const accountSid = Deno.env.get('TWILIO_ACCOUNT_SID')! const authToken = Deno.env.get('TWILIO_AUTH_TOKEN')! const fromNumber = Deno.env.get('TWILIO_FROM_NUMBER')! // Busca override de from_number do tenant const { data: creditRow } = await supabase .from('addon_credits') .select('from_number_override') .eq('tenant_id', tenantId) .eq('addon_type', 'sms') .maybeSingle() const fromAddr = creditRow?.from_number_override || fromNumber // 4. Modo mock para testes if (accountSid.startsWith('AC_TEST') || Deno.env.get('DEV') === 'true') { const mockSid = `mock_test_${crypto.randomUUID().slice(0, 8)}` console.log(`[SMS TEST MOCK] To: ${profile.phone} | SID: ${mockSid}`) return new Response( JSON.stringify({ success: true, message: `SMS de teste enviado (mock) para ${profile.phone}`, sid: mockSid }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } // 5. Envia SMS real via Twilio const url = `https://api.twilio.com/2010-04-01/Accounts/${accountSid}/Messages.json` const auth = btoa(`${accountSid}:${authToken}`) const params = new URLSearchParams() params.set('From', fromAddr) params.set('To', profile.phone) params.set('Body', 'Teste de configuração SMS — Agência PSI. Canal funcionando!') const res = await fetch(url, { method: 'POST', headers: { 'Authorization': `Basic ${auth}`, 'Content-Type': 'application/x-www-form-urlencoded', }, body: params.toString(), }) const data = await res.json() if (!res.ok) { return new Response( JSON.stringify({ success: false, message: data.message || `Erro Twilio: ${data.code || res.status}`, }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } return new Response( JSON.stringify({ success: true, message: `SMS de teste enviado para ${profile.phone}`, sid: data.sid, }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } catch (err) { console.error('[test-sms-channel] Erro:', err) return new Response( JSON.stringify({ success: false, message: err.message || 'Erro interno' }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } })