"use server" import { createServiceClient } from "@/lib/supabase" import { buscarOuCriarClienteAsaas, criarPagamentoAsaas, type MetodoPagamento } from "@/lib/asaas" import { revalidatePath } from "next/cache" // ============================================= // PRODUTOS // ============================================= export async function getProdutos() { const supabase = createServiceClient() const { data, error } = await supabase .from("produtos") .select("*") .eq("ativo", true) .order("preco_centavos", { ascending: true }) if (error) throw new Error(error.message) return data } export async function getProdutoById(id: string) { const supabase = createServiceClient() const { data, error } = await supabase .from("produtos") .select("*") .eq("id", id) .single() if (error) throw new Error(error.message) return data } export async function createProduto(data: { nome: string descricao?: string tipo: "PF" | "PJ" | "SSL" | "NFe" validade: string midia?: string preco_centavos: number imagem_url?: string }) { const supabase = createServiceClient() const { error } = await supabase.from("produtos").insert(data) if (error) throw new Error(error.message) revalidatePath("/admin") revalidatePath("/produtos") return { success: true } } export async function updateProduto( id: string, data: Partial<{ nome: string descricao: string tipo: "PF" | "PJ" | "SSL" | "NFe" validade: string midia: string preco_centavos: number ativo: boolean imagem_url: string }> ) { const supabase = createServiceClient() const { error } = await supabase.from("produtos").update(data).eq("id", id) if (error) throw new Error(error.message) revalidatePath("/admin") revalidatePath("/produtos") return { success: true } } export async function deleteProduto(id: string) { const supabase = createServiceClient() const { error } = await supabase.from("produtos").update({ ativo: false }).eq("id", id) if (error) throw new Error(error.message) revalidatePath("/admin") return { success: true } } // ============================================= // CLIENTES // ============================================= export async function getClientes(options: { limit?: number; search?: string } = {}) { const supabase = createServiceClient() let query = supabase.from("clientes").select("*").order("created_at", { ascending: false }) if (options.search) { query = query.or( `nome.ilike.%${options.search}%,email.ilike.%${options.search}%,cpf_cnpj.ilike.%${options.search}%` ) } if (options.limit) query = query.limit(options.limit) const { data, error } = await query if (error) throw new Error(error.message) return data } export async function createCliente(data: { nome: string email: string telefone?: string cpf_cnpj?: string }) { const supabase = createServiceClient() const { data: cliente, error } = await supabase .from("clientes") .insert(data) .select() .single() if (error) throw new Error(error.message) revalidatePath("/admin") return cliente } // ============================================= // CHECKOUT — cria cliente no ASAAS + pedido // ============================================= export async function criarCheckout(input: { produtoId: string cliente: { nome: string email: string cpfCnpj: string telefone?: string } metodo: MetodoPagamento cartao?: { holderName: string number: string expiryMonth: string expiryYear: string ccv: string postalCode: string addressNumber: string } }) { const supabase = createServiceClient() // 1. Busca produto const { data: produto, error: produtoError } = await supabase .from("produtos") .select("*") .eq("id", input.produtoId) .eq("ativo", true) .single() if (produtoError || !produto) throw new Error("Produto não encontrado") // 2. Busca ou cria cliente no banco (por email ou CPF/CNPJ) let cliente = await supabase .from("clientes") .select("*") .or(`email.eq.${input.cliente.email},cpf_cnpj.eq.${input.cliente.cpfCnpj}`) .limit(1) .single() .then(({ data }) => data) if (!cliente) { const { data: novoCliente, error } = await supabase .from("clientes") .upsert({ nome: input.cliente.nome, email: input.cliente.email, telefone: input.cliente.telefone, cpf_cnpj: input.cliente.cpfCnpj, }, { onConflict: "cpf_cnpj" }) .select() .single() if (error) throw new Error(error.message) cliente = novoCliente } // 3. Busca ou cria cliente no ASAAS const asaasCliente = await buscarOuCriarClienteAsaas({ nome: input.cliente.nome, email: input.cliente.email, cpfCnpj: input.cliente.cpfCnpj, telefone: input.cliente.telefone, }) // Atualiza asaas_id no banco se necessário if (!cliente.asaas_id) { await supabase.from("clientes").update({ asaas_id: asaasCliente.id }).eq("id", cliente.id) } // 4. Cria pagamento no ASAAS const valorReais = produto.preco_centavos / 100 const asaasPayment = await criarPagamentoAsaas({ asaasClienteId: asaasCliente.id, valor: valorReais, descricao: `${produto.nome}`, metodo: input.metodo, cartao: input.cartao ? { holderName: input.cartao.holderName, number: input.cartao.number, expiryMonth: input.cartao.expiryMonth, expiryYear: input.cartao.expiryYear, ccv: input.cartao.ccv, } : undefined, cartaoHolder: input.cartao ? { name: input.cliente.nome, email: input.cliente.email, cpfCnpj: input.cliente.cpfCnpj, postalCode: input.cartao.postalCode, addressNumber: input.cartao.addressNumber, phone: input.cliente.telefone ?? "", } : undefined, }) // 5. Salva pedido no Supabase const { data: pedido, error: pedidoError } = await supabase .from("pedidos") .insert({ cliente_id: cliente.id, produto_id: produto.id, valor_centavos: produto.preco_centavos, metodo_pagamento: input.metodo, status: "PENDING", asaas_payment_id: asaasPayment.id, asaas_invoice_url: asaasPayment.invoiceUrl ?? asaasPayment.bankSlipUrl ?? null, pix_copia_cola: asaasPayment.pixQrCode?.payload ?? null, pix_qrcode_url: asaasPayment.pixQrCode?.encodedImage ?? null, }) .select() .single() if (pedidoError) throw new Error(pedidoError.message) // Dispara notificação PIX via n8n (fire-and-forget) if (input.metodo === "PIX" && asaasPayment.pixQrCode?.payload) { const n8nPixUrl = process.env.N8N_PIX_WEBHOOK_URL if (n8nPixUrl) { fetch(n8nPixUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ pedido_id: pedido.id, cliente: { nome: input.cliente.nome, email: input.cliente.email, telefone: input.cliente.telefone ?? "", }, produto: { nome: produto.nome, validade: produto.validade, }, valor: (produto.preco_centavos / 100).toLocaleString("pt-BR", { style: "currency", currency: "BRL", minimumFractionDigits: 2, }), pix_copia_cola: asaasPayment.pixQrCode.payload, pix_qrcode_base64: asaasPayment.pixQrCode.encodedImage, }), }).catch((err) => console.error("n8n pix webhook error:", err)) } } return { pedidoId: pedido.id, metodo: input.metodo, invoiceUrl: asaasPayment.invoiceUrl ?? asaasPayment.bankSlipUrl, pixCopiaECola: asaasPayment.pixQrCode?.payload, pixQrCodeBase64: asaasPayment.pixQrCode?.encodedImage, } } // ============================================= // PEDIDOS // ============================================= export async function getPedidos(options: { limit?: number; status?: string } = {}) { const supabase = createServiceClient() let query = supabase .from("pedidos") .select("*, clientes(nome, email, telefone, cpf_cnpj), produtos(nome, tipo, validade, midia)") .order("created_at", { ascending: false }) if (options.status) query = query.eq("status", options.status) if (options.limit) query = query.limit(options.limit) const { data, error } = await query if (error) throw new Error(error.message) return data } export async function getPedidoById(id: string) { const supabase = createServiceClient() const { data, error } = await supabase .from("pedidos") .select("*, clientes(*), produtos(*)") .eq("id", id) .single() if (error) throw new Error(error.message) return data } // ============================================= // AGENDAMENTOS // ============================================= export async function getAgendamentos(options: { limit?: number } = {}) { const supabase = createServiceClient() let query = supabase .from("agendamentos") .select("*, clientes(nome, email), produtos(nome)") .order("data_hora", { ascending: true }) if (options.limit) query = query.limit(options.limit) const { data, error } = await query if (error) throw new Error(error.message) return data } export async function criarAgendamento(data: { clienteId: string produtoId: string pedidoId?: string dataHora: string observacoes?: string }) { const supabase = createServiceClient() const { data: agendamento, error } = await supabase .from("agendamentos") .insert({ cliente_id: data.clienteId, produto_id: data.produtoId, pedido_id: data.pedidoId, data_hora: data.dataHora, observacoes: data.observacoes, }) .select() .single() if (error) throw new Error(error.message) revalidatePath("/admin") return agendamento } // ============================================= // CUPONS // ============================================= export async function getCupomDestaque() { const supabase = createServiceClient() const hoje = new Date().toISOString().slice(0, 10) const { data } = await supabase .from("cupons") .select("*") .eq("ativo", true) .eq("destaque", true) .gte("validade", hoje) .order("created_at", { ascending: false }) .limit(1) .single() return data ?? null } export async function getCupons() { const supabase = createServiceClient() const { data, error } = await supabase .from("cupons") .select("*") .order("created_at", { ascending: false }) if (error) throw new Error(error.message) return data } export async function criarCupom(input: { codigo: string descricao: string percentual: number validade: string destaque: boolean }) { const supabase = createServiceClient() const { error } = await supabase.from("cupons").insert({ codigo: input.codigo.toUpperCase(), descricao: input.descricao, percentual: input.percentual, validade: input.validade, ativo: true, destaque: input.destaque, }) if (error) throw new Error(error.message) revalidatePath("/admin") revalidatePath("/") } export async function atualizarCupom(id: string, input: Partial<{ codigo: string descricao: string percentual: number validade: string ativo: boolean destaque: boolean }>) { const supabase = createServiceClient() const { error } = await supabase.from("cupons").update(input).eq("id", id) if (error) throw new Error(error.message) revalidatePath("/admin") revalidatePath("/") } export async function deletarCupom(id: string) { const supabase = createServiceClient() const { error } = await supabase.from("cupons").delete().eq("id", id) if (error) throw new Error(error.message) revalidatePath("/admin") revalidatePath("/") }