# NXGate SDK para Elixir
SDK oficial da **NXGATE** para integração com a API PIX em aplicações Elixir.
## Funcionalidades
- Geração de cobranças PIX (cash-in) com QR Code
- Saques PIX (cash-out)
- Consulta de saldo e transações
- Gerenciamento automático de token OAuth2 via GenServer
- Assinatura HMAC-SHA256 automática (quando configurado)
- Parser de webhooks para eventos PIX
- Retry automático com backoff em erros 503
- Zero dependências HTTP externas (usa `:httpc` da stdlib Erlang)
## Requisitos
- Elixir ~> 1.14
- Erlang/OTP ~> 25
## Instalação
Adicione `nxgate` às suas dependências no `mix.exs`:
```elixir
def deps do
[
{:nxgate, "~> 0.1.0"}
]
end
```
## Configuração
### Início Rápido
```elixir
# Iniciar o client (geralmente no seu application supervisor)
{:ok, client} = NXGate.start_link(
client_id: "nxgate_xxx",
client_secret: "secret",
hmac_secret: "opcional" # opcional - ativa assinatura HMAC
)
```
### Uso com Supervisor
Adicione o client ao seu supervisor para gerenciamento automático do ciclo de vida:
```elixir
defmodule MyApp.Application do
use Application
@impl true
def start(_type, _args) do
children = [
{NXGate,
client_id: System.fetch_env!("NXGATE_CLIENT_ID"),
client_secret: System.fetch_env!("NXGATE_CLIENT_SECRET"),
hmac_secret: System.get_env("NXGATE_HMAC_SECRET"),
name: :nxgate}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
```
### Opções de Configuração
| Opção | Tipo | Obrigatório | Padrão | Descrição |
|-------|------|:-----------:|--------|-----------|
| `client_id` | `string` | Sim | - | ID do client OAuth2 |
| `client_secret` | `string` | Sim | - | Secret do client OAuth2 |
| `hmac_secret` | `string` | Não | `nil` | Secret para assinatura HMAC-SHA256 |
| `base_url` | `string` | Não | `https://api.nxgate.com.br` | URL base da API |
| `timeout` | `integer` | Não | `15000` | Timeout em milissegundos |
| `max_retries` | `integer` | Não | `2` | Máximo de retentativas em erro 503 |
## Uso
### Gerar Cobrança PIX (Cash-in)
Gera uma cobrança PIX e retorna o QR Code para pagamento:
```elixir
{:ok, charge} = NXGate.pix_generate(client, %{
valor: 100.00,
nome_pagador: "João da Silva",
documento_pagador: "12345678901",
webhook: "https://meusite.com/webhook",
descricao: "Pagamento do pedido #123"
})
# Resposta:
# %{
# "status" => "success",
# "message" => "...",
# "paymentCode" => "00020126...",
# "idTransaction" => "abc123",
# "paymentCodeBase64" => "data:image/png;base64,..."
# }
```
#### Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
|-----------|------|:-----------:|-----------|
| `valor` | `float` | Sim | Valor em reais |
| `nome_pagador` | `string` | Sim | Nome do pagador |
| `documento_pagador` | `string` | Sim | CPF ou CNPJ do pagador |
| `forcar_pagador` | `boolean` | Não | Forçar dados do pagador |
| `email_pagador` | `string` | Não | Email do pagador |
| `celular` | `string` | Não | Celular do pagador |
| `descricao` | `string` | Não | Descrição da cobrança |
| `webhook` | `string` | Não | URL de callback |
| `magic_id` | `string` | Não | ID mágico para referência |
| `api_key` | `string` | Não | Chave de API |
| `split_users` | `list` | Não | Split de pagamento |
#### Split de Pagamento
```elixir
{:ok, charge} = NXGate.pix_generate(client, %{
valor: 100.00,
nome_pagador: "Maria Santos",
documento_pagador: "98765432100",
split_users: [
%{username: "loja_principal", percentage: 90.0},
%{username: "parceiro", percentage: 10.0}
]
})
```
### Saque PIX (Cash-out)
Realiza uma transferência PIX para a chave informada:
```elixir
{:ok, withdrawal} = NXGate.pix_withdraw(client, %{
valor: 50.0,
chave_pix: "joao@email.com",
tipo_chave: :email,
webhook: "https://meusite.com/webhook"
})
# Resposta:
# %{
# "status" => "success",
# "message" => "...",
# "internalreference" => "ref_xyz"
# }
```
#### Tipos de Chave PIX
| Atom | Valor | Descrição |
|------|-------|-----------|
| `:cpf` | `CPF` | CPF do destinatário |
| `:cnpj` | `CNPJ` | CNPJ do destinatário |
| `:phone` | `PHONE` | Telefone do destinatário |
| `:email` | `EMAIL` | Email do destinatário |
| `:random` | `RANDOM` | Chave aleatória |
### Consultar Saldo
```elixir
{:ok, balance} = NXGate.get_balance(client)
# Resposta:
# %{
# "balance" => 1000.00,
# "blocked" => 50.00,
# "available" => 950.00
# }
```
### Consultar Transação
```elixir
# Cash-in
{:ok, tx} = NXGate.get_transaction(client, :cash_in, "id_transacao")
# Cash-out
{:ok, tx} = NXGate.get_transaction(client, :cash_out, "id_transacao")
# Resposta:
# %{
# "idTransaction" => "...",
# "status" => "PAID",
# "amount" => 100.0,
# "paidAt" => "2026-01-15T10:30:00Z",
# "endToEnd" => "..."
# }
```
## Webhooks
O módulo `NXGate.Webhook` facilita o processamento de notificações recebidas da NXGATE.
### Configuração no Phoenix
```elixir
defmodule MyAppWeb.WebhookController do
use MyAppWeb, :controller
def nxgate(conn, params) do
case NXGate.Webhook.parse(params) do
{:ok, %{type: "QR_CODE_COPY_AND_PASTE_PAID", category: :cash_in} = event} ->
# Pagamento PIX recebido
IO.inspect(event.data.amount, label: "Valor recebido")
IO.inspect(event.data.tx_id, label: "ID da transação")
json(conn, %{ok: true})
{:ok, %{type: "QR_CODE_COPY_AND_PASTE_REFUNDED"} = event} ->
# Pagamento estornado
handle_refund(event)
json(conn, %{ok: true})
{:ok, %{type: "PIX_CASHOUT_SUCCESS"} = event} ->
# Saque realizado com sucesso
IO.inspect(event.data.id_transaction, label: "Saque confirmado")
json(conn, %{ok: true})
{:ok, %{type: "PIX_CASHOUT_ERROR"} = event} ->
# Erro no saque
handle_cashout_error(event)
json(conn, %{ok: true})
{:ok, %{type: "PIX_CASHOUT_REFUNDED"} = event} ->
# Saque estornado
handle_cashout_refund(event)
json(conn, %{ok: true})
{:error, reason} ->
Logger.warning("Webhook NXGATE inválido: #{inspect(reason)}")
conn |> put_status(400) |> json(%{error: "invalid payload"})
end
end
end
```
### Tipos de Evento
#### Cash-in (PIX Recebido)
| Tipo | Descrição |
|------|-----------|
| `QR_CODE_COPY_AND_PASTE_PAID` | QR Code pago com sucesso |
| `QR_CODE_COPY_AND_PASTE_REFUNDED` | QR Code estornado |
#### Cash-out (PIX Enviado)
| Tipo | Descrição |
|------|-----------|
| `PIX_CASHOUT_SUCCESS` | Saque realizado com sucesso |
| `PIX_CASHOUT_ERROR` | Erro no saque |
| `PIX_CASHOUT_REFUNDED` | Saque estornado |
### Funções Auxiliares
```elixir
# Verificar se é evento de cash-in
NXGate.Webhook.cash_in?("QR_CODE_COPY_AND_PASTE_PAID") # true
# Verificar se é evento de cash-out
NXGate.Webhook.cash_out?("PIX_CASHOUT_SUCCESS") # true
# Listar todos os tipos conhecidos
NXGate.Webhook.known_types()
```
## Assinatura HMAC
Quando o `hmac_secret` é configurado, todas as requisições incluem automaticamente os seguintes headers:
| Header | Descrição |
|--------|-----------|
| `X-Client-ID` | Identificador do client |
| `X-HMAC-Signature` | Assinatura HMAC-SHA256 codificada em Base64 |
| `X-HMAC-Timestamp` | Timestamp ISO 8601 da requisição |
| `X-HMAC-Nonce` | String única por requisição |
A string de assinatura é composta por:
```
METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY
```
## Tratamento de Erros
Todas as funções retornam `{:ok, result}` ou `{:error, %NXGate.Error{}}`.
```elixir
case NXGate.pix_generate(client, params) do
{:ok, charge} ->
# Sucesso
IO.puts("QR Code: #{charge["paymentCode"]}")
{:error, %NXGate.Error{reason: :validation_error} = err} ->
# Erro de validação dos parâmetros
IO.puts("Parâmetros inválidos: #{err.message}")
{:error, %NXGate.Error{reason: :auth_error}} ->
# Erro de autenticação
IO.puts("Verifique suas credenciais")
{:error, %NXGate.Error{reason: :api_error, status_code: code} = err} ->
# Erro retornado pela API
IO.puts("Erro HTTP #{code}: #{err.message}")
{:error, %NXGate.Error{reason: :connection_error}} ->
# Erro de rede/conexão
IO.puts("Verifique sua conexão")
{:error, %NXGate.Error{reason: :timeout}} ->
# Timeout na requisição
IO.puts("A requisição expirou")
{:error, %NXGate.Error{reason: :max_retries}} ->
# Serviço indisponível após retentativas
IO.puts("Serviço temporariamente indisponível")
end
```
### Tipos de Erro
| Reason | Descrição |
|--------|-----------|
| `:validation_error` | Parâmetros de entrada inválidos |
| `:auth_error` | Falha na autenticação OAuth2 |
| `:api_error` | Erro retornado pela API NXGATE |
| `:connection_error` | Erro de rede/conexão |
| `:timeout` | Timeout na requisição |
| `:max_retries` | Máximo de retentativas atingido (503) |
## Retry Automático
O SDK realiza retry automático com backoff exponencial quando recebe HTTP 503 (Service Unavailable):
- **1a tentativa**: aguarda 1 segundo
- **2a tentativa**: aguarda 2 segundos
- Após esgotar as tentativas, retorna `{:error, %NXGate.Error{reason: :max_retries}}`
O número máximo de retentativas pode ser configurado via opção `:max_retries`.
## Gerenciamento de Token
O token OAuth2 é gerenciado automaticamente pelo `NXGate.TokenManager`:
- O token é obtido automaticamente ao iniciar o client
- Renovação automática 5 minutos antes da expiração
- Em caso de falha na renovação, nova tentativa após 5 segundos
- Forçar renovação manualmente: `NXGate.refresh_token(client)`
## Desenvolvimento
```bash
# Clonar o repositório
git clone https://github.com/nxgate/sdk-elixir.git
cd sdk-elixir
# Instalar dependências
mix deps.get
# Executar testes
mix test
# Verificar formatação
mix format --check-formatted
```
## Licença
Este projeto está licenciado sob a licença MIT - veja o arquivo [LICENSE](LICENSE) para detalhes.