> ## Documentation Index
> Fetch the complete documentation index at: https://documentacao.legitimuz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Backend em Go

> A rota que emite a credencial e o receptor de webhook, num servidor Go com a biblioteca padrão.

A integração sem framework: `net/http` e `crypto/hmac` dão conta das duas pontas.

|               |                                                                                   |
| ------------- | --------------------------------------------------------------------------------- |
| **Stack**     | Go 1.23 · biblioteca padrão                                                       |
| **Você terá** | uma rota que emite a credencial e um endpoint de webhook com assinatura conferida |

<Card title="Confira o exemplo" icon="brand-github" href="https://github.com/Legitimuz-Tech/legitimuz-examples/tree/master/pocs/go-backend" horizontal>
  Os arquivos desta POC, com o destino de cada um.
</Card>

## Emitir a credencial

```go title="verification.go" theme={null}
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

const endpoint = "https://api.legitimuz.com/public/verifications"

type entry struct {
	Kind string `json:"kind"`
	URL  string `json:"url,omitempty"`
}

type legitimuzResponse struct {
	Verification struct {
		PublicID string `json:"public_id"`
	} `json:"verification"`
	Entry entry `json:"entry"`
}

var client = &http.Client{Timeout: 10 * time.Second}

func createVerification(w http.ResponseWriter, r *http.Request) {
	user, ok := authenticate(r)
	if !ok {
		http.Error(w, "not_authenticated", http.StatusUnauthorized)
		return
	}

	registration := db.RegistrationByUser(user.ID)

	body, _ := json.Marshal(map[string]any{
		"schema_version": "1.0",
		"ref_id":         registration.ID,
		"document":       map[string]string{"type": "cpf", "number": registration.CPF},
		"flow_public_id": os.Getenv("LEGITIMUZ_FLOW_ID"),
	})

	req, _ := http.NewRequestWithContext(r.Context(), http.MethodPost, endpoint, bytes.NewReader(body))
	req.Header.Set("X-API-Key", os.Getenv("LEGITIMUZ_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	response, err := client.Do(req)
	if err != nil {
		http.Error(w, "legitimuz_unavailable", http.StatusBadGateway)
		return
	}
	defer response.Body.Close()

	if response.StatusCode >= 400 {
		http.Error(w, fmt.Sprintf("legitimuz_%d", response.StatusCode), http.StatusBadGateway)
		return
	}

	var data legitimuzResponse
	if err := json.NewDecoder(response.Body).Decode(&data); err != nil {
		http.Error(w, "invalid_response", http.StatusBadGateway)
		return
	}

	db.Link(registration.ID, data.Verification.PublicID)

	// Só a `entry` volta ao cliente.
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{"entry": data.Entry})
}
```

## Receber o desfecho

```go title="webhook.go" theme={null}
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

const toleranceSeconds = 300

func isSignatureValid(rawBody []byte, header, secret string) bool {
	parts := map[string]string{}
	for _, part := range strings.Split(header, ",") {
		if key, value, ok := strings.Cut(part, "="); ok {
			parts[key] = value
		}
	}

	ts, err := strconv.ParseInt(parts["t"], 10, 64)
	if err != nil || parts["v1"] == "" {
		return false
	}

	if d := time.Now().Unix() - ts; d > toleranceSeconds || d < -toleranceSeconds {
		return false
	}

	mac := hmac.New(sha256.New, []byte(secret))
	fmt.Fprintf(mac, "%d.", ts)
	mac.Write(rawBody)

	return hmac.Equal([]byte(hex.EncodeToString(mac.Sum(nil))), []byte(parts["v1"]))
}

func receberWebhook(w http.ResponseWriter, r *http.Request) {
	// Lê os bytes originais. Um `json.NewDecoder(r.Body)` aqui consumiria o corpo antes da conferência.
	rawBody, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	if !isSignatureValid(rawBody, r.Header.Get("X-Legitimuz-Signature"), os.Getenv("LEGITIMUZ_WEBHOOK_SECRET")) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	if db.RecordIfNew(r.Header.Get("X-Legitimuz-Delivery")) {
		queue.Publish(rawBody)
	}

	w.WriteHeader(http.StatusOK)
}
```

<Warning>
  O `io.LimitReader` não é decoração: sem teto, um corpo grande no seu endpoint público vira
  consumo de memória sem limite.
</Warning>

## Antes de rodar

```bash title=".env" theme={null}
LEGITIMUZ_API_KEY=<SUA_CHAVE>
LEGITIMUZ_WEBHOOK_SECRET=<SEGREDO_DO_ENDPOINT>
LEGITIMUZ_FLOW_ID=<FLOW_PUBLIC_ID>
```

| Variável                   | Onde achar                                                                       |
| -------------------------- | -------------------------------------------------------------------------------- |
| `LEGITIMUZ_API_KEY`        | Integrações → Segurança → [Chaves de API](/platform/tokens). Aparece uma vez     |
| `LEGITIMUZ_WEBHOOK_SECRET` | Integrações → Segurança → [Webhooks](/platform/webhooks), na criação do endpoint |
| `LEGITIMUZ_FLOW_ID`        | Solução KYC → Fluxos, no menu da linha, em **Copiar ID do fluxo**                |

Use uma integração **sandbox**. Nenhum dos três valores vai para o browser ou para o app.

## O que esta POC não faz

<Warning>
  POC é código para entender o fluxo, não para copiar em produção. Em todas elas, `authenticate()` é
  um stub, `db` é um objeto de mentira e não há migration, observabilidade nem retentativa própria.
</Warning>

* `db` e `queue` são variáveis de pacote fictícias.
* Sem `context` de cancelamento no worker.
* Sem retry na criação. O catálogo de quando repetir está em [erros](/api/errors).

## Próximo passo

<Columns cols={2}>
  <Card title="Fila e worker" icon="stack" href="/guides/pocs/queue-worker">
    Processar o desfecho fora do request.
  </Card>

  <Card title="Boas práticas" icon="bulb" href="/help/best-practices">
    O que separa uma integração que funciona de uma que gera chamado.
  </Card>
</Columns>
