jg interno

Pasted Sep 24, 2026, 5:05 PM1 file283 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgtbed29qz57r2a
sem-titulo
# Especificação Técnica de Integração: JG Social Media ↔ JG Interno
**Responsável Técnico**: Yuri / Patrick Santi  
**Versão do Contrato Proposto**: 1.0.0  
**Data**: 24 de Setembro de 2026  
**Status**: Proposta Técnica Implementada e Validada

---

## 1. Visão Geral da Arquitetura

Esta integração conecta o **JG Interno** (responsável pelo controle de clientes, serviços e contratos) ao **JG Social Media** (responsável pela produção, aprovação e publicação de conteúdos).

```
 ┌──────────────────────┐                     ┌────────────────────────┐
 │      JG INTERNO      │                     │    JG SOCIAL MEDIA     │
 │ (Clientes/Contratos) │                     │ (Produção/Publicações) │
 └──────────┬───────────┘                     └───────────┬────────────┘
            │                                             │
            │── POST /api/jg-interno/webhook ────────────>│ (Recebe Clientes/Planos)
            │   (client.activated, plan_updated, etc.)    │
            │                                             │
            │<── POST (JG_INTERNO_WEBHOOK_URL) ───────────│ (Envia Andamento Publicações)
            │   (publication.created, published, etc.)    │ [Fila Outbox com Retries]
            │                                             │
            │── GET /api/jg-interno/reconcile ───────────>│ (Reconciliação e Auditoria)
            │   (Consulta paginada de entregas)           │
```

---

## 2. Autenticação e Segurança

A comunicação entre os sistemas é autenticada através de **HMAC-SHA256** com proteção contra ataques de repetição (*replay attack*).

### Headers Obrigatórios em Todas as Requisições:
| Header | Descrição | Exemplo |
|---|---|---|
| `X-JG-Signature` | Assinatura HMAC-SHA256 do corpo | `sha256=d2b4f59a...` |
| `X-JG-Timestamp` | Data/hora da requisição em ISO 8601 | `2026-09-24T13:45:00-03:00` |
| `X-JG-Event-ID` | UUID único do evento (idempotência) | `c8a6f432-8419-4f68-b7db-01b4c919a30d` |
| `Content-Type` | Formato do corpo | `application/json; charset=utf-8` |

### Cálculo da Assinatura HMAC-SHA256:
A string assinada é composta por: `${X-JG-Timestamp}.${RAW_JSON_BODY}`  
Usando o algoritmo `HMAC-SHA256` com a chave secreta compartilhada.
A janela de tolerância de timestamp é de **5 minutos**. Requisições fora dessa janela são rejeitadas com HTTP 401.

### Variáveis de Ambiente Necessárias (Servidor):
- `JG_INTERNO_WEBHOOK_SECRET`: Segredo compartilhado para computação/validação do HMAC.
- `JG_INTERNO_WEBHOOK_URL`: URL do endpoint receptor no JG Interno (para onde o Social Media enviará eventos de publicações).
- `JG_INTERNO_WEBHOOK_ENABLED`: `"true"` para ativar envios reais ou `"false"` para operar em modo seguro simulado (padrão atual).

> **Aviso de Segurança**: Nenhuma chave, segredo ou token deve ser exposto no frontend ou em logs.

---

## 3. Endpoint de Entrada (JG Interno → JG Social Media)

- **URL**: `https://<dominio>/api/jg-interno/webhook`
- **Método**: `POST`
- **Autenticação**: HMAC-SHA256 (`X-JG-Signature`, `X-JG-Timestamp`) ou Token no header `X-JG-API-Key`

### Tipos de Eventos Aceitos:
1. `client.activated`: Ativação de social media para cliente novo ou contratação posterior por cliente antigo.
2. `client.plan_updated`: Alteração de plano, formatos ou quantidades contratadas.
3. `client.service_suspended`: Suspensão temporária do serviço (mantém histórico).
4. `client.service_reactivated`: Reativação de serviço suspenso.
5. `client.service_cancelled`: Cancelamento definitivo do serviço (mantém histórico).
6. `client.link_external_id`: Associação manual de cliente pré-existente ao identificador externo.

### Exemplo Completo de Entrada (`client.activated`):
```json
{
  "event_id": "c8a6f432-8419-4f68-b7db-01b4c919a30d",
  "event_type": "client.activated",
  "timestamp": "2026-09-24T13:45:00-03:00",
  "version": 1,
  "data": {
    "jg_interno_client_id": "CLI-2026-0045",
    "name": "Restaurante Sabor Mineiro",
    "plan_name": "Plano Social Media Pro",
    "quantities": {
      "posts": 16,
      "reels": 8,
      "stories": 40
    },
    "periodicity": "monthly",
    "effective_date": "2026-10-01T00:00:00-03:00",
    "service_status": "active",
    "notes": "Cliente concluiu a etapa 'Criar grupos do cliente' no JG Interno."
  }
}
```

### Resposta de Sucesso:
```json
{
  "success": true,
  "event_id": "c8a6f432-8419-4f68-b7db-01b4c919a30d",
  "message": "Evento 'client.activated' processado com sucesso para o cliente 'Restaurante Sabor Mineiro'.",
  "client": {
    "jg_interno_client_id": "CLI-2026-0045",
    "social_media_client_id": "client-restaurante-sabor-mineiro",
    "name": "Restaurante Sabor Mineiro",
    "service_status": "active",
    "plan_name": "Plano Social Media Pro",
    "quantities": {
      "posts": 16,
      "reels": 8,
      "stories": 40
    },
    "periodicity": "monthly",
    "effective_date": "2026-10-01T00:00:00-03:00",
    "version": 1,
    "created_at": "2026-09-24T16:45:00.000Z",
    "updated_at": "2026-09-24T16:45:00.000Z"
  }
}
```

---

## 4. Endpoint de Saída (JG Social Media → JG Interno)

- **URL de Destino**: Configurada em `JG_INTERNO_WEBHOOK_URL`
- **Método**: `POST`
- **Garantia de Entrega**: Fila Outbox Persistente com Retries Progressivos

### Tipos de Eventos Emitidos:
- `publication.created`: Novo conteúdo planejado/criado na grade de produção.
- `publication.updated`: Atualização de legenda, briefing, formato ou equipe.
- `publication.rescheduled`: Conteúdo teve seu prazo de publicação alterado.
- `publication.approved`: Conteúdo aprovado pelo cliente no link de aprovação.
- `publication.rejected`: Conteúdo reprovado com solicitação de alteração pelo cliente.
- `publication.published`: Conteúdo efetivamente publicado no Instagram/Rede.
- `publication.cancelled`: Conteúdo descartado ou cancelado.

### Mapeamento Oficial de Status:
| Status no Social Media | Status na Integração | Descrição para o JG Interno |
|---|---|---|
| `todo`, `producing` | `producing` | Em produção interna (briefing/arte/vídeo). |
| `approval`, `pending` | `approval` | Aguardando aprovação do cliente ou da agência. |
| `approved`, `scheduled` | `scheduled` | Conteúdo aprovado e programado para postagem. |
| `published`, `ok` | `published` | Conteúdo efetivamente postado (com link ou comprovante). |
| `cancelled`, `archived` | `cancelled` | Conteúdo descartado/cancelado. |

### Exemplo Completo de Saída (`publication.published` / `publication.rescheduled`):
```json
{
  "event_id": "evt-1790268500-a7b8c9d",
  "event_type": "publication.published",
  "timestamp": "2026-09-24T13:50:00-03:00",
  "version": 4,
  "data": {
    "jg_interno_client_id": "CLI-2026-0045",
    "social_media_client_id": "client-restaurante-sabor-mineiro",
    "post_id": "post-202609-0012",
    "title": "Vídeo Reels: Bastidores do Festival Gastronômico",
    "format": "reels",
    "social_network": "instagram",
    "status": "published",
    "due_date": "2026-09-24T12:00:00-03:00",
    "previous_due_date": "2026-09-20T18:00:00-03:00",
    "published_at": "2026-09-24T12:04:15-03:00",
    "post_url": "https://www.instagram.com/p/DAX_example/",
    "period": "2026-09",
    "content_units": 1,
    "requires_manual_confirmation": false,
    "version": 4,
    "updated_at": "2026-09-24T13:50:00-03:00",
    "notes": "Postagem realizada com sucesso no Instagram Reels."
  }
}
```

### Regras de Contagem e Datas:
- **Unidade de Contagem (`content_units`)**: Sempre fixada em `1` por conteúdo. Se o mesmo post for publicado no Instagram e no Facebook, o JG Interno deve contabilizar apenas **1 entrega** contratada.
- **Preservação de Prazos (`previous_due_date`)**: Se um post for reagendado de dia 20 para dia 24, `previous_due_date` mantém `2026-09-20`, permitindo ao JG Interno evidenciar atrasos por reagendamento.
- **Fusos Horários**: Todas as datas são emitidas com fuso horário explícito (formato ISO 8601: `-03:00` ou `Z`).

---

## 5. Garantia de Entrega e Fila Outbox

1. **Idempotência**: Reenviar o mesmo `event_id` retorna resposta imediata informando `is_duplicate: true`, sem duplicar clientes ou posts.
2. **Fila Outbox**:
   - Todo evento é gravado na fila antes ou no momento da alteração.
   - Retries automáticos com backoff progressivo: **1 min, 5 min, 15 min, 1 hora, 4 horas, 24 horas**.
   - Após 6 tentativas sem sucesso, o evento é marcado como `dead_letter` e retido para reenvio manual via endpoint `/api/jg-interno/outbox/retry`. Nenhum evento é descartado.
3. **Controle de Versão**:
   - Cada publicação possui um campo `version` sequencial (1, 2, 3...).
   - Se o JG Interno receber um evento com versão menor que a versão já salva, deve descartar a atualização para evitar concorrência ou sobrescrita fora de ordem.

---

## 6. Consulta de Reconciliação (Recuperação de Informações Faltantes)

- **URL**: `https://<dominio>/api/jg-interno/reconcile`
- **Método**: `GET`
- **Autenticação**: HMAC ou `X-JG-API-Key`

### Parâmetros de Query:
| Parâmetro | Tipo | Descrição |
|---|---|---|
| `jg_interno_client_id` | string | Filtra por cliente específico do JG Interno. |
| `period` | string | Filtra por mês de referência (ex: `2026-09`). |
| `status` | string | Filtra por status (`producing`, `approval`, `scheduled`, `published`, `cancelled`). |
| `since` | string | ISO date para sincronização delta/incremental. |
| `page` | integer | Número da página (padrão 1). |
| `limit` | integer | Quantidade por página (padrão 50, máx 200). |

### Exemplo de Resposta de Reconciliação:
```json
{
  "client": {
    "jg_interno_client_id": "CLI-2026-0045",
    "name": "Restaurante Sabor Mineiro",
    "service_status": "active"
  },
  "period": "2026-09",
  "pagination": {
    "page": 1,
    "limit": 50,
    "total_records": 12,
    "total_pages": 1
  },
  "publications": [
    {
      "jg_interno_client_id": "CLI-2026-0045",
      "post_id": "post-202609-0012",
      "title": "Vídeo Reels: Bastidores",
      "format": "reels",
      "social_network": "instagram",
      "status": "published",
      "due_date": "2026-09-24T12:00:00-03:00",
      "published_at": "2026-09-24T12:04:15-03:00",
      "version": 4,
      "is_cancelled": false,
      "is_deleted": false
    }
  ],
  "reconciled_at": "2026-09-24T13:55:00-03:00"
}
```

---

## 7. Procedimento para Associar Clientes Já Existentes

Para evitar duplicidades e nunca associar clientes automaticamente apenas pelo nome:

- **Endpoint**: `POST /api/jg-interno/link-client`
- **Body**:
```json
{
  "jg_interno_client_id": "CLI-2026-0089",
  "existing_social_media_client_id": "client-bell-maquinas",
  "name": "Bell Máquinas"
}
```
A partir desta chamada, o vínculo permanente fica estabelecido. Todos os eventos subsequentes de publicações serão enviados referenciando `"CLI-2026-0089"`.

---

## 8. Relatório de Execução dos 10 Cenários de Testes Obrigatórios

Executado e validado no servidor do JG Social Media:

| # | Cenário Testado | Status | Evidência / Detalhes |
|---|---|---|---|
| 1 | Cliente novo com social media | **PASS** | Cliente criado com `jg_interno_client_id`, plano e status active. |
| 2 | Cliente antigo que contrata depois | **PASS** | Vinculado com sucesso mantendo identificador existente. |
| 3 | Alteração das quantidades contratadas | **PASS** | Quantidades atualizadas com histórico e nova versão gravados. |
| 4 | Suspensão, reativação e cancelamento | **PASS** | Transições de status testadas preservando histórico completo. |
| 5 | Mesmo evento recebido várias vezes | **PASS** | Idempotência ativada: detectado `is_duplicate: true` sem criar registros. |
| 6 | Eventos recebidos fora de ordem | **PASS** | Versionamento sequencial `v1 -> v2 -> v3` evita dados defasados. |
| 7 | Destino indisponível e recuperação | **PASS** | Fila Outbox retém mensagens e programa retries progressivos. |
| 8 | Dados salvos com confirmação segura | **PASS** | Resposta HTTP retorna `event_id` somente após persistência. |
| 9 | Reagendamento e publicação em atraso | **PASS** | `previous_due_date` preservado demonstrando data anterior e nova. |
| 10 | Reconciliação e recuperação periódica | **PASS** | Endpoint `/api/jg-interno/reconcile` paginado e operacional. |

**Resultado Geral dos Testes**: **10/10 PASSOU (100% de conformidade)**.

12,225 chars · codepastes.com