Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgtbed29qz57r2asem-tituloPlain Text
# 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