# 🔐 Sistema KYC com Google Cloud Vision - TudoAqui

## 🎯 Fluxo Técnico de Verificação

### **3 Etapas do KYC Automático:**

```
1️⃣ CAPTURA       → Utilizador envia BI + Selfie
2️⃣ PROCESSAMENTO → Google Vision OCR + Face Detection
3️⃣ VALIDAÇÃO     → Comparar nome + rosto → ✅ Selo Verde
```

---

## 📋 Implementação Completa

### **Arquivo: `src/modules/kyc/kyc.service.ts`**

#### **1️⃣ Processamento do BI (`processBI`)**

**Fluxo:**
```typescript
1. Recebe foto do BI (buffer)
2. Adiciona marca d'água "TudoAqui - KYC" (Sharp)
3. Guarda em uploads/bi/{userId}_{timestamp}.jpg
4. Executa OCR com Google Cloud Vision
5. Extrai: nome + número do BI (formato: 000000000XY00)
6. Atualiza user.fotoBI e user.biNumero no PostgreSQL
```

**Código Principal:**
```typescript
// OCR do BI via Google Cloud Vision
const dadosBI = await this.extractBIData(watermarked);
// Retorna: { nome: 'João Silva', numeroBI: '123456789AO01' }
```

**Regex para BI Angolano:**
```regex
/(\d{9}[A-Z]{2}\d{2})/
```

---

#### **2️⃣ Processamento da Selfie (`processSelfie`)**

**Fluxo:**
```typescript
1. Recebe selfie (buffer)
2. Guarda em uploads/selfie/{userId}_{timestamp}.jpg
3. Liveness Detection → Verifica exatamente 1 rosto
4. Confiança > 80% → livenessDetected = true
5. Atualiza user.fotoSelfie no PostgreSQL
```

**Código Principal:**
```typescript
const [result] = await this.visionClient.faceDetection({
  image: { content: imageBuffer },
});

const faces = result.faceAnnotations || [];
return faces.length === 1 && faces[0].detectionConfidence > 0.8;
```

---

#### **3️⃣ Análise e Aprovação Automática (`analyzeWithAI`)**

**Fluxo:**
```typescript
1. Re-executa OCR no BI para extrair nome
2. Compara nome do BI com user.nome (normalizado, sem acentos)
3. Detecta rostos em BI + Selfie (Google Vision Face Detection)
4. Verifica se ambos têm landmarks (nariz, olhos, boca)
5. SE nomeCoincide && rostosCorrespondem → Aprova
6. Atualiza: user.kycStatus = 'aprovado', user.seloVerificado = true
```

**Validação de Nome:**
```typescript
// Normaliza: Remove acentos, lowercase, só letras
normalize('João Silva') → 'joao silva'
normalize('SILVA, JOÃO') → 'silva joao'

// Match: BI contém nome da conta OU conta contém BI
'joao silva'.includes('joao') → true ✅
```

**Validação de Rosto:**
```typescript
// Ambos devem ter > 5 landmarks detectados
biHasLandmarks && selfieHasLandmarks → true ✅

// TODO: Implementar cálculo de distância euclidiana para produção
```

---

## 🔧 Configuração Google Cloud Vision

### **1. Obter Credenciais:**
```bash
# 1. Ir para console.cloud.google.com
# 2. Ativar "Cloud Vision API"
# 3. Criar Service Account
# 4. Baixar chave JSON (ex: tudoaqui-kyc-key.json)
```

### **2. Configurar no Backend:**
```bash
# Linux/Mac:
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/tudoaqui-kyc-key.json"

# Windows (PowerShell):
$env:GOOGLE_APPLICATION_CREDENTIALS="C:\xampp\htdocs\TudoAqui\backend\tudoaqui-kyc-key.json"
```

### **3. Adicionar ao `.env`:**
```env
# Google Cloud Vision
GOOGLE_APPLICATION_CREDENTIALS=./tudoaqui-kyc-key.json
GOOGLE_CLOUD_PROJECT_ID=tudoaqui-production
```

---

## 📤 Endpoints da API

### **POST /api/v1/kyc/upload-bi**
```json
// Request (multipart/form-data)
{
  "file": [imagem do BI],
  "userId": "uuid"
}

// Response
{
  "message": "Foto do BI carregada com sucesso",
  "url": "uploads/bi/uuid_1769054884237.jpg",
  "dadosExtraidos": {
    "nome": "João Silva",
    "numeroBI": "123456789AO01"
  }
}
```

---

### **POST /api/v1/kyc/upload-selfie**
```json
// Request (multipart/form-data)
{
  "file": [selfie],
  "userId": "uuid"
}

// Response
{
  "message": "Selfie carregada com sucesso",
  "url": "uploads/selfie/uuid_1769054900000.jpg",
  "livenessDetected": true
}
```

---

### **POST /api/v1/kyc/analyze**
```json
// Request
{
  "userId": "uuid"
}

// Response (Aprovado)
{
  "message": "✅ Verificação aprovada! Selo verde atribuído.",
  "validacoes": {
    "nomeCoincide": true,
    "rostosCorrespondem": true,
    "aprovado": true
  },
  "seloVerificado": true
}

// Response (Rejeitado)
{
  "message": "❌ Verificação rejeitada. Dados não coincidem.",
  "validacoes": {
    "nomeCoincide": false,
    "rostosCorrespondem": true,
    "aprovado": false
  },
  "seloVerificado": false
}
```

---

### **GET /api/v1/kyc/status/:userId**
```json
{
  "kycStatus": "aprovado",
  "seloVerificado": true,
  "biEnviado": true,
  "selfieEnviada": true,
  "numeroBI": "123456789AO01"
}
```

---

## 🧪 Como Testar

### **1. Upload do BI:**
```bash
curl -X POST http://localhost:3000/api/v1/kyc/upload-bi \
  -H "Authorization: Bearer {JWT_TOKEN}" \
  -F "file=@bi-frente.jpg" \
  -F "userId=c36da64a-7a6b-4143-98ac-a7a6c0ccdf68"
```

### **2. Upload da Selfie:**
```bash
curl -X POST http://localhost:3000/api/v1/kyc/upload-selfie \
  -H "Authorization: Bearer {JWT_TOKEN}" \
  -F "file=@selfie.jpg" \
  -F "userId=c36da64a-7a6b-4143-98ac-a7a6c0ccdf68"
```

### **3. Analisar e Aprovar:**
```bash
curl -X POST http://localhost:3000/api/v1/kyc/analyze \
  -H "Authorization: Bearer {JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"userId": "c36da64a-7a6b-4143-98ac-a7a6c0ccdf68"}'
```

---

## 🔐 Segurança Implementada

### **1. Marca d'água Anti-Fraude**
```typescript
// SVG "TudoAqui - KYC" no canto inferior direito
// Impede reutilização de fotos roubadas
watermarkSvg = `<text>TudoAqui - KYC</text>`
```

### **2. Liveness Detection**
- Verifica exatamente 1 rosto na selfie
- Confiança > 80% (Google Vision)
- Previne foto de foto

### **3. Comparação de Nome**
- Normalização: Remove acentos, case-insensitive
- Match parcial: BI contém nome OU nome contém BI
- Exemplo: "João Silva" ≈ "SILVA, JOÃO"

### **4. Comparação de Rosto**
- Ambos devem ter > 5 landmarks detectados
- TODO: Calcular distância euclidiana entre pontos faciais

---

## ⚠️ Limitações Atuais

### **Comparação de Rostos:**
Atualmente implementação **básica** (apenas verifica se landmarks existem).

**Para produção, implementar:**
```typescript
// Cálculo de distância euclidiana entre landmarks
function calculateFaceSimilarity(landmarks1, landmarks2) {
  const distances = landmarks1.map((l1, i) => {
    const l2 = landmarks2[i];
    const dx = l1.position.x - l2.position.x;
    const dy = l1.position.y - l2.position.y;
    return Math.sqrt(dx * dx + dy * dy);
  });
  
  const avgDistance = distances.reduce((a, b) => a + b) / distances.length;
  return avgDistance < THRESHOLD; // Similaridade > 90%
}
```

**OU usar AWS Rekognition CompareFaces:**
```typescript
import { CompareFacesCommand } from '@aws-sdk/client-rekognition';

const similarity = await rekognition.send(new CompareFacesCommand({
  SourceImage: { Bytes: biBuffer },
  TargetImage: { Bytes: selfieBuffer },
  SimilarityThreshold: 90,
}));
```

---

## 📊 Estatísticas e Logs

### **Logs no Console:**
```
[KYC] Erro no OCR do BI: InvalidImageException
[KYC] Erro no Liveness Detection: RateLimitExceeded
[KYC] Erro na comparação de rostos: ServiceUnavailable
```

### **Monitorização em Produção:**
```typescript
// Integrar Sentry ou Datadog
import * as Sentry from '@sentry/node';

Sentry.captureException(err, {
  tags: { module: 'kyc', operation: 'face-comparison' },
  extra: { userId, timestamp },
});
```

---

## 🚀 Próximos Passos

1. ✅ **Instalado:** `@google-cloud/vision`
2. ⏳ **Configurar:** Credenciais Google Cloud (service account JSON)
3. ⏳ **Testar:** Upload BI + Selfie + Analisar
4. ⏳ **Melhorar:** Comparação de rostos com distância euclidiana
5. ⏳ **Produção:** Migrar uploads para AWS S3 ou Cloudinary

---

**Backend atualizado!** Agora o KYC usa Google Cloud Vision em vez de AWS Rekognition.

**Para testar:** Configure `GOOGLE_APPLICATION_CREDENTIALS` e execute os endpoints no Postman.
