docs
Guias de Integração

Autenticação

Visão Geral

A API FyPay utiliza autenticação baseada em OAuth 2.0 (clientId + clientSecret) combinada com certificado de cliente X.509 (mTLS). O certificado é apresentado no handshake TLS da chamada de geração de token; as demais rotas usam apenas o token Bearer.

Por que mTLS?

O mTLS (mutual TLS) oferece segurança superior comparado a credenciais simples:

  • Autenticação mútua: Tanto o cliente quanto o servidor se autenticam no handshake TLS
  • Não-repúdio: Certificados vinculados à conta garantem rastreabilidade
  • Proteção contra roubo de credenciais: Mesmo com clientId/clientSecret vazados, o atacante precisaria do certificado e da chave privada

Pré-requisitos

Antes de iniciar, você precisará:

Certificado em formato PEM, chave RSA 2048, válido por 2 anos e vinculado à sua conta, junto com a respectiva chave privada. Nos exemplos abaixo, os arquivos se chamam client.crt (certificado) e client.key (chave privada).

Gere seu clientId e clientSecret no Painel FyPay, em Configurações → Credenciais. O clientSecret é exibido uma única vez: copie-o e guarde-o em um cofre de segredos.

Configure seu cliente HTTP para apresentar o certificado e a chave privada no handshake TLS (mecanismo nativo de TLS da sua linguagem).

O certificado deve estar vinculado à sua conta. Certificados não vinculados, ou vinculados a outra conta, são rejeitados mesmo que tecnicamente válidos.

O certificado é entregue apenas em PEM; não há arquivo .pfx. Se sua stack exigir PKCS#12 (por exemplo, alguns cenários Windows/.NET), converta localmente:

openssl pkcs12 -export -in client.crt -inkey client.key -out client.pfx

Endpoint de Autenticação

POST /api/auth/token

Gera um token JWT de acesso. O tempo de vida do token, em segundos, vem no campo expires_in da resposta.

Esta é a única rota que exige o certificado de cliente. Ele deve ser apresentado no handshake TLS (mTLS), e não em um header HTTP.

Request

curl -X POST https://api.fypay.me/api/auth/token \
  --cert client.crt \
  --key client.key \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "6967c0a0-3566-48d0-8c13-717038b52279",
    "clientSecret": "seu-client-secret"
  }'
CampoTipoObrigatórioDescrição
clientIdstring (UUID v4)SimIdentificador da credencial gerada no painel
clientSecretstringSimSegredo da credencial, exibido uma única vez no painel

Response (201 Created)

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 600,
  "scope": "email profile"
}
CampoDescrição
access_tokenToken JWT a ser enviado em Authorization: Bearer <token>
token_typeSempre Bearer
expires_inTempo de vida do token em segundos. Use este valor para agendar a renovação
scopeEscopo do token

Exemplo Prático: Node.js

Instalação

npm install undici

Código Completo

const fs = require('fs');
const { Agent, request } = require('undici');

const mtlsAgent = new Agent({
  connect: {
    cert: fs.readFileSync('./client.crt'),
    key: fs.readFileSync('./client.key'),
  },
});

async function getToken() {
  const { statusCode, body } = await request('https://api.fypay.me/api/auth/token', {
    method: 'POST',
    dispatcher: mtlsAgent,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      clientId: process.env.FYPAY_CLIENT_ID,
      clientSecret: process.env.FYPAY_CLIENT_SECRET,
    }),
  });

  const data = await body.json();

  if (statusCode !== 201) {
    throw new Error(`Erro ao obter token: ${data.code} ${data.message}`);
  }

  console.log('Token obtido com sucesso!');
  console.log('Expira em:', data.expires_in, 'segundos');

  return data;
}

getToken().catch(console.error);

Usando o módulo nativo https, o equivalente é new https.Agent({ cert, key }) passado na opção agent da requisição.

Exemplo Prático: Python

Instalação

pip install requests

Código Completo

import os
import requests

url = 'https://api.fypay.me/api/auth/token'
payload = {
    'clientId': os.environ.get('FYPAY_CLIENT_ID'),
    'clientSecret': os.environ.get('FYPAY_CLIENT_SECRET'),
}

try:
    response = requests.post(
        url,
        json=payload,
        cert=('client.crt', 'client.key'),
        timeout=30,
    )
    response.raise_for_status()

    data = response.json()

    print('Token obtido com sucesso!')
    print(f"Expira em: {data['expires_in']} segundos")

except requests.exceptions.RequestException as e:
    print(f'Erro ao obter token: {e}')
    if e.response is not None:
        print(f'Resposta: {e.response.text}')

Outras linguagens

Em todas elas, o certificado e a chave são carregados pelo mecanismo nativo de TLS do cliente HTTP:

LinguagemMecanismo
PHP (cURL)CURLOPT_SSLCERT = client.crt e CURLOPT_SSLKEY = client.key
C# (.NET)HttpClientHandler.ClientCertificates.Add(X509Certificate2.CreateFromPemFile("client.crt", "client.key"))
Gotls.LoadX509KeyPair("client.crt", "client.key") em tls.Config.Certificates
JavaConverta para PKCS#12 com openssl pkcs12 -export e carregue como KeyStore no SSLContext

Utilizando o Token

Após obter o token, inclua-o no header Authorization das demais requisições. Essas rotas não exigem certificado de cliente:

curl -X GET https://api.fypay.me/api/balance \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Renovação de Token

Não assuma uma duração fixa para o token. Leia expires_in em cada resposta e renove o token antes que ele expire.

Estratégia Recomendada

const fs = require('fs');
const { Agent, request } = require('undici');

class TokenManager {
  constructor(clientId, clientSecret, certPath, keyPath) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.agent = new Agent({
      connect: {
        cert: fs.readFileSync(certPath),
        key: fs.readFileSync(keyPath),
      },
    });
    this.token = null;
    this.expiresAt = 0;
  }

  async getValidToken() {
    const safetyMarginMs = 30000;
    if (this.token && Date.now() < this.expiresAt - safetyMarginMs) {
      return this.token;
    }
    return this.refreshToken();
  }

  async refreshToken() {
    const { statusCode, body } = await request('https://api.fypay.me/api/auth/token', {
      method: 'POST',
      dispatcher: this.agent,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        clientId: this.clientId,
        clientSecret: this.clientSecret,
      }),
    });

    const data = await body.json();
    if (statusCode !== 201) {
      throw new Error(`${data.code}: ${data.message}`);
    }

    this.token = data.access_token;
    this.expiresAt = Date.now() + data.expires_in * 1000;
    return this.token;
  }
}

const tokenManager = new TokenManager(
  process.env.FYPAY_CLIENT_ID,
  process.env.FYPAY_CLIENT_SECRET,
  './client.crt',
  './client.key'
);

const token = await tokenManager.getValidToken();

Validação do Certificado

O sistema realiza as seguintes validações no certificado apresentado no handshake:

  1. Handshake mTLS: O certificado deve ter sido emitido pela FyPay e você deve possuir a chave privada correspondente
  2. Validade: O certificado precisa estar dentro do período de validade (2 anos a partir da emissão)
  3. Registro: O fingerprint SHA-256 do certificado deve estar registrado e ativo
  4. Vinculação à conta: O certificado deve estar vinculado à mesma conta da credencial (clientId)

Certificados não vinculados ou vinculados a outra conta serão rejeitados, mesmo que tecnicamente válidos.

Respostas de erro

Todos os erros retornados por POST /api/auth/token seguem o mesmo formato:

{
  "statusCode": 400,
  "timestamp": "2026-04-24T16:59:25.577Z",
  "path": "/api/auth/token",
  "method": "POST",
  "code": "PUB_CERT_HEADER_MISSING",
  "message": "Client certificate header is missing",
  "userMessage": "Client certificate is required.",
  "details": {
    "hint": "Present your client certificate during the TLS handshake (e.g. curl --cert client.crt --key client.key)."
  },
  "errorId": "81421e7a5afbd9cf25212f51dac08da1"
}
  • code: identificador estável legível por máquina. Faça o tratamento com base neste campo.
  • message: descrição técnica resumida (em inglês, voltada para logs).
  • userMessage: descrição voltada ao usuário final.
  • details.hint: orientação prática para desenvolvedores integrando a API.
  • errorId: identificador único da requisição. Informe-o ao contatar o suporte.

Referência de códigos de erro

HTTPCódigoSignificado
400PUB_CERT_HEADER_MISSINGNenhum certificado de cliente foi apresentado no handshake TLS
400PUB_CERT_MALFORMED_PEMCertificado em formato PEM inválido
400PUB_REQUEST_BODY_INVALIDCorpo JSON inválido
401PUB_CERT_NOT_VERIFIEDO certificado apresentado no handshake foi rejeitado
401PUB_CERT_NOT_YET_VALIDA data de início de validade do certificado está no futuro
401PUB_CERT_EXPIREDO certificado expirou
401PUB_CERT_NOT_REGISTEREDO certificado não está registrado ou foi revogado
401PUB_INVALID_CREDENTIALSclientId ou clientSecret incorretos
403PUB_CERT_NOT_AUTHORIZED_FOR_ACCOUNTO certificado não está vinculado à conta do clientId
403PUB_ACCOUNT_INACTIVECredenciais corretas, mas a conta não está ativa
502PUB_AUTH_UPSTREAM_ERRORErro inesperado no serviço de autenticação
503PUB_AUTH_UPSTREAM_UNAVAILABLEServiço de autenticação temporariamente indisponível

PUB_CERT_HEADER_MISSING (400)

Nenhum certificado de cliente foi apresentado no handshake TLS.

Como resolver: configure seu cliente HTTP para enviar o certificado e a chave privada na conexão TLS (por exemplo, curl --cert client.crt --key client.key). Se houver um proxy ou gateway entre sua aplicação e a FyPay que termine o TLS, a conexão com api.fypay.me precisa partir de um cliente que apresente o certificado.

PUB_CERT_MALFORMED_PEM (400)

Certificado em formato PEM inválido.

PUB_REQUEST_BODY_INVALID (400)

O corpo JSON não satisfez o esquema (campo ausente, tipo errado, ou clientId não é um UUID v4). Verifique details.violations para identificar o(s) campo(s) com problema e envie Content-Type: application/json.

PUB_CERT_NOT_VERIFIED (401)

O certificado apresentado no handshake TLS foi rejeitado.

Como resolver: use o certificado emitido pela FyPay para sua conta e a chave privada correspondente. Certificados de outras autoridades certificadoras não são aceitos.

PUB_CERT_NOT_YET_VALID (401)

A data notBefore do certificado está no futuro.

Como resolver: sincronize o relógio do sistema via NTP. Se a data realmente estiver no futuro, aguarde ou solicite um novo certificado.

PUB_CERT_EXPIRED (401)

A data notAfter do certificado expirou. Os certificados valem 2 anos a partir da emissão.

Como resolver: solicite um novo certificado ao suporte e substitua-o na sua integração. Planeje a troca antes do vencimento.

PUB_CERT_NOT_REGISTERED (401)

O fingerprint SHA-256 do certificado não está no registro: ele nunca foi registrado ou foi revogado.

Como resolver: calcule o fingerprint local com openssl x509 -noout -fingerprint -sha256 -in client.crt e informe-o ao suporte junto com o errorId da resposta.

PUB_INVALID_CREDENTIALS (401)

O clientId não é reconhecido, ou o clientSecret não corresponde. A plataforma deliberadamente não diferencia esses casos (resistência a enumeração).

Como resolver: verifique o clientId e o clientSecret. Como o clientSecret só é exibido uma vez, se você o perdeu gere uma nova credencial em Configurações → Credenciais.

PUB_CERT_NOT_AUTHORIZED_FOR_ACCOUNT (403)

O certificado é válido e registrado, mas não está vinculado à conta que possui o clientId informado.

Como resolver: verifique se você está usando o par certificado/clientId da mesma conta.

PUB_ACCOUNT_INACTIVE (403)

As credenciais estão corretas, mas a conta não está ativa. Novas tentativas não terão sucesso.

Como resolver: contate o suporte com o errorId da resposta.

PUB_AUTH_UPSTREAM_UNAVAILABLE (503)

O serviço de autenticação está temporariamente inacessível.

Como resolver: tente novamente com backoff exponencial (por exemplo, 1s, 2s, 4s). Se o problema persistir por mais de um minuto, contate o suporte.

PUB_AUTH_UPSTREAM_ERROR (502)

O serviço de autenticação retornou um erro inesperado.

Como resolver: contate o suporte com o errorId.

Resolução de problemas

"Recebo PUB_CERT_HEADER_MISSING mesmo enviando o certificado"

O certificado precisa ir no handshake TLS, não em um header HTTP. Confirme que o cliente HTTP está configurado com certificado e chave (por exemplo, --cert/--key no curl, cert=(crt, key) no Python requests) e que nenhum proxy intermediário termina o TLS antes de chegar à FyPay.

"Recebo ocasionalmente PUB_CERT_NOT_YET_VALID"

O relógio do sistema está atrás da data notBefore do certificado. Configure o NTP e garanta que o relógio do host esteja com poucos segundos de diferença do UTC. Contêineres podem herdar a deriva de relógio do host; verifique com date -u dentro do contêiner.

"Recebo PUB_CERT_NOT_REGISTERED após trocar o certificado"

Calcule o fingerprint do novo certificado (openssl x509 -noout -fingerprint -sha256 -in client.crt) e confirme com o suporte que ele está registrado e vinculado à sua conta antes de fazer a transição.

Boas Práticas

Próximos Passos

Nesta página