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

# Notificação de status de pagamento (DSI → Fire)

> Endpoint do Fire que recebe o status final de um pagamento do PayBridge: assinatura HMAC, payload, status, resposta e idempotência.

<Info>
  **Implementado no Fire** e verificado contra o sandbox da DSI. Falta definir o host público final; até
  então a URL é combinada por ambiente.
</Info>

Quando um pagamento criado pelo [PayBridge](/pt/api-reference/paybridge-charge) muda de status — é
aprovado, cancelado ou reembolsado — a DSI faz um `POST` neste endpoint do Fire. É o **caminho
primário** para fechar a cobrança: sem essa notificação, a cobrança fica esperando o cliente.

## Endpoint

```http theme={null}
POST https://{host-do-fire}/api/v1/webhooks/dsi/payment-status/{country}
Content-Type: application/json
X-Hmac-Signature: {assinatura}
```

<ParamField path="country" type="string" required>
  País da conexão DSI em ISO alpha-2 (`EC`, `CL`, `CO`, `AR`, `VE`, `BR`). Define **qual conexão e qual
  segredo** são usados para validar a assinatura. Aceita minúsculas.
</ParamField>

O Fire envia essa URL em cada solicitação de pagamento, dentro de `settings.callbacks.status`, então
não há nada para configurar por fora: cada pagamento já viaja com o callback do seu país.

## Autenticação: assinatura HMAC

Este endpoint **não usa API key nem bearer**. A autenticidade vem da assinatura.

<ParamField header="X-Hmac-Signature" type="string" required>
  HMAC-SHA256 em hexadecimal do **corpo cru** da requisição, calculado com o segredo da conexão do
  país. Um administrador do Fire carrega esse segredo ao configurar a conexão DSI do país — peça-o a
  ele se precisar verificar a assinatura.
</ParamField>

```js Cálculo da assinatura theme={null}
import { createHmac } from 'node:crypto'

const signature = createHmac('sha256', webhookHmacSecret)
  .update(rawBody, 'utf8')   // o body EXATO que você envia, sem re-serializar
  .digest('hex')
```

* Assina-se o **body cru**, não um JSON reconstruído: reordenar chaves ou mudar espaços invalida a
  assinatura.
* A comparação é feita em tempo constante.
* Assinatura ausente ou inválida → `400`, e **nada** é processado.

<Warning>
  Se a conexão do país ainda não tem segredo carregado, o Fire **aceita a notificação sem validar** a
  assinatura e registra um aviso. Carregue o segredo na conexão antes de ir para produção.
</Warning>

## Payload

<ParamField body="externalReference" type="string" required>
  Referência que o Fire enviou ao criar o pagamento. É a **chave de correlação**: identifica a
  tentativa (`attempt`) exata à qual a notificação pertence.
</ParamField>

<ParamField body="transactionId" type="string" required>
  Id da transação na DSI. O Fire usa como id de evento para deduplicar.
</ParamField>

<ParamField body="status" type="string" required>
  Status alcançado: `approved`, `cancelled`, `waitingPayment`, `refundPayment` ou `refundFailed`.
</ParamField>

<ParamField body="paidPrice" type="integer" required>
  Valor pago em **centavos** (inteiro). `1990` = 19,90.
</ParamField>

<ParamField body="messages" type="string">
  Mensagem do provedor (motivo da recusa, detalhe do reembolso).
</ParamField>

<ParamField body="branchId" type="string" required>
  Filial do pagamento (o `branchOffice` que o Fire enviou na criação).
</ParamField>

<RequestExample>
  ```json Pagamento aprovado theme={null}
  {
    "externalReference": "6b2c9a54-8d31-4f77-b0c6-9e3a1f5d2b88",
    "transactionId": "9f8e7d6c5b4a",
    "status": "approved",
    "paidPrice": 1990,
    "messages": "Pagamento aprovado",
    "branchId": "3f6c1b6e-52b1-4f0e-9c2a-2b7d5e8a1c40"
  }
  ```

  ```json Reembolso confirmado theme={null}
  {
    "externalReference": "6b2c9a54-8d31-4f77-b0c6-9e3a1f5d2b88",
    "transactionId": "9f8e7d6c5b4a",
    "status": "refundPayment",
    "paidPrice": 1990,
    "messages": "Reembolso processado",
    "branchId": "3f6c1b6e-52b1-4f0e-9c2a-2b7d5e8a1c40"
  }
  ```
</RequestExample>

## O que o Fire faz com cada status

| `status`         | Tentativa no Fire | Efeito                                                                                   |
| ---------------- | ----------------- | ---------------------------------------------------------------------------------------- |
| `approved`       | `succeeded`       | Soma ao valor cobrado e recalcula a cobrança (`succeeded` ou `partially_paid` no misto). |
| `cancelled`      | `canceled`        | Fecha a tentativa; a cobrança passa a `canceled` se não sobrar nenhuma viva.             |
| `refundPayment`  | `refunded`        | Fecha o reembolso (aplica até sobre uma tentativa já aprovada).                          |
| `refundFailed`   | `solving`         | O reembolso segue em andamento; o motivo é guardado na tentativa.                        |
| `waitingPayment` | *(sem mudança)*   | Informativo: o link está esperando o cliente.                                            |

`paidPrice` é guardado como referência do provedor; o valor que o Fire credita é o da tentativa.

## Resposta

O Fire responde **`200`** assim que valida a assinatura e **enfileira** a notificação. A transição de
status é aplicada por um worker segundos depois.

```json 200 theme={null}
{
  "message": "received",
  "duplicate": false,
  "webhookEventId": "7c1e9a02-4b56-4c3d-8a1f-0d2b6e9f3a55"
}
```

| HTTP  | Quando                                                                                     |
| ----- | ------------------------------------------------------------------------------------------ |
| `200` | Recebida e enfileirada. Inclui duplicados (`duplicate: true`) e referências desconhecidas. |
| `400` | Assinatura inválida ou payload fora do contrato. Nada é enfileirado.                       |
| `5xx` | Erro inesperado do Fire. **Repetir.**                                                      |

<Note>
  Um `200` significa *recebida*, não *aplicada*. Para saber o resultado final, consulte a cobrança com
  `GET /api/v1/external/paybridge/intents/{intentId}`.
</Note>

## Idempotência e repetições

<CardGroup cols={2}>
  <Card title="Deduplicação" icon="copy">
    O Fire deduplica pela trinca **`externalReference` + `transactionId` + `status`**. Reenviar a mesma
    notificação devolve `200` com `duplicate: true` e não processa de novo.
  </Card>

  <Card title="Sem retrocesso" icon="lock">
    Uma tentativa já em status terminal não volta atrás por uma notificação atrasada. A única exceção
    são os reembolsos, que se aplicam sobre um pagamento aprovado.
  </Card>

  <Card title="Referência desconhecida" icon="circle-question">
    Se o `externalReference` não corresponde a nenhuma tentativa, o Fire responde `200` e descarta,
    para a DSI não repetir para sempre.
  </Card>

  <Card title="Repetições seguras" icon="rotate">
    O processamento é idempotente: dá para repetir após um `5xx` sem risco de aplicar o mesmo status
    duas vezes.
  </Card>
</CardGroup>

## Relacionado

<CardGroup cols={2}>
  <Card title="Cobrar a partir de um canal" icon="credit-card" href="/pt/api-reference/paybridge-charge">
    Como se cria a cobrança que esta notificação fecha.
  </Card>

  <Card title="Métodos suportados por país" icon="globe" href="/pt/manuals/paybridge/supported-methods">
    Quais métodos cobram hoje pela DSI em cada país.
  </Card>
</CardGroup>
