# Carteiras externas

> Conecte uma implantação dedicada do Nodaro Cloud à sua carteira compartilhada, que reserva, liquida e informa os créditos de cada cliente em seus produtos.

Source: https://nodaro.ai/pt-BR/docs/developers/external-wallet

Uma **carteira externa** é um serviço que você opera, que guarda o orçamento compartilhado de cada cliente e que uma implantação dedicada do Nodaro Cloud consulta antes de gastar. O Nodaro pede à carteira que reserve créditos antes de cada geração, liquida o valor exato depois e lê o saldo para mostrá-lo ao cliente. A conta de cobrança da implantação continua pagando o uso da plataforma com o próprio saldo pré-pago; a carteira decide quanto disso cada cliente pode usar, em todos os seus produtos.

## Como as peças se encaixam
- **Uma conta de cobrança paga.** A implantação define uma conta de cobrança (`billing.payerAccount` no perfil de superfície da implantação), cujo saldo pré-pago paga todas as ações da implantação. Veja [Edições e perfis de superfície](https://nodaro.ai/docs/self-hosting/editions-and-profiles).
- **O SSO identifica o cliente.** Os clientes entram pelo [provedor de SSO](https://nodaro.ai/docs/developers/sso) da implantação. O subject que o provedor atribui a um cliente, enviado como `sso_subject`, é como a sua carteira reconhece o cliente.
- **A carteira decide.** Em cada requisição de nó individual e em cada nó de workflow, o Nodaro verifica o saldo pré-pago da conta de cobrança e depois pede à sua carteira uma reserva no orçamento compartilhado do cliente. O saldo local de créditos do Nodaro do cliente não é considerado.

Um saldo pré-pago vazio pode interromper uma requisição antes de a carteira ser consultada. Depois da consulta, a decisão da carteira é final para o orçamento compartilhado do cliente.

### Reservar
Um cliente inicia uma geração. O Nodaro envia `reserve` com um `operation_id` e um valor, e a sua carteira reserva esse valor de forma atômica e responde `allow` ou `deny`.

### Executar
Com `allow`, o modelo é executado. Com qualquer outra resposta, nada é enviado ao modelo.

### Liquidar
Quando o trabalho termina, o Nodaro envia `settle` com o valor real. A sua carteira cobra exatamente esse valor e libera o restante da reserva.

### Mostrar o saldo
O estúdio pede à sua carteira o `balance` do cliente e o mostra na unidade de exibição da implantação.

## Configurar a implantação
Defina estas variáveis na API e nos workers:

| Variável | O que define |
| --- | --- |
| `DEPLOYMENT_WALLET_URL` | O endpoint da carteira. Precisa usar HTTPS. |
| `DEPLOYMENT_WALLET_TOKEN` | Um segredo de servidor dedicado, que o Nodaro envia como token Bearer. |
| `DEPLOYMENT_WALLET_SSO_PROVIDER` | O id do provedor de SSO cujos subjects identificam os clientes. |
| `DEPLOYMENT_WALLET_TIMEOUT_MS` | O tempo limite de cada chamada à carteira, de 1000 a 30000 milissegundos. O padrão é 10000. |

- **Defina as três primeiras juntas.** Deixe as três sem valor para manter a cobrança atual da implantação.
- **Desative os limites de uso locais.** A aplicação dos limites de uso por usuário, `billing.allowances: "enforce"`, precisa estar desativada. Uma configuração que combina os dois é recusada na ativação.
- **Combine a unidade antes.** Antes de ativar a carteira, combine a conversão entre os créditos do Nodaro e o orçamento do seu produto. Defina `billing.unitRate` e `billing.unitLabel` no perfil de superfície de acordo com essa conversão, para que os dois produtos mostrem o mesmo número.

## O contrato das requisições
O Nodaro envia toda chamada como um `POST` com um corpo JSON que traz `contract: 1` e `unit: "nodaro_credit"`, e o cabeçalho `Authorization: Bearer <token>`. Os valores são créditos do Nodaro. Nunca os interprete como unidades do seu próprio produto sem uma conversão explícita, controlada pela sua carteira.

### Reservar
```json
{
"contract": 1,
"unit": "nodaro_credit",
"action": "reserve",
"operation_id": "a3354131-4e5e-43a6-8f61-3914f09c658e",
"job_id": null,
"user_id": "5bf0d884-47b1-468e-a7b2-2433f957b267",
"sso_provider": "partner",
"sso_subject": "customer-123",
"model_identifier": "example-model",
"reserved_credits": 30
}
```

| Campo | Significado |
| --- | --- |
| `operation_id` | Um registro de uso. Um job ou um workflow pode conter vários. |
| `sso_subject` | O cliente. Ele vem de dados de login confiáveis, gerenciados pelo servidor do Nodaro, então use-o para identificar o cliente. |
| `sso_provider`, `user_id` | O provedor de SSO e o usuário do Nodaro por trás da requisição. |
| `model_identifier` | O modelo que está sendo cobrado. |
| `reserved_credits` | O número inteiro de créditos a reservar. |

Reserve o valor de forma atômica no orçamento compartilhado disponível do cliente antes de responder. Para permitir a requisição:

```json
{"contract":1,"unit":"nodaro_credit","operation_id":"a3354131-4e5e-43a6-8f61-3914f09c658e","decision":"allow","reserved_credits":30}
```

Para saldo insuficiente ou uma conta que não pode gastar, responda HTTP `200` com o mesmo envelope e `"decision": "deny"`; nenhum valor é necessário. Em caso de erro no serviço, responda com um status fora da faixa `2xx`.

Nada chega ao modelo sem a permissão da sua carteira. Um timeout, uma resposta malformada, um valor ou id divergente, uma identidade ausente e uma recusa interrompem a requisição. O cabeçalho `Idempotency-Key` da requisição é `<operation_id>:reserve`.

### Liquidar ou liberar
Quando o trabalho termina, o Nodaro envia os mesmos campos da operação com `"action": "settle"` e `actual_credits`. Cobre exatamente esse valor e libere o restante da reserva. Uma liquidação de `0` libera tudo.

```json
{"contract":1,"unit":"nodaro_credit","operation_id":"a3354131-4e5e-43a6-8f61-3914f09c658e","settled":true,"actual_credits":12}
```

- **A chave é `<operation_id>:settle`.** A entrega acontece pelo menos uma vez. Uma nova tentativa precisa devolver o resultado original sem cobrar de novo.
- **Recuse conflitos.** Rejeite uma requisição para a mesma operação com identidade, teto de reserva ou valor final diferente.
- **Uma liquidação pode chegar antes de uma reserva atrasada.** Uma liquidação de `0` precisa criar um registro final permanente, mesmo para uma operação que você ainda não conhece. Uma reserva posterior nunca pode reabrir esse registro.
- **As reservas nunca expiram.** Uma geração, e uma revisão humana do resultado dela, podem durar mais que qualquer requisição HTTP. Mantenha cada reserva até ela ser liquidada, e guarde os seus registros de idempotência. Nunca libere uma reserva ativa porque um callback está atrasado.

### Saldo
```json
{"contract":1,"unit":"nodaro_credit","action":"balance","user_id":"5bf0d884-47b1-468e-a7b2-2433f957b267","sso_provider":"partner","sso_subject":"customer-123"}
```

```json
{"contract":1,"unit":"nodaro_credit","sso_subject":"customer-123","available_credits":250}
```

- **Disponível significa depois das reservas.** `available_credits` exclui todas as reservas em aberto, em todos os seus produtos.
- **Aqui, frações são permitidas.** O saldo pode ser fracionário, então um resto menor que um crédito cobrável ainda pode ser mostrado. Os valores de reserva e de liquidação são sempre créditos inteiros.
- **Um único número no estúdio.** O estúdio converte o saldo uma vez para a unidade de exibição da implantação e oculta a própria exibição do limite de uso por usuário. Um saldo indisponível aparece como desconhecido.
- **A reserva decide as disputas.** Quando requisições chegam ao mesmo tempo, quem vale é a chamada de reserva, e não o saldo.

## Entrega e recuperação
- O Nodaro tenta entregar cada liquidação na hora. Se isso falhar, um worker executado a cada minuto tenta de novo, com um backoff de até uma hora. As liquidações sem confirmação são guardadas até serem entregues.
- Por conta própria, o Nodaro cancela só as reservas que nunca foram autorizadas. Os jobs autorizados em execução seguem a recuperação normal de jobs.
- **Nunca remova a configuração da carteira, nem a aponte para outra carteira, enquanto houver operações em aberto.** Para rotacionar o token, rotacione-o na mesma carteira.

## Descoberta de preços
`GET /v1/deployment-billing/pricing` devolve a tabela de preços da implantação, para a exibição e o planejamento da sua própria carteira. O endpoint exige a sessão da conta de cobrança ou uma das chaves de integração de cobrança dela.

| Campo | Significado |
| --- | --- |
| `creditCost` | A tarifa base de um modelo. |
| `creditIdentifier` | O identificador que é cobrado. |
| `pricingBasis` | O que a tarifa base pressupõe. |
| `pricing` | Para modelos de texto, os identificadores de cada operação. Os modelos de texto aparecem como `llm-chat` com as configurações padrão. |
| `denomination` | A unidade de crédito e a conversão para exibição. |
| `purchaseOptions` | As recargas padrão em USD, com o preço efetivo por crédito. Termos comerciais separados podem se aplicar. |

- As variantes de mídia e as operações de texto são resolvidas pelos preços em vigor. Uma variante sem preço configurado fica `null`, com um código de erro.
- Configurações, quantidade, duração e uso medido podem mudar a cobrança final. Faça a contabilidade com os valores de reserva e de liquidação, nunca com uma tarifa base desta lista.
- Para detectar mudanças, prefira uma requisição completa com `If-None-Match`. O parâmetro `since` acompanha os timestamps das linhas de preço e não enxerga todas as mudanças de configuração em tempo de execução.

## Frequently asked questions

### O que é uma carteira externa no Nodaro?

Um serviço que você opera e que guarda o orçamento compartilhado de cada cliente entre os seus produtos. Uma implantação dedicada do Nodaro Cloud pede a ele que reserve créditos antes de uma geração ser executada, liquida o valor real depois e lê o saldo para exibi-lo.

### Quais implantações do Nodaro podem usar uma carteira externa?

Uma implantação dedicada do Nodaro Cloud com uma conta de cobrança, a única conta que paga por todas as ações da implantação. Os clientes dela entram pelo provedor de SSO da implantação, que os identifica para a carteira.

### O que acontece se a carteira não responder a tempo?

Nada é gerado. Um timeout, uma resposta malformada, um valor ou id divergente, uma identidade ausente e uma recusa interrompem a requisição antes que qualquer modelo seja executado.

### A mesma requisição de liquidação pode chegar duas vezes?

Sim. A entrega acontece pelo menos uma vez, então a sua carteira precisa devolver o resultado original para uma liquidação repetida, sem cobrar de novo. O cabeçalho Idempotency-Key identifica as repetições.

### As reservas expiram?

Não. Uma geração e uma revisão humana podem levar mais tempo que qualquer requisição HTTP, então uma reserva continua até o Nodaro liquidá-la. Nunca libere uma reserva ativa porque um callback está atrasado.
