# NinePay Developers Documentation > Documentação da API NinePay: cobranças recorrentes e avulsas por cartão, PIX e boleto. This file holds the full Markdown of every page, in navigation order. Index with links: https://docs.ninepay.com.br/llms.txt Per-section files: https://docs.ninepay.com.br/docs/llms-full.txt, https://docs.ninepay.com.br/reference/llms-full.txt, https://docs.ninepay.com.br/recipes/llms-full.txt --- # NinePay Developers Source: https://docs.ninepay.com.br/

Guias / Guides

Entenda os fluxos de cobrança, autenticação, sandbox e webhooks, em português e inglês.

Clique aqui

Referência / Reference

Todos os endpoints da API, com parâmetros, respostas, exemplos e Try It para testar no sandbox.

Clique aqui

Recipes

Passo a passo com código pronto: cobrança no PIX e no cartão, assinaturas, webhooks e checkout.

Clique aqui
--- # Comece aqui Source: https://docs.ninepay.com.br/docs/comece-aqui O caminho para a primeira integração com a API NinePay, do acesso ao sandbox à primeira cobrança Estas páginas cobrem o que você precisa antes de criar a primeira cobrança: como a API funciona, como autenticar, onde testar e como importar a API no seu cliente HTTP. Guias e referência: o que encontrar em cada um. Ambientes, headers, respostas, erros, datas e hash de cartões. Chaves de API, prefixos por ambiente, troca e ciclo de vida. A API e o dashboard do sandbox. Final 0 aprova; finais 1 a 9 recusam. Paginação, ordenação e filtros das listagens. Para testar os endpoints sem escrever código, baixe a API para o Postman ou o Insomnia em [Collections](/docs/collections). --- # Introdução aos Guias Source: https://docs.ninepay.com.br/docs/introducao Bem-vindo aos guias de integração da NinePay. Os conteúdos aqui presentes te ajudarão a entender melhor alguns fluxos e saber exatamente como interagir com a API da NinePay. Boa leitura! > 👍 Dica > > Se precisar de suporte para arquitetar algum fluxo, basta [acionar nossa equipe](mailto:ti@ninepay.com.br) que teremos prazer em ajudar! # Guias vs Referência ### Guias 📖 Explicam os fluxos macros usando termos de negócios e técnicos. A leitura dos guias te ajuda a saber exatamente quais endpoints e outros recursos você precisará usar para suas necessidades. ### Referência 🔩 A documentação dos endpoints, seus parâmetros e respostas, descritos no padrão OpenAPI, e apresentados em uma interface intuitiva e com exemplos de implementação em várias linguagens. --- # Introdução à API Source: https://docs.ninepay.com.br/docs/introducao-api Ambientes, autenticação, formato das respostas e dos erros, paginação e hash de cartões Bem-vindo à referência da **API NinePay**. É através desta API que você integra o seu sistema ao nosso. Nossa API é [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) e todas as suas respostas são em [JSON](https://www.json.org/), no endpoint base: ```text Produção https://payments.cefis.com.br/api/v1/ ``` A lista completa de endpoints, com parâmetros, respostas e exemplos em várias linguagens, está na [Referência da API](/reference/criar-novo-pagamento). ## Ambientes Além do ambiente de **produção** temos disponível o ambiente **sandbox** para a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da URL: ```text Sandbox https://api.sandbox.cefispay.com.br/api/v1/ ``` Veja como pedir acesso em [Ambiente de Testes](/docs/sandbox). ## Autenticação Todas as requisições que requerem autenticação devem informar no header `Authorization` o token de API enviado pela nossa equipe, observando sempre o ambiente desejado, no seguinte formato: ```http Authorization: Bearer ``` > ❗️ O token é segredo > > Use o token só no seu back-end. Nunca o coloque em código que roda no navegador ou no app do cliente. Prefixos das chaves (`sk_prod_` e `sk_hmlg_`), criação, rotação e expiração estão em [Autenticação](/docs/autenticacao). ## Headers Recomendamos que os cabeçalhos das requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API: - Todas as requisições devem conter o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente. - Todas as requisições devem conter o header `Accept: application/json`, a menos que o endpoint específico defina diferente. ## Respostas - Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ "data": { ... } }`. - As criações respondem **201**. `POST /payments` responde `{ "customer", "payment", "invoice_url" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ "customer", "subscription" }`. - Valores sempre em centavos (inteiro): R$ 50,00 é `5000`. ## Erros Todo erro tem o mesmo formato: ```json { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": ["O campo email é obrigatório."] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } ``` `errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte. | `code` | HTTP | Quando | | --- | --- | --- | | `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido | | `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem | | `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa | | `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe | | `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe | | `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) | | `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) | | `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` | | `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado | | `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` | | `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` | ## Paginação As listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`). Para percorrer tudo, avance `page` até `meta.last_page`. ## Datas - Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição. - Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele. ## Identificadores Cada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto). ## Hash de cartões O hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente. Essa é a maneira mais segura de enviar os dados do cartão do seu cliente diretamente para a gente. Abaixo detalhamos como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente. > 🚧 Só no front-end > > Esse fluxo deve ser executado no front-end, seja ele web ou mobile. Os dados do cartão do cliente nunca devem ser enviados para o seu back-end; já o hash do cartão pode ser enviado para o back-end sem problemas. ### Gerando uma encryption key A encryption key é uma chave pública RSA usada para encriptar os dados do cartão. Essa chave só pode ser usada uma vez: sempre que precisar encriptar um novo cartão, gere uma nova encryption key. Para gerar uma encryption key, faça uma requisição `POST` para a rota `/encryption_keys` da API enviando como parâmetro o seu `merchant_id`. Ela não usa o token da API (nunca o coloque no front-end): o `merchant_id` identifica a sua conta, e um `merchant_id` que não existe responde 401. A requisição deverá ter esse formato: ```bash cURL curl -X POST https://payments.cefis.com.br/api/v1/encryption_keys \ -H 'Content-Type: application/json' \ -d '{"merchant_id":"SEU_MERCHANT_ID"}' ``` Como resposta você receberá um objeto JSON no seguinte formato: ```json { "id": 1111, "public_key": "-----BEGIN PUBLIC KEY-----\n ... \n-----END PUBLIC KEY-----\n", "ip_address": "000.000.000.000", "created_at": "2017-07-03" } ``` ### Encriptando os dados do cartão Para encriptar os dados do cartão, primeiro formate-os corretamente: crie uma string JSON contendo todos os dados do cartão no seguinte formato: ```json {"card_number":"1111111111111111","card_holder_name":"Jose Silva","card_expiration_date":"11/2021","card_cvv":"111"} ``` Em seguida faça a criptografia RSA dessa string utilizando a chave pública (`public_key`) gerada no passo anterior, com padding do tipo **PKCS1**. Após criptografar os dados, converta o resultado para base64. Você terá algo parecido com isto: ```text GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ== ``` O último passo é concatenar o `id` da sua encryption key (recebido na resposta da requisição anterior) com a string em base64 que representa os dados do cartão, da seguinte forma: ```text encryption_key_id + "_" + base64_encrypted_card ``` O resultado será o seu card hash: ```text 1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ== ``` Pronto: os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede e podem ser enviados para o seu back-end dar sequência ao fluxo (no campo `card_hash` do [`POST /payments`](/reference/criar-novo-pagamento)). > 📘 Validade do card hash > > Cada card hash pode ser usado apenas uma vez e vale por no máximo 30 minutos após a geração da encryption key. Caso precise usar o cartão mais de uma vez, faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém: você receberá um ID único que poderá ser usado em novas transações com o mesmo cartão. ## Suporte Dúvidas sobre a integração: [ti@ninepay.com.br](mailto:ti@ninepay.com.br). Termos de uso: [bit.ly/cefispay-contrato](https://bit.ly/cefispay-contrato). --- # Autenticação Source: https://docs.ninepay.com.br/docs/autenticacao Toda chamada à API da NinePay é autenticada por uma chave de API. Você gera as chaves no dashboard, e elas têm um ciclo de vida automático: as que ficam sem uso são desabilitadas e, depois, expiradas. ## Como autenticar Envie a chave no header `Authorization`, no formato `Bearer`: ```bash curl https://payments.cefis.com.br/api/v1/customers \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" ``` Sem a chave, ou com uma chave desabilitada, expirada ou excluída, a API responde 401. ## Formato | Prefixo | Ambiente | | --- | --- | | `sk_prod_…` | Produção | | `sk_hmlg_…` | [Sandbox](/docs/sandbox) | As chaves criadas antes da nova tela de Integrações têm o formato antigo, sem prefixo, e continuam valendo do mesmo jeito. Elas entram no ciclo de vida descrito abaixo como qualquer outra. ## Gerando uma chave 1. No dashboard, abra **Integrações** e vá na aba **Chaves de API**. 2. Crie a chave com um nome que diga onde ela é usada (por exemplo, "Site" ou "ERP"). 3. Se quiser, escolha uma data e hora de expiração. 4. Copie a chave e guarde num lugar seguro. > 🚧 A chave aparece uma vez só > > Depois de fechar a janela, nem você nem a NinePay conseguem ver a chave de novo, porque guardamos só um hash dela. Se perder, gere outra. Cada empresa pode ter até 10 chaves. ## Trocando uma chave Troque sem derrubar a integração: 1. Gere uma chave nova. 2. Atualize o seu sistema para usar a nova. 3. Desabilite a chave antiga no dashboard. 4. Confira que nada quebrou. Se algo quebrar, reabilite a antiga e volte a investigar. 5. Com tudo funcionando, exclua a chave antiga. ## Ciclo de vida automático Uma chave sem uso passa por estas situações: | Situação | Quando | Dá para voltar? | | --- | --- | --- | | Habilitada | Em uso, ou sem uso há menos de 3 meses | | | Desabilitada | 3 meses sem uso | Sim, reabilite no dashboard | | Expirada | 6 meses sem uso | Não, gere uma chave nova | - O dono da conta recebe um e-mail 7 dias antes de a chave ser desabilitada por falta de uso e outro no dia. A expiração, seja aos 6 meses sem uso, seja na data que você escolheu, não tem aviso. - Cada chamada autenticada com a chave conta como uso e reinicia a contagem. - Se você definiu uma data de expiração, a chave para de funcionar nela, independentemente de uso. ## Boas práticas - Nunca coloque a chave em código que roda no navegador nem em aplicativo móvel: quem baixar o app ou abrir o código-fonte da página consegue lê-la. Chame a API a partir do seu servidor. - Guarde a chave em variável de ambiente (como `NINEPAY_TOKEN` nos exemplos), não no código nem no repositório. - Use uma chave por integração. Assim você desabilita ou troca uma sem afetar as outras, e vê quais estão em uso. - Se suspeitar que uma chave vazou, desabilite-a e gere outra. --- # Ambiente de Testes Source: https://docs.ninepay.com.br/docs/sandbox Para facilitar o desenvolvimento das integrações com a plataforma NinePay, nós oferecemos um ambiente **sandbox** completo, sendo uma API de testes e uma Plataforma Web de testes que simulam os comportamentos do ambiente de produção. > 📘 Acesso > > Quando você recebe acesso ao ambiente de sandbox, nossa equipe te disponibiliza as `chaves` para autenticação na API (no sandbox elas começam com `sk_hmlg_`; veja [Autenticação](/docs/autenticacao)), e o `usuário` e `senha` pra acesso a Dashboard. Se você ainda não tem essas informações, [solicite a nossa equipe](mailto:ti@ninepay.com.br). # API Sandbox A API Sandbox é por onde todas as chamadas de API devem sem realizadas durante o desenvolvimento e testes da integração. Os detalhes de acesso a esta API estão documentados na [Introdução à API](/docs/introducao-api). ![API Sample](/guides-assets/api-sandbox.png) # Dashboard Sandbox A Dashboard Sandbox é a plataforma web conectada a API Sandbox para a realização dos testes. Elá apresenta a visualização gráfica via interface das informações e transações da conta, além de permitir toda a manipulação da conta, como criar pagamentos, links, planos, etc. ![Dashboard Sample](/guides-assets/dashboard-sandbox.png) --- # Cartões para Testes Source: https://docs.ninepay.com.br/docs/test-cards Final 0 aprova, finais 1 a 9 recusam (só no sandbox) Ao realizar testes de pagamento nas páginas de checkout e/ou via endpoints da API, o número do cartão de crédito que você usa define se o pagamento será aprovado ou não. Isso permite que você teste casos de autorização e de não autorização do cartão. > 🚧 Só no sandbox > > Estes cartões valem apenas no [ambiente de testes](/docs/sandbox). Em produção a cobrança vai para a adquirente de verdade, que recusa esses números. ## Número do cartão (PAN) O **último dígito** do cartão define a autorização ou a recusa: | Resultado | Final do cartão | | --- | --- | | ✅ Pagamento aprovado | **0** | | ❌ Pagamento recusado | **1 a 9** | | Bandeira | Número | | --- | --- | | Mastercard | `5358 7326 8161 8240` | | Visa | `4293 5672 7563 6450` | | Elo | `4514 1617 3512 5180` | | Bandeira | Número | | --- | --- | | Mastercard | `5168 7948 3166 8921` | | Visa | `4594 3598 6479 9162` | | Elo | `5041 7541 7176 2158` | ## Validade Para a **validade** do cartão de crédito, sempre use um mês e ano futuros, por exemplo: `05/2030`. ## CVV Para o **CVV**, qualquer número de 3 dígitos é aceito, por exemplo: `123`. --- # Listagens e Paginação Source: https://docs.ninepay.com.br/docs/listagens As rotas de listagem devolvem os itens da sua empresa, do mais novo para o mais antigo, em páginas. ## Paginação | Parâmetro | Padrão | O que faz | | --- | --- | --- | | `page` | `1` | A página, a partir de 1 | | `limit` | `10` | Itens por página, de 1 a 100 | | `sort` | `created_at` | O campo da ordenação | | `direction` | `desc` | `asc` ou `desc` | A resposta traz os itens em `data`, os endereços das páginas em `links` e os totais em `meta`: ```json { "data": [{ "id": 315968, "name": "Maria Silva" }], "links": { "first": "https://payments.cefis.com.br/api/v1/customers?page=1", "last": "https://payments.cefis.com.br/api/v1/customers?page=4", "prev": null, "next": "https://payments.cefis.com.br/api/v1/customers?page=2" }, "meta": { "current_page": 1, "last_page": 4, "per_page": 10, "total": 37 } } ``` Para ler tudo, avance `page` até `meta.last_page`. > 📘 Planos > > `GET /plans` só pagina quando você manda `page` ou `limit`. Sem nenhum dos dois, devolve todos os planos de uma vez, sem `links` e `meta`. ## Filtros Os filtros que recebem lista vão repetidos na query: `status[]=active&status[]=overdue`. Os intervalos de data (`AAAA-MM-DD`) só valem com o início **e** o fim. ### `GET /customers` | Filtro | O que faz | | --- | --- | | `query` | Busca no nome, e-mail, CPF ou CNPJ | | `status[]` | `1` (ativo) ou `2` (inativo) | ### `GET /subscriptions` | Filtro | O que faz | | --- | --- | | `customer_id` | As assinaturas de um cliente | | `plan_id` | As assinaturas de um plano | | `status[]` | `new`, `trial`, `active`, `overdue`, `paused`, `canceled` ou `expired` | | `query` | Busca no nome, e-mail, CPF ou CNPJ do cliente, no nome do plano e nos valores do `custom_data` | | `created_at_start` e `created_at_end` | Criadas no intervalo | | `end_at_start` e `end_at_end` | Que terminam no intervalo | Aceita também `sort=customer_name`, para ordenar pelo nome do cliente. ### `GET /payments` | Filtro | O que faz | | --- | --- | | `subscription_id` | As faturas de uma assinatura | | `payment_link_id` | Os pagamentos de um link de pagamento | | `show_only_individual` | `true`: só as cobranças avulsas (sem assinatura, link ou carnê) | | `method[]` | `credit`, `debit`, `pix` ou `billet` | | `status[]` | `unpaid`, `paid`, `unauthorized`, `canceled` ou `reversed` | | `query` | Busca no nome, e-mail, CPF ou CNPJ do cliente, no valor e nos dígitos do cartão | | `created_at_start` e `created_at_end` | Criados no intervalo | | `due_date_start` e `due_date_end` | Que vencem no intervalo | | `paid_at_start` e `paid_at_end` | Pagos no intervalo | As validações de cartão e o início de período de teste não aparecem na listagem de pagamentos. --- # MCP Server (IA) Source: https://docs.ninepay.com.br/docs/mcp-server Use a documentação da NinePay com assistentes de IA # Use a documentação da NinePay com assistentes de IA O MCP Server da NinePay deixa assistentes de IA como Claude, ChatGPT, Cursor e VS Code consultarem esta documentação direto, com as informações sempre atualizadas: endpoints, parâmetros, exemplos e guias. Com ele, o assistente responde perguntas sobre a integração e gera código com base na documentação oficial, sem você precisar copiar e colar páginas. > 📘 Só documentação > > O MCP Server é público e só lê a documentação. Ele não acessa a sua conta, não usa a sua chave de API e não faz chamadas à API da NinePay. ## Conecte seu assistente ao MCP Server O endereço do MCP Server é: ```text URL https://docs.ninepay.com.br/mcp ``` ### O que o assistente pode consultar | Ferramenta | Para que serve | |---|---| | `list-specs` | Lista as especificações OpenAPI disponíveis | | `list-endpoints` | Lista todos os endpoints da API | | `search-endpoints` | Busca endpoints por caminho, nome, parâmetro ou campo | | `get-endpoint` | Traz os detalhes de um endpoint: parâmetros, corpo, respostas e exemplos | | `search` | Busca nos guias e na referência | | `fetch` | Lê uma página da documentação em Markdown | ## Configure o MCP Server Em qualquer página da documentação, o menu ao lado de **Copiar página** tem atalhos para instalar no Cursor e no VS Code e para copiar a configuração. ### Cursor Adicione ao arquivo `~/.cursor/mcp.json`: ```json mcp.json { "mcpServers": { "ninepay-docs-mcp": { "url": "https://docs.ninepay.com.br/mcp" } } } ``` ### VS Code Adicione ao arquivo `.vscode/mcp.json` do projeto: ```json .vscode/mcp.json { "servers": { "ninepay-docs-mcp": { "type": "http", "url": "https://docs.ninepay.com.br/mcp" } } } ``` ### Claude Code ```bash Terminal claude mcp add --transport http ninepay-docs-mcp https://docs.ninepay.com.br/mcp ``` ### Claude Desktop, ChatGPT e outros Adicione um conector MCP remoto com a URL `https://docs.ninepay.com.br/mcp`. O caminho no menu muda conforme o aplicativo: consulte a documentação dele. ## Teste a configuração Depois de conectar, pergunte ao assistente algo como: - "Como criar uma assinatura recorrente na NinePay?" - "Quais campos são obrigatórios para criar um pagamento?" - "Mostre um exemplo em Node.js para cobrar no PIX no sandbox." Se a resposta citar endpoints e guias da NinePay, o MCP Server está funcionando. ## Consulte o arquivo LLMs.txt Para assistentes que não usam MCP, a documentação também está disponível em arquivos de texto pensados para IA: - [`/llms.txt`](/llms.txt): índice com todas as páginas. - [`/llms-full.txt`](/llms-full.txt): toda a documentação em um arquivo só. Toda página também tem uma versão em Markdown: troque o fim do endereço por `.md`. ## Reporte informações incorretas Assistentes de IA podem errar. Se encontrar uma resposta incorreta baseada nesta documentação, avise a nossa equipe em [ti@ninepay.com.br](mailto:ti@ninepay.com.br). ## Próximos passos - [Autenticação](/docs/autenticacao) - [Ambiente de testes](/docs/sandbox) - [Collections Postman e Insomnia](/docs/collections) --- # Collections Source: https://docs.ninepay.com.br/docs/collections Baixe a API pronta para importar no Postman ou no Insomnia. Todos os endpoints de **NinePay API** estão disponíveis em arquivos prontos para os clientes de API mais usados. Importe o arquivo, escolha o ambiente e informe sua credencial: cada requisição já vem com URL, parâmetros, corpo de exemplo e autenticação. Coleção (formato v2.1) e um ambiente por servidor. Um arquivo de exportação com todos os ambientes. ## O que vem nos arquivos - Uma pasta por grupo da Referência da API, na mesma ordem da barra lateral, e uma requisição por endpoint. - URLs montadas com a variável `baseUrl`, que muda conforme o ambiente escolhido (um por servidor da API). - Parâmetros documentados: os obrigatórios ativos e os opcionais desmarcados, prontos para ligar. - O mesmo corpo de exemplo que a documentação e o Try It mostram. - Autenticação configurada a partir dos esquemas de segurança da API, apontando para variáveis vazias. Nenhuma credencial vem nos arquivos. > 📘 Sempre atualizados > Os arquivos são gerados novamente a cada publicação desta documentação, a partir da mesma especificação OpenAPI da Referência. --- # Postman Source: https://docs.ninepay.com.br/docs/postman Importe a coleção da API no Postman, escolha o ambiente e faça a primeira requisição. Use a coleção do Postman para testar os endpoints da API antes de implementar as chamadas na sua aplicação. > 📘 Ao final deste guia > Você terá a coleção e um ambiente importados no Postman, sua credencial configurada e uma requisição executada. ## Quando usar - explorar os endpoints sem escrever código; - conferir autenticação, headers e URLs; - testar o corpo de uma requisição antes de levá-lo para a sua aplicação; - reproduzir um erro durante o desenvolvimento; - compartilhar requisições com o seu time. ## Antes de começar - Instale o [Postman](https://www.postman.com/downloads/) ou use a versão web. - Tenha uma credencial de acesso à API para o ambiente que vai testar. > ⚠️ Proteja sua credencial > Não publique sua credencial em workspaces públicos, capturas de tela, repositórios ou conversas. Use credenciais de teste sempre que a API oferecer um ambiente de testes. ## 1. Baixe os arquivos Baixe a coleção e o ambiente do servidor que vai usar. A coleção é a mesma para todos os ambientes: só o ambiente muda. - [NinePay API — coleção do Postman](https://docs.ninepay.com.br/postman/ninepay-api.postman_collection.json) - [NinePay API — ambiente Servidor de Testes (sandbox)](https://docs.ninepay.com.br/postman/ninepay-api.servidor-de-testes-sandbox.postman_environment.json) ## 2. Importe no Postman 1. No Postman, abra o workspace em que quer guardar a coleção e clique em **Import**. 2. Arraste a coleção e o ambiente para a janela ou clique em **files** para selecioná-los. Também dá para colar o endereço de um arquivo (clique com o botão direito no botão de download e copie o link). 3. Confirme em **Import**. A coleção aparece em **Collections** e o ambiente, em **Environments**. ## 3. Selecione o ambiente Cada ambiente define a variável `baseUrl` com a URL base de um servidor da API: | Ambiente | URL base (`baseUrl`) | | --- | --- | | Servidor de Testes (sandbox) | `https://api.sandbox.cefispay.com.br/api/v1` | No seletor de ambientes, no canto superior direito, escolha o ambiente importado. Sem um ambiente selecionado, as requisições usam o `baseUrl` da própria coleção (o primeiro servidor). ## 4. Informe sua credencial A autenticação já está configurada na aba **Authorization** da coleção e as requisições a herdam. Ela usa as variáveis abaixo, que vêm vazias: | Variável | Uso | Referência | | --- | --- | --- | | `token` | Token de acesso, enviado como `Authorization: Bearer ` | `{{token}}` | Em **Environments**, abra o ambiente selecionado, preencha o valor da variável e salve (**Ctrl+S** ou **Cmd+S**). As variáveis de credencial são do tipo **secret**: o valor fica mascarado na tela. ## 5. Envie uma requisição 1. Abra uma pasta da coleção e selecione um endpoint. 2. Confira a URL e a aba **Params**: as variáveis de path (`:id`) e os parâmetros opcionais, que vêm desmarcados. 3. Ajuste o corpo de exemplo na aba **Body**, quando houver. 4. Clique em **Send** e confira o status e o corpo da resposta. Uma resposta com status `2xx` indica sucesso. O código exato e o formato da resposta estão na página do endpoint, na Referência. ## Erros comuns | Problema | O que verificar | | --- | --- | | `401` ou `403` | Se a variável de credencial está preenchida no ambiente selecionado e se a credencial é do mesmo ambiente da URL. | | URL com `{{baseUrl}}` ou erro de URL inválida | Se há um ambiente selecionado no canto superior direito. | | `404` | Se as variáveis de path da aba **Params** foram preenchidas. | | `400` ou `422` | Se o corpo é um JSON válido e segue os campos descritos na página do endpoint. | ## Atualizações Os arquivos são gerados novamente a cada publicação desta documentação, a partir da mesma especificação OpenAPI da Referência. Para receber endpoints novos ou alterados, baixe a coleção de novo e importe-a. O Postman avisa que a coleção já existe e pergunta se deve substituí-la ou importar uma cópia. ## Próximos passos - [Consulte a Referência da API](/reference/criar-novo-pagamento) - [Use a coleção no Insomnia](/docs/insomnia) - [Veja todas as collections](/docs/collections) --- # Insomnia Source: https://docs.ninepay.com.br/docs/insomnia Importe a API no Insomnia com ambientes e autenticação prontos e faça a primeira requisição. Importe a API no Insomnia para ter todos os endpoints em uma coleção, organizados em pastas e com ambientes e autenticação prontos. > 📘 Ao final deste guia > Você terá a coleção importada no Insomnia, um ambiente selecionado, sua credencial configurada e uma requisição executada. ## Antes de começar - Instale o [Insomnia](https://insomnia.rest/download). - Tenha uma credencial de acesso à API para o ambiente que vai testar. > ⚠️ Proteja sua credencial > Não publique sua credencial em workspaces públicos, capturas de tela, repositórios ou conversas. Use credenciais de teste sempre que a API oferecer um ambiente de testes. ## 1. Baixe o arquivo Um único arquivo traz a coleção e todos os ambientes. - [NinePay API — arquivo do Insomnia](https://docs.ninepay.com.br/insomnia/ninepay-api.insomnia.json) ## 2. Importe no Insomnia 1. No Insomnia, abra o projeto em que quer guardar a coleção e clique em **Import**. 2. Escolha **File** e selecione o arquivo baixado, ou escolha **URL** e cole o endereço do arquivo (abaixo). 3. Clique em **Scan**, confira o resumo e confirme em **Import**. Uma coleção com o nome da API é criada, com uma pasta por grupo da Referência. Endereço para a importação por URL: ```text https://docs.ninepay.com.br/insomnia/ninepay-api.insomnia.json ``` > 📘 Alternativa: coleção do Postman > O Insomnia também importa a [coleção do Postman](/docs/postman) (formato v2.1). Use-a se o seu time já trabalha com ela. ## 3. Escolha o ambiente A coleção traz um ambiente base e um subambiente por servidor da API, cada um com a variável `baseUrl`: | Subambiente | URL base (`baseUrl`) | | --- | --- | | Servidor de Testes (sandbox) | `https://api.sandbox.cefispay.com.br/api/v1` | No seletor de ambientes, acima da lista de requisições, escolha o subambiente. Sem um subambiente, vale o ambiente base (o primeiro servidor). ## 4. Informe sua credencial Cada requisição já tem a autenticação configurada na aba **Auth**, usando as variáveis abaixo, que vêm vazias: | Variável | Uso | Referência | | --- | --- | --- | | `token` | Token de acesso, enviado como `Authorization: Bearer ` | `{{ _.token }}` | Abra **Manage Environments** (no seletor de ambientes), selecione o subambiente em uso e preencha o valor da variável. As alterações são salvas automaticamente. ## 5. Envie uma requisição 1. Abra uma pasta da coleção e selecione um endpoint. 2. Confira a URL e as abas **Params** (variáveis de path e parâmetros opcionais, que vêm desativados) e **Headers**. 3. Ajuste o corpo de exemplo na aba **Body**, quando houver. 4. Clique em **Send** e confira o status e o corpo da resposta. Uma resposta com status `2xx` indica sucesso. O código exato e o formato da resposta estão na página do endpoint, na Referência. ## Erros comuns | Problema | O que verificar | | --- | --- | | A importação não encontra requisições | Se o arquivo escolhido é o `.insomnia.json` desta página (ou a URL completa dele) e se ele foi baixado por inteiro. | | `401` ou `403` | Se a variável de credencial está preenchida no subambiente selecionado e se a credencial é do mesmo ambiente da URL. | | URL com `_.baseUrl` sem valor | Se há um subambiente selecionado ou se o ambiente base tem o `baseUrl` preenchido. | | `400` ou `422` | Se o corpo é um JSON válido e segue os campos descritos na página do endpoint. | ## Atualizações Os arquivos são gerados novamente a cada publicação desta documentação, a partir da mesma especificação OpenAPI da Referência. Para receber endpoints novos ou alterados, importe o arquivo de novo. Se quiser comparar com a versão anterior antes de substituir, importe em um projeto separado. ## Próximos passos - [Consulte a Referência da API](/reference/criar-novo-pagamento) - [Use a coleção no Postman](/docs/postman) - [Veja todas as collections](/docs/collections) --- # Cobranças Source: https://docs.ninepay.com.br/docs/cobrancas Cobranças avulsas e assinaturas recorrentes no cartão, PIX e boleto A NinePay cobra de duas formas: **pagamentos** avulsos (uma cobrança no cartão, PIX ou boleto) e **assinaturas**, que geram uma fatura a cada ciclo do plano. Cobrança em aberto, estorno e baixa manual. Pausar, reativar e dar desconto com cupons. Os campos de cada chamada estão na referência: [Criar novo pagamento](/reference/criar-novo-pagamento) e [Criar nova assinatura](/reference/criar-nova-assinatura). --- # Pagamentos - Estorno e Baixa Manual Source: https://docs.ninepay.com.br/docs/pagamentos ## Cobrança em aberto Para criar uma cobrança sem cobrar na hora, e deixar o cliente escolher como pagar no checkout embutido, use `open: true` com `allowed_methods`. O passo a passo está no [guia do checkout embutido](/docs/checkout-embutido). ## Estorno Estorna um pagamento pago no cartão ou no PIX e devolve o valor ao cliente: ```bash curl -X POST https://payments.cefis.com.br/api/v1/payment/reversal \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "payment": { "id": 1470491 } }' ``` Só pode ser estornado o pagamento: - com `status: "paid"`; - no cartão (`credit`), pago há menos de 350 dias, ou no PIX (`pix`), pago há menos de 90 dias; - com saldo suficiente na sua conta para a devolução. Boleto e baixa manual não têm estorno pela API. O campo `can_refund` do pagamento diz se ele pode ser estornado agora. Quando não pode, a API responde **422** `REFUND_NOT_ALLOWED`, com o motivo em `message` (já estornado, fora do prazo, saldo insuficiente, estorno já pedido...). ### Acompanhando o estorno O estorno do cartão costuma ser imediato: o pagamento passa a `reversed` e sai o webhook `payment.updated`. O do PIX pode ficar em processamento; consulte o pagamento com os reembolsos: ```bash curl "https://payments.cefis.com.br/api/v1/payments/1470491?with[]=refunds" \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" ``` Cada item de `refunds` tem `id`, `method`, `value` (centavos), `status` e as datas. O `status` vai de `new` e `processing` para `completed` (devolvido) ou `failed`. Enquanto um estorno está em `processing`, outro pedido para o mesmo pagamento é recusado. ## Baixa manual Marca como pago um pagamento que o cliente quitou por fora da NinePay (em dinheiro ou transferência, por exemplo): ```bash curl -X POST https://payments.cefis.com.br/api/v1/payment/1470491/write-off \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "value_type": "normal" }' ``` - `value_type`: `normal` (o padrão) mantém o valor do pagamento; `zero` dá a baixa com valor 0 (um pagamento perdoado, por exemplo). - Só vale para pagamentos `unpaid` ou `unauthorized`. Nos outros, a API responde **400** `INVALID_STATE_ERROR`. - O pagamento passa a `paid` com `method: "manual"`, e sai o webhook `payment.updated`. - Por ser `manual`, ele não tem estorno pela API. --- # Assinaturas - Pausa e Cupons Source: https://docs.ninepay.com.br/docs/assinaturas ## Pausar e reativar Pausar uma assinatura para de gerar faturas para ela até você reativar. As faturas que já existem continuam como estão. ```bash curl -X PUT https://payments.cefis.com.br/api/v1/subscription/pause \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "subscription": { "id": 205465 } }' ``` Para reativar, o mesmo corpo em `PUT /subscription/unpause`. A assinatura volta a `active` e, se a próxima cobrança venceu durante a pausa, ela passa a ser agora: a fatura sai na próxima rotina de cobrança. As duas respostas são `201` com `{ "subscription": { ... } }` e disparam o webhook `subscription.updated`. Só pausa a assinatura `active`, `overdue` ou `trial`, e só reativa a `paused`. Nos outros status, a API responde **400** `INVALID_STATE_ERROR`. Para encerrar uma assinatura, cancele com `POST /subscription/cancel`. ## Cupons Um cupom dá desconto nas faturas de uma assinatura (`type: "subscription"`) ou num link de pagamento (`type: "payment_link"`). ### Criando um cupom ```bash curl -X POST https://payments.cefis.com.br/api/v1/coupons \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "type": "subscription", "name": "Black Friday", "code": "BLACK20", "percentage": 20, "value": 0, "duration": 3, "max_usages": 100, "expiration_date": "2026-11-30" }' ``` | Campo | O que faz | | --- | --- | | `code` | O código que o cliente digita, até 50 caracteres | | `percentage` | O desconto em porcentagem do valor da fatura | | `value` | Um desconto fixo, em centavos, somado ao da porcentagem (`0` para só a porcentagem) | | `duration` | Em quantas faturas da assinatura o desconto vale (opcional; sem ele, em todas) | | `max_usages` | Quantas vezes, no total, o cupom pode ser usado (opcional) | | `expiration_date` | Até quando o cupom pode ser aplicado (opcional) | O desconto nunca passa do valor da fatura. Para desativar um cupom, `PUT /coupons/{id}` com `"status": "canceled"`. ### Aplicando numa assinatura Na criação da assinatura, mande os códigos em `coupon_codes`: ```json { "customer": { "id": 315968 }, "plan": { "id": 413 }, "method": "credit", "coupon_codes": ["BLACK20"] } ``` Numa assinatura que já existe, aplique pelo id do cupom; o desconto entra nas próximas faturas: ```bash curl -X POST https://payments.cefis.com.br/api/v1/subscriptions/205465/coupons \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "coupon_id": 77 }' ``` - `GET /subscriptions/{id}/coupons` lista os cupons da assinatura, com o `status` de cada um: `unused` (ainda não usado), `using` (já deu desconto e ainda restam faturas), `used` (acabou a duração) ou `canceled`. - `PUT /subscription/coupons/{id}/cancel` tira o cupom da assinatura (o `id` é o do cupom na assinatura, da listagem acima). As faturas que já tiveram desconto não mudam. Cada desconto aparece como um item negativo na fatura, `Cupom `. --- # Webhooks Source: https://docs.ninepay.com.br/docs/webhooks Os webhooks avisam o seu sistema quando um cliente, um pagamento ou uma assinatura muda. São eles que devem liberar ou suspender o acesso de quem paga, não o retorno do checkout no navegador. ## Configurando Cadastre o webhook no dashboard, em **Integrações → Webhooks**, ou pela API (`/v1/automations`): ```bash curl -X POST https://payments.cefis.com.br/api/v1/automations \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "type": "webhook", "name": "Meu sistema", "webhook_url": "https://meusistema.com.br/webhooks/ninepay", "triggers": ["payment.approved", "payment.updated", "subscription.updated"], "status": "active" }' ``` O `webhook_url` precisa ser um endereço público (`http` ou `https`, fora de redes internas); senão a API responde 422. Use `https`. Para conferir o endereço antes de esperar um evento de verdade, o formulário do webhook no dashboard tem um envio de teste para cada evento. São até 10 envios de teste por minuto; acima disso, a resposta é 429 e é só esperar um minuto. O limite vale também para o envio de teste pela API (`POST /v1/automations/test`). > 📘 Webhook com segredo só se edita pela tela > > A API continua criando, listando e editando webhooks sem segredo, mas responde 422 em dois casos: > > - **Webhook gerido em Integrações**, como um que já tem segredo (veja [Validando](#validando)): ele só se altera no dashboard, nem que seja só o nome ou o status. A API devolve o erro em `errors.webhook_url` e o webhook fica como estava. > - **Evento não aceito em `triggers`**: a API devolve o erro em `errors.triggers.N`, em que `N` é a posição do evento na lista, a partir de 0, e nada é gravado. Use os eventos da tabela de [Eventos](#eventos). > > ```json > { > "message": "Evento inválido.", > "errors": { > "triggers.1": ["Evento inválido."] > } > } > ``` ## Eventos | Evento | Quando | | --- | --- | | `customer.created` | Um cliente foi criado | | `customer.updated` | Um cliente foi alterado | | `payment.created` | Uma cobrança ou fatura foi criada | | `payment.updated` | Um pagamento mudou (pago, recusado, cancelado, estornado, baixado...) | | `payment.approved` | Um pagamento foi aprovado | | `subscription.created` | Uma assinatura foi criada | | `subscription.updated` | Uma assinatura mudou (ativada, atrasada, pausada, reativada, cancelada...) | | `all` | Todos os eventos acima | ## O que chega Um `POST` com `Content-Type: application/json`: ```json { "id": 987654, "type": "payment.approved", "data": { "id": 1470491, "uuid": "6f1c1a52-1d7e-4f43-a7a4-0a5a2b1c9e10", "status": "paid", "method": "credit", "value": 600 }, "created_at": "2026-10-22 14:05:12" } ``` - `id` é o do envio: o mesmo em todas as tentativas. Use para não processar o mesmo evento duas vezes. - `type` é o evento. - `data` é o recurso no mesmo formato da API (`GET /customers/{id}`, `GET /payments/{id}` ou `GET /subscriptions/{id}`), sem o envelope `data` da API. ## Respondendo - Responda com um status **2xx** em até 10 segundos. Faça o processamento pesado depois de responder (numa fila, por exemplo). - Qualquer outro status, ou a falta de resposta, conta como falha, e o envio é repetido: até 10 tentativas, com intervalos crescentes (1 s, 30 s, 1 min, 5 min, 10 min, 30 min e depois de hora em hora). - Como pode chegar mais de uma vez, e fora de ordem, trate cada evento como um aviso para consultar o recurso, não como o estado final. ## Validando Cada envio de um webhook com segredo vem assinado, para você ter certeza de que foi a NinePay quem mandou. Dois headers acompanham o `POST`: | Header | Valor | | --- | --- | | `X-Ninepay-Timestamp` | O instante do envio, em segundos (Unix) | | `X-Ninepay-Signature` | `sha256=` seguido do HMAC-SHA256, em hexadecimal, de `{timestamp}.{corpo}` | O HMAC usa o segredo do webhook (`nep_whs_…`) como chave, e o `{corpo}` é o corpo cru da requisição, exatamente como chegou, antes de qualquer parse do JSON. Se você reserializar o JSON, os bytes mudam e a conta não bate. Para validar: 1. Leia o corpo cru e os dois headers. 2. Recuse se o timestamp tiver mais de 5 minutos de diferença para o seu relógio. Isso impede que alguém reenvie um envio capturado. 3. Calcule `sha256=` + HMAC-SHA256 de `"{timestamp}.{corpo}"` com o segredo. 4. Compare com o header `X-Ninepay-Signature` em tempo constante (não use `==`). Se não for igual, responda 401. A cada tentativa o timestamp é novo, então a assinatura muda de uma tentativa para outra, mesmo com o mesmo corpo. Valide cada uma. ```javascript // Node.js (Express): o corpo precisa chegar cru // Monte esta rota antes de qualquer app.use(express.json()) global: senão o // req.body chega já convertido em objeto e a conta não bate. Ou aplique o // express.json() só nas outras rotas e deixe o express.raw só nesta. const crypto = require('crypto'); app.post('/webhooks/ninepay', express.raw({ type: 'application/json' }), (req, res) => { const timestamp = req.header('X-Ninepay-Timestamp') || ''; const signature = req.header('X-Ninepay-Signature') || ''; if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) { return res.sendStatus(401); } const expected = 'sha256=' + crypto .createHmac('sha256', process.env.NINEPAY_WEBHOOK_SECRET) .update(`${timestamp}.`) .update(req.body) // Buffer com o corpo cru .digest('hex'); const a = Buffer.from(signature); const b = Buffer.from(expected); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) { return res.sendStatus(401); } const event = JSON.parse(req.body.toString('utf8')); // enfileire o processamento e responda logo res.sendStatus(200); }); ``` ```php 300) { http_response_code(401); exit; } $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('NINEPAY_WEBHOOK_SECRET')); if (!hash_equals($expected, $signature)) { http_response_code(401); exit; } $event = json_decode($body, true); // enfileire o processamento e responda logo http_response_code(200); ``` ### O segredo - O segredo aparece uma vez só: ao criar o webhook ou ao clicar em **Gerar novo segredo**. Copie e guarde num lugar seguro, como variável de ambiente. - Ao gerar um novo segredo, o anterior para de valer na hora. Os envios seguintes já saem assinados com o novo, então atualize o seu sistema antes de gerar, ou valide os dois por um tempo. - Webhooks criados antes da assinatura não têm segredo e seguem sem assinatura até alguém clicar em **Gerar segredo**. Sem os headers, não há o que validar. > 🚧 A assinatura não substitui a consulta à API > > Mesmo com a assinatura válida, antes de liberar algo, confirme o estado na API com o seu token: ao receber `payment.approved` para o pagamento `1470491`, consulte `GET /payments/1470491` e libere só se o `status` for `paid`. Assim, um `POST` forjado ou repetido para o seu endereço não libera nada. ## E-mail de erro Se o seu endereço falha sem parar, a NinePay avisa por e-mail, para o e-mail cadastrado no webhook. O e-mail traz um link direto para os [logs](#logs) daquele webhook. O aviso sai quando o webhook acumula 10 falhas seguidas ou mais e está falhando há 6 horas ou mais. - Sai no máximo 1 e-mail por dia por webhook. - Sem e-mail cadastrado, não sai aviso. Os webhooks criados pela API (`/v1/automations`) não têm e-mail: cadastre um no dashboard, em **Integrações → Webhooks**. - O webhook nunca é pausado: os eventos novos continuam saindo e voltam a chegar assim que o seu endereço responder 2xx. Um evento que esgotou as 10 tentativas, porém, não é reenviado. Para não perder nada, consulte na API o que mudou enquanto o endereço esteve fora. ## Logs Para ver o que a NinePay enviou e o que o seu servidor respondeu, abra o dashboard em **Integrações → Logs de Webhooks**. A página Integrações inteira é restrita ao dono da conta: quem não é o dono é redirecionado. A lista mostra os envios dos webhooks da empresa nos últimos 14 dias. Os eventos de webhooks são armazenados por 14 dias; depois disso, os logs são apagados. Webhooks excluídos continuam com os logs visíveis dentro desse prazo. Use os filtros para achar o envio que procura: - **Evento** e **Webhook**. - **Status**: **Todos**, **Qualquer erro** ou um dos códigos 200, 400, 401, 404, 500, 502, 503 e 504. - **Período**: **Hoje**, **Últimos 7 dias**, **Últimos 14 dias** ou **Personalizado**, sempre dentro dos 14 dias. Ao clicar num envio, os **Detalhes do log** mostram a URL, o evento, a data, a situação e o código, a duração, o número de tentativas, o payload enviado e o corpo da resposta do seu servidor. - A tela não reenvia eventos. Um evento que esgotou as tentativas não volta a sair; consulte na API o que mudou. --- # Checkout Source: https://docs.ninepay.com.br/docs/checkout Receba o pagamento na sua página ou numa página hospedada pela NinePay Você pode deixar a NinePay coletar os dados de pagamento de duas formas: O iframe do ninepay.js dentro da sua página, com a aparência dela. Redirecione o cliente para uma página da NinePay e receba o resultado por webhook. Nos dois casos, libere o acesso pelo [webhook](/docs/webhooks), nunca pelo retorno do navegador. ## Qual escolher - **Checkout externo:** para integrar rápido, sem desenvolver tela de pagamento. O cliente sai do seu site e paga numa página da NinePay. - **Checkout embutido:** para o cliente não sair do seu site. O pagamento acontece num iframe da NinePay dentro da sua página, com a aparência dela. ## Diferenças | | Checkout externo | Checkout embutido (ninepay.js) | |---|---|---| | Onde o cliente paga | Numa página da NinePay, para onde você redireciona o cliente | Na sua página, num iframe da NinePay | | O que fica na sua página | Nada da tela de pagamento | Título, valor e o botão de pagar; o iframe mostra os métodos, os campos do cartão e o QR do PIX | | Antes de integrar | Nada | Cadastrar no dashboard os endereços do site que abrem o iframe (até cinco) | | Assinatura | Crie plano, cliente e assinatura e redirecione para `https://checkout.ninepay.com.br/subscription//activate` | Crie a assinatura com `method` e sem cartão; a primeira fatura fica em aberto e pagá-la no iframe ativa a assinatura | | Métodos | Os da página da NinePay | Cartão, PIX Automático, PIX e boleto (sem Open Finance: o banco não abre num iframe) | | Dados do cartão | Ficam na página da NinePay | Vão do iframe direto para a NinePay; a sua página nunca os vê | Nos dois casos, o token da API fica só no seu servidor. --- # Checkout Embutido (ninepay.js) Source: https://docs.ninepay.com.br/docs/checkout-embutido O checkout embutido coloca o pagamento de uma fatura da NinePay dentro da sua página. A sua página tem o título, o valor e **o botão de pagar**; o iframe da NinePay tem só os métodos de pagamento, os campos do cartão e o QR do PIX, com a aparência da sua página. O cartão vai do iframe direto para a NinePay: a sua página nunca o vê. ## Antes de integrar 1. **Cadastre o endereço do site.** No dashboard (menu do usuário → Integrações → Checkout embutido), cadastre os endereços que vão abrir o checkout: só `https://dominio` ou `https://dominio:porta`, sem caminho, até cinco. Sem nenhum, o navegador recusa o iframe. 2. **Confira os métodos dos planos.** O iframe oferece os `allowed_methods` da fatura, nesta ordem: cartão, PIX Automático, PIX e boleto. O Open Finance não aparece no embutido, porque o banco não abre num iframe. As faturas de assinatura herdam os métodos do plano. ## No seu servidor Tudo o que usa o token da API fica no seu servidor. **O token nunca vai para o navegador.** ### Uma cobrança avulsa Crie a fatura em aberto, sem cartão, com `open: true` e os métodos que o iframe pode oferecer: ```bash curl -X POST https://payments.cefis.com.br/api/v1/payments \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "customer": { "id": 123 }, "method": "credit", "open": true, "allowed_methods": ["credit", "pix", "billet"], "value": 5000, "due_date": "2026-11-10" }' ``` `allowed_methods` é obrigatório com `open`. Nada é cobrado na criação: a fatura espera o pagamento no iframe. Mande para a página o `payment.uuid` da resposta. ### Uma assinatura Crie a assinatura com o `method` e **sem cartão**. Ela nasce em `new`, com a primeira fatura em aberto; é essa fatura que vai para o iframe, e pagá-la ativa a assinatura. ```bash curl -X POST https://payments.cefis.com.br/api/v1/subscriptions \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "customer": { "id": 123 }, "plan": { "id": 456 }, "method": "credit", "allowed_methods": ["credit"] }' ``` - A resposta traz `customer`, `subscription` e `payment`: mande para a página o `payment.uuid`. - **Sem o `method`, a assinatura é criada sem nenhuma fatura**, e não há o que pagar no iframe. - `allowed_methods` é opcional: um subconjunto dos métodos do plano, só para a primeira fatura (as seguintes usam os do plano). Com `["credit"]`, o iframe mostra só o cartão. ### Depois do pagamento - **Libere ou suspenda o acesso só pelo webhook**, confirmando com `GET /payments/{id}`. O evento `paid` na página serve para atualizar a tela: um evento no navegador pode ser forjado. - Uma assinatura em `new` que não é paga **não é cancelada sozinha**. Cancele a assinatura com `POST /subscription/cancel` e o corpo `{ "subscription": { "id": 789 } }`, e a fatura aberta com `DELETE /payments/{id}`. - Fatura cancelada não pode mais ser paga. Cancele as faturas que você não quer mais. ## Na página O script é sempre o mesmo endereço, `https://checkout.ninepay.com.br/js/v1/ninepay.js`. As correções e os recursos novos da v1 chegam nele sem você trocar nada; uma mudança incompatível vira outro endereço (`/js/v2/`), e a v1 continua no ar. `Ninepay.version` informa o build exato. A tela Integrações do dashboard gera este código já com a aparência e as opções que você escolher. ```html
``` > 📘 Nota > > O iframe não tem botão de pagar. O botão é da sua página: ele chama `elements.submit()` e depois `ninepay.confirmPayment({ elements })`. ## A troca de cartão O mesmo script, com o uuid da assinatura e `mode: 'setup'`: só os campos do cartão, e o botão da sua página confirma. ```js const elements = ninepay.elements({ subscription: '', mode: 'setup' }) elements.create('payment').mount('#cartao') botao.onclick = async () => { const { error, subscription } = await ninepay.confirmSetup({ elements }) // subscription.status: 'succeeded' (o cartão novo já é o da assinatura) } ``` Use sempre o `uuid` da assinatura, que vem do seu servidor; nunca o id numérico. ## Referência | Chamada | O que faz | | --- | --- | | `Ninepay(options?)` | Cria o cliente. `options.origin` só para outro ambiente (sandbox); sem ele, vale a origem de onde o script veio | | `Ninepay.version` | O build exato, `1.` | | `ninepay.elements({ payment, appearance? })` | O grupo de um pagamento. Para a troca de cartão: `{ subscription, mode: 'setup', appearance? }` | | `elements.create('payment', options?)` | O elemento. Opções: `layout`, `paymentMethods`, `paymentMethodOrder`, `texts`, `saveCard`, `title` | | `elements.update({ appearance })` | Troca a aparência; o elemento montado acompanha | | `elements.submit()` | Valida e mostra os erros no iframe, sem enviar: `{ selectedPaymentMethod }` ou `{ error }` | | `element.mount(alvo)`, `unmount()`, `destroy()` | Põe e tira o iframe; depois do `destroy`, não monta mais | | `element.update(options)` | Troca as opções do elemento (não troca a fatura, a assinatura nem o modo) | | `element.focus()`, `blur()` | O foco no primeiro campo vazio, ou fora do iframe | | `element.clear()` | Esvazia os campos do cartão; o método escolhido fica | | `element.collapse()` | Volta para a escolha do método (layouts `accordion` e `steps`) | | `element.on/once/off(evento, fn)` | Os eventos abaixo; um nome desconhecido lança erro | | `ninepay.confirmPayment({ elements })` | Paga: `{ payment: { id, status, method } }` ou `{ error }`. Sempre resolve | | `ninepay.confirmSetup({ elements })` | Troca o cartão: `{ subscription: { id, status } }` ou `{ error }` | ### As opções do elemento | Opção | Valores | O que faz | | --- | --- | --- | | `layout` | `'tabs'` (padrão), `'accordion'`, `{ type: 'accordion', defaultCollapsed?, radios?: 'always' \| 'never', spacedAccordionItems? }` ou `'steps'` | Como a pessoa escolhe o método: abas, uma lista que abre o método escolhido, ou uma tela de escolha com "Trocar forma de pagamento" | | `paymentMethods` | lista de métodos | Só estes métodos aparecem, sempre dentro dos que a fatura aceita. Vazia ou ausente, todos os da fatura | | `paymentMethodOrder` | lista de métodos | Só reordena os métodos que sobraram | | `texts` | `'card.holder'`, `'card.number'`, `'card.expiry'`, `'card.cvc'` | Trocam os rótulos dos campos do cartão (texto, até 40 caracteres) | | `saveCard` | `'ask'` (padrão), `'always'` ou `'never'` | A caixa "Cobrar automaticamente os próximos pagamentos neste cartão", na fatura de assinatura. `'always'` só vale com a assinatura já no cartão; o método da assinatura nunca muda sem a pessoa aceitar | | `title` | texto | O título do iframe, para leitores de tela | Os métodos são `credit`, `pix_automatic`, `pix` e `billet`. Por exemplo, numa parcela avulsa de uma assinatura com PIX Automático, `paymentMethods: ['credit', 'pix', 'billet']` esconde o PIX Automático. ### Os eventos Todos chegam com `elementType: 'payment'`. | Evento | Dados | Quando | | --- | --- | --- | | `loaderstart` | — | Logo depois do `mount` | | `ready` | `status`, `payable`, `methods` | Uma vez a cada `mount`. `payable` é falso com a fatura paga, cancelada ou estornada, ou sem método disponível | | `change` | `empty`, `complete`, `collapsed`, `value: { type }` | A pessoa escolhe ou troca o método, ou os campos ficam completos ou vazios | | `focus`, `blur` | — | O foco entra ou sai do iframe (também ao trocar de aba do navegador) | | `escape` | — | A pessoa apertou Esc dentro do iframe | | `copy` | `value: { type }` | A pessoa copiou o código do PIX ou a linha do boleto (o código não vem). Com o `copy` escutado, o aviso "Código copiado!" é da sua página | | `paid` | — | O pagamento feito nesta tela, inclusive o PIX ou o boleto pagos depois | | `loaderror` | `error` | O pagamento não carregou, ou o iframe não abriu em 15 s (confira o endereço do site no dashboard) | | `resize` | `height` | A altura do conteúdo mudou | ### Os erros Os erros vêm como `{ type, code, message }`, com `message` em português, pronta para mostrar: | `type` | `code` | | --- | --- | | `validation_error` | `incomplete`, `no_method` | | `card_error` | `card_declined` | | `invalid_request_error` | `not_ready`, `not_payable`, `in_progress`, `not_mounted`, `destroyed`, `wrong_mode` | | `rate_limit_error` | `rate_limited` | | `api_error` | `api_error`, `unexpected` | | `api_connection_error` | `timeout` (sem resposta em 65 s no `confirm` ou em 15 s no `submit`), `load_timeout` (o iframe não abriu) | ## A aparência `appearance` aceita `{ theme?, labels?, inputs?, variables?, rules? }`. O que não segue o formato é ignorado, sem erro, e fica o padrão. - `theme`: `'default'` (neutro), `'night'` ou `'flat'`, o ponto de partida das variáveis. - `labels`: `'auto'` (padrão), `'above'` (o rótulo em cima do campo) ou `'floating'` (dentro do campo, subindo ao digitar). - `inputs`: `'spaced'` (padrão) ou `'condensed'` (os campos do cartão num bloco só). **A aparência padrão da empresa.** Em Integrações → Checkout embutido, no dashboard, você salva um tema e as variáveis; o iframe usa essa aparência quando a página não manda `appearance`. O que a página manda vence, variável por variável; um `theme` diferente do salvo descarta as variáveis salvas. Uma mudança no dashboard aparece em até um minuto. ### As variáveis | Variável | Formato | Onde vale | | --- | --- | --- | | `colorPrimary` | `#rgb` ou `#rrggbb` | Os botões (métodos, copiar código) | | `colorOnPrimary` | `#rgb` ou `#rrggbb` | O texto dos botões | | `colorText` | `#rgb` ou `#rrggbb` | O texto e a base da borda dos campos | | `colorTextSecondary` | `#rgb` ou `#rrggbb` | Os textos de apoio | | `colorTextPlaceholder` | `#rgb` ou `#rrggbb` | O texto de exemplo dentro dos campos | | `colorBackground` | `#rgb` ou `#rrggbb` | O fundo do iframe e dos campos | | `colorBorder` | `#rgb` ou `#rrggbb` | A borda dos campos | | `colorDanger` | `#rgb` ou `#rrggbb` | Os erros dos campos | | `fontFamily` | nomes de fonte separados por vírgula, até 120 caracteres | Todo o texto. Fontes do sistema ou a `Inter`, que o checkout serve (não é possível carregar fontes de fora) | | `fontSizeBase` | número, 12 a 20 (px) | O tamanho do texto. Abaixo de 16, o celular aumenta o zoom ao focar um campo | | `borderRadius` | número, 0 a 24 (px) | Os cantos dos campos e dos botões | | `spacingUnit` | número, 1 a 8 (px) | O espaço por dentro dos campos (padrão 2) | > 🚧 Contraste > > O contraste das variáveis é responsabilidade de quem integra. Para os campos serem legíveis e acessíveis, use texto e fundo com pelo menos 4,5:1 e borda e fundo com 3:1 (WCAG 1.4.3 e 1.4.11). ### As regras `rules` aplica CSS por classe pública: `{ ".Tab--selected": { "borderColor": "#47a472" } }`. Cada seletor é uma classe, com no máximo um estado e uma pseudoclasse, nessa ordem (`.Classe--estado:pseudo`). | Classe | Estados | Pseudoclasses | Onde | | --- | --- | --- | --- | | `.Tab`, `.TabIcon`, `.TabLabel` | `--selected` | `:hover` `:focus` `:active` `:disabled` | As abas de método (layout `tabs`) | | `.AccordionItem` | `--selected` | `:hover` `:focus-visible` | Cada método do layout `accordion` | | `.RadioIcon` | `--checked` | — | O rádio de cada método do `accordion` | | `.StepOption` | — | `:hover` `:focus-visible` | Cada método na escolha do layout `steps` | | `.StepHint` | — | — | A frase debaixo de cada método no `steps` | | `.StepBack` | — | `:hover` `:focus-visible` | O "Trocar forma de pagamento" do `steps` | | `.Label` | `--empty` `--invalid` `--focused` `--floating` `--resting` | — | O rótulo de cada campo do cartão | | `.Input` | `--empty` `--invalid` `--complete` | `:hover` `:focus` `:disabled` `:autofill` `::placeholder` `::selection` | Os campos do cartão | | `.Error` | — | — | O erro de cada campo e a recusa do cartão | | `.Block` | — | — | A caixa com o código do PIX e a linha do boleto | | `.Text` | — | — | Os textos do PIX, do PIX Automático e do boleto | | `.CopyButton` | — | `:hover` `:focus-visible` | Os botões de copiar | | `.Link` | — | `:hover` `:focus-visible` | O "Acessar boleto" | | Propriedade | Formato | | --- | --- | | `color` | Uma cor (como abaixo) com alfa de 0,5 para cima | | `backgroundColor`, `borderColor` | `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb()`/`rgba()` ou `transparent` | | `borderWidth` | Até 4 comprimentos de 0 a 4px | | `borderStyle` | `solid`, `dashed` ou `none` | | `borderRadius` | Até 4 comprimentos de 0 a 999px | | `boxShadow` | `none`, ou até duas sombras com deslocamentos até 24px, desfoque até 24px e espalhamento até 4px | | `padding` | Até 4 comprimentos de 0 a 32px | | `fontFamily` | Como a variável | | `fontSize` | De 10 a 24px | | `fontWeight` | `normal`, `bold` ou de `300` a `800` | | `letterSpacing` | De -2 a 4px | | `lineHeight` | De `1` a `2` sem unidade, ou de 12 a 36px | | `textTransform` | `none`, `uppercase`, `lowercase` ou `capitalize` | | `transition` | Até 4 de `color`, `background-color`, `border-color` ou `box-shadow`, com a duração em ms | Comprimento é `0` ou um número em `px`, `rem` ou `em` (`rem` e `em` contam como 16px). Não são aceitos `url()`, `var()`, `!important`, seletores de atributo, descendente ou irmão, nem as propriedades que mudam o layout (`position`, `display`, `width`, `height`, `margin`, `transform` e semelhantes). Limites: 40 seletores, 12 propriedades por seletor e 120 caracteres por valor. Uma regra com `color` ou `backgroundColor` de contraste abaixo de 3:1 é descartada. Em navegadores antigos (antes do Safari 16.4), as regras não se aplicam e valem só as variáveis. ## Cuidados - Carregue o script sempre de `https://checkout.ninepay.com.br`, nunca de uma cópia ou de um CDN: a origem do iframe é a de onde o script veio. Para o sandbox, passe `Ninepay({ origin: 'https://...' })`; só `https` é aceito (ou `http://localhost`, para testar). - O CSP da página precisa liberar o checkout: `script-src` e `frame-src` com `https://checkout.ninepay.com.br`. - Mostre qualquer dado que venha do iframe como texto, nunca em `innerHTML`. - O script não usa SRI, porque recebe correções no mesmo endereço. Se você precisa de SRI, peça à NinePay uma cópia congelada. - Não monte o checkout dentro de outro iframe. Se a sua página puser `sandbox` num iframe em volta, inclua `allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox` (o PDF do boleto abre em outra aba). - O iframe limita as confirmações (uma por vez e cinco por minuto), mas a proteção contra teste de cartão é a da NinePay, no servidor. --- # Checkout Externo Source: https://docs.ninepay.com.br/docs/checkout-externo A modalidade de checkout externo permite que você processe pagamentos, links e assinaturas sem precisar desenvolver um checkout no seu site ou app. ## Como funciona O fluxo é bem simples: você direciona o cliente para uma página da NinePay onde ele fará o pagamento e/ou assinatura, e a NinePay notifica seu sistema via webhook! ## Exemplo: criando uma assinatura com dados de cliente pré-definidos Durante a criação de uma assinatura, a NinePay precisa coletar informações básicas do cliente como nome e email. As vezes você irá preferir seu próprio sistema enviar essas informações ao invés de deixar o cliente preenchê-las no checkout, pois ele pode cometer erros neste processo. Para isso, recomendamos que antes de enviar o cliente para o checkout externo, seu sistema faça uma chamada para criar o registro deste cliente na NinePay. Assim, como o ID dele em seu sistema, você pode usar o recurso de Criar uma Assinatura já vinculado diretamente a este cliente, e ele terá que preencher apenas as informações de pagamento no checkout. > 👍 Dica > > Outra vantagem deste fluxo é que tendo de antemão o ID do cliente salvo em seu sistema, você poderá receber e processar as notificações/webhooks de maneira mais simples. ## Usando os endpoints para criar uma assinatura com dados de cliente pré-definidos 1. Use o [endpoint de criação de plano](/reference/criar-novo-plano) ou crie um via dashboard. Você precisa de um plano para criar uma assinatura pois todas as assinaturas estão vinculadas a um plano. 2. Use o [endpoint de criação de cliente](/reference/criar-novo-cliente). Toda assinatura tem um cliente vinculado. 3. Use o [endpoint de criação da assinatura](/reference/criar-nova-assinatura). Na resposta de sucesso deste endpoint, você receberá um campo `uuid` dentro dos dados da assinatura. Com esse valor você deve redirecionar o usuário para a página de checkout: - em produção: `https://checkout.ninepay.com.br/subscription//activate`; - no sandbox: `https://app.sandbox.cefispay.com.br/subscription//activate`. > 📘 Nota > > Substitua `` pelo valor recebido na criação da assinatura. --- # Start here Source: https://docs.ninepay.com.br/docs/start-here The path to your first NinePay API integration, from sandbox access to your first charge These pages cover what you need before creating your first charge: how the API works, how to authenticate, where to test and how to import the API into your HTTP client. Guides and reference: what you will find in each. Environments, headers, responses, errors, dates and card hashes. API keys, environment prefixes, rotation and lifecycle. The sandbox API and dashboard. Ending in 0 approves; ending in 1 to 9 declines. Pagination, sorting and filters for list endpoints. To try the endpoints without writing code, download the API for Postman or Insomnia in [Collections](/docs/collections-en). --- # Introduction to the Guides Source: https://docs.ninepay.com.br/docs/introduction Welcome to the NinePay integration guides. The content here will help you better understand the main flows and know exactly how to interact with the NinePay API. Happy reading! > 👍 Tip > > If you need help designing a flow, just [reach out to our team](mailto:ti@ninepay.com.br) and we will be happy to help! # Guides vs Reference ### Guides 📖 They explain the overall flows in business and technical terms. Reading the guides helps you know exactly which endpoints and other resources you will need for your use case. ### Reference 🔩 The documentation of the endpoints, their parameters and responses, described in the OpenAPI standard and presented in an intuitive interface with implementation examples in several languages. --- # API Introduction Source: https://docs.ninepay.com.br/docs/api-introduction Environments, authentication, response and error formats, pagination and card hashes Welcome to the **NinePay API** reference. This API is how you integrate your system with ours. Our API is [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) and all of its responses are in [JSON](https://www.json.org/), at the base endpoint: ```text Production https://payments.cefis.com.br/api/v1/ ``` The full list of endpoints, with parameters, responses and examples in several languages, is in the [API Reference](/reference/criar-novo-pagamento). ## Environments In addition to the **production** environment, a **sandbox** environment is available for integration testing. All communication with the sandbox environment must use the URL: ```text Sandbox https://api.sandbox.cefispay.com.br/api/v1/ ``` See how to request access in [Test Environment](/docs/sandbox-en). ## Authentication All requests that require authentication must send, in the `Authorization` header, the API token provided by our team, always for the intended environment, in the following format: ```http Authorization: Bearer ``` > ❗️ The token is a secret > > Use the token only in your back end. Never put it in code that runs in the browser or in your customer's app. Key prefixes (`sk_prod_` and `sk_hmlg_`), creation, rotation and expiration are covered in [Authentication](/docs/authentication). ## Headers We recommend setting the request headers correctly to work with the data types sent to and received from the API: - All requests must include the `Content-Type: application/json` header, unless the specific endpoint says otherwise. - All requests must include the `Accept: application/json` header, unless the specific endpoint says otherwise. ## Responses - Resources come inside `data`: `GET /customers/{id}` responds with `{ "data": { ... } }`. - Creations respond with **201**. `POST /payments` responds with `{ "customer", "payment", "invoice_url" }` (`invoice_url` only for bank slips) and `POST /subscriptions` responds with `{ "customer", "subscription" }`. - Amounts are always in cents (integer): R$ 50.00 is `5000`. ## Errors Every error has the same format: ```json { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": ["O campo email é obrigatório."] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } ``` `errors` is only present on validation errors, with a **list** of messages per field. `suggestion` is present when there is one. Messages are returned in Portuguese. Keep the `request_id` when contacting support. | `code` | HTTP | When | | --- | --- | --- | | `AUTHENTICATION_ERROR` | 401 | Missing or invalid token | | `FORBIDDEN_ERROR` | 403 | The resource does not belong to your company. On several routes, an id that does not exist also responds with 403, so as not to reveal which ids exist | | `COMPANYBLOCKED_ERROR` | 403 | The account is suspended | | `RESOURCE_NOT_FOUND_ERROR` | 404 | The resource does not exist | | `ROUTE_NOT_FOUND_ERROR` | 404 | The route does not exist | | `METHOD_NOT_ALLOWED_ERROR` | 405 | The method is not valid for the route (e.g. `PUT /customers` without the id) | | `INVALID_STATE_ERROR` | 400 | The resource is not in a state that allows the action (e.g. canceling a payment that is already paid) | | `VALIDATION_ERROR` | 422 | Invalid data; see `errors` | | `REFUND_NOT_ALLOWED` | 422 | The payment cannot be refunded | | `TOO_MANY_ATTEMPTS` | 429 | Too many requests; respect the `Retry-After` header | | `UNEXPECTED_ERROR` | 500 | Our error; if it happens again, contact support with the `request_id` | ## Pagination List endpoints accept `page` (starting at 1) and `limit` (items per page: **10** by default, from 1 to **100**). The response contains `data` (the items), `links` and `meta` (`current_page`, `last_page`, `per_page`, `total`). To go through everything, increase `page` up to `meta.last_page`. ## Dates - Calendar dates (`due_date`, `next_payment`) are sent as `YYYY-MM-DD` in requests. - In responses, date-time fields come in two formats: ISO 8601 in UTC (`2026-10-27T03:00:00.000000Z`, in the payment's `due_date` and `ref_date`) and `YYYY-MM-DD HH:MM:SS` in Brasília time (`created_at` and `updated_at`). Parse each field according to its format. ## Identifiers Every resource has a numeric `id` and a `uuid`. Use the `id` in authenticated routes and the **`uuid`** in everything that is public (the payment link, the checkout and the bank slip PDF). ## Card hash A card hash is an encrypted version of all of your customer's card data. It is the safest way to send your customer's card data directly to us. Below we explain how it works and how to obtain a hash from the data entered by the customer. > 🚧 Front end only > > This flow must run on the front end, whether web or mobile. Your customer's card data must never be sent to your back end; the card hash, however, can be sent to the back end safely. ### Generating an encryption key The encryption key is an RSA public key used to encrypt the card data. This key can be used only once: whenever you need to encrypt a new card, generate a new encryption key. To generate an encryption key, make a `POST` request to the API's `/encryption_keys` route, sending your `merchant_id` as a parameter. It does not use the API token (never put it on the front end): the `merchant_id` identifies your account, and a `merchant_id` that does not exist responds with 401. The request must have this format: ```bash cURL curl -X POST https://payments.cefis.com.br/api/v1/encryption_keys \ -H 'Content-Type: application/json' \ -d '{"merchant_id":"SEU_MERCHANT_ID"}' ``` In response you will receive a JSON object in the following format: ```json { "id": 1111, "public_key": "-----BEGIN PUBLIC KEY-----\n ... \n-----END PUBLIC KEY-----\n", "ip_address": "000.000.000.000", "created_at": "2017-07-03" } ``` ### Encrypting the card data To encrypt the card data, first format it correctly: create a JSON string containing all the card data in the following format: ```json {"card_number":"1111111111111111","card_holder_name":"Jose Silva","card_expiration_date":"11/2021","card_cvv":"111"} ``` Then RSA-encrypt this string using the public key (`public_key`) generated in the previous step, with **PKCS1** padding. After encrypting the data, convert the result to base64. You will have something like this: ```text GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ== ``` The last step is to concatenate the `id` of your encryption key (received in the response to the previous request) with the base64 string that represents the card data, as follows: ```text encryption_key_id + "_" + base64_encrypted_card ``` The result is your card hash: ```text 1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ== ``` Done: your customer's card data is now in a safe format to travel over the network and can be sent to your back end to continue the flow (in the `card_hash` field of [`POST /payments`](/reference/criar-novo-pagamento)). > 📘 Card hash validity > > Each card hash can be used only once and is valid for at most 30 minutes after the encryption key is generated. If you need to use the card more than once, make a request to the cards route and save the card linked to the user who owns it: you will receive a unique ID that can be used in new transactions with the same card. ## Support Questions about the integration: [ti@ninepay.com.br](mailto:ti@ninepay.com.br). Terms of use: [bit.ly/cefispay-contrato](https://bit.ly/cefispay-contrato). --- # Authentication Source: https://docs.ninepay.com.br/docs/authentication Every call to the NinePay API is authenticated with an API key. You generate keys in the dashboard, and they have an automatic lifecycle: keys that go unused are disabled and, later, expired. ## How to authenticate Send the key in the `Authorization` header, using the `Bearer` scheme: ```bash curl https://payments.cefis.com.br/api/v1/customers \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" ``` Without a key, or with a disabled, expired or deleted key, the API responds with 401. ## Format | Prefix | Environment | | --- | --- | | `sk_prod_…` | Production | | `sk_hmlg_…` | [Sandbox](/docs/sandbox-en) | Keys created before the new Integrations screen use the old format, without a prefix, and keep working the same way. They follow the lifecycle described below like any other key. ## Generating a key 1. In the dashboard, open **Integrações** (Integrations) and go to the **Chaves de API** (API keys) tab. 2. Create the key with a name that says where it is used (for example, "Website" or "ERP"). 3. Optionally, choose an expiration date and time. 4. Copy the key and store it somewhere safe. > 🚧 The key is shown only once > > After you close the window, neither you nor NinePay can see the key again, because we only store a hash of it. If you lose it, generate a new one. Each company can have up to 10 keys. ## Rotating a key Rotate a key without taking your integration down: 1. Generate a new key. 2. Update your system to use the new key. 3. Disable the old key in the dashboard. 4. Check that nothing broke. If something does, re-enable the old key and investigate. 5. Once everything works, delete the old key. ## Automatic lifecycle An unused key goes through these states: | State | When | Can it be undone? | | --- | --- | --- | | Enabled | In use, or unused for less than 3 months | | | Disabled | 3 months without use | Yes, re-enable it in the dashboard | | Expired | 6 months without use | No, generate a new key | - The account owner receives an email 7 days before the key is disabled for lack of use, and another one on the day. Expiration, whether after 6 months without use or on the date you chose, has no warning. - Every call authenticated with the key counts as use and restarts the count. - If you set an expiration date, the key stops working on that date, regardless of use. ## Best practices - Never put the key in code that runs in the browser or in a mobile app: anyone who downloads the app or opens the page source can read it. Call the API from your server. - Store the key in an environment variable (such as `NINEPAY_TOKEN` in the examples), not in your code or repository. - Use one key per integration. That way you can disable or rotate one without affecting the others, and see which ones are in use. - If you suspect a key has leaked, disable it and generate a new one. --- # Test Environment Source: https://docs.ninepay.com.br/docs/sandbox-en To make it easier to build integrations with the NinePay platform, we offer a complete **sandbox** environment: a test API and a test Web Platform that simulate the behavior of the production environment. > 📘 Access > > When you are given access to the sandbox environment, our team provides the `keys` to authenticate with the API (in the sandbox they start with `sk_hmlg_`; see [Authentication](/docs/authentication)), and the `username` and `password` to access the Dashboard. If you do not have this information yet, [ask our team](mailto:ti@ninepay.com.br). # Sandbox API The Sandbox API is where all API calls should be made while you develop and test your integration. The details for accessing this API are documented in the [API Introduction](/docs/api-introduction). ![API Sample](/guides-assets/api-sandbox.png) # Sandbox Dashboard The Sandbox Dashboard is the web platform connected to the Sandbox API for running tests. It shows the account's information and transactions in a graphical interface and lets you manage the whole account, such as creating payments, links, plans, etc. ![Dashboard Sample](/guides-assets/dashboard-sandbox.png) --- # Test Cards Source: https://docs.ninepay.com.br/docs/test-cards-en Ending in 0 approves, ending in 1 to 9 declines (sandbox only) When you test payments on the checkout pages and/or through the API endpoints, the credit card number you use determines whether the payment is approved or not. This lets you test both authorized and unauthorized card scenarios. > 🚧 Sandbox only > > These cards only work in the [test environment](/docs/sandbox-en). In production the charge goes to the real acquirer, which declines these numbers. ## Card number (PAN) The **last digit** of the card determines authorization or decline: | Result | Card ending | | --- | --- | | ✅ Payment approved | **0** | | ❌ Payment declined | **1 to 9** | | Brand | Number | | --- | --- | | Mastercard | `5358 7326 8161 8240` | | Visa | `4293 5672 7563 6450` | | Elo | `4514 1617 3512 5180` | | Brand | Number | | --- | --- | | Mastercard | `5168 7948 3166 8921` | | Visa | `4594 3598 6479 9162` | | Elo | `5041 7541 7176 2158` | ## Expiration date For the credit card **expiration date**, always use a future month and year, for example: `05/2030`. ## CVV For the **CVV**, any 3-digit number is accepted, for example: `123`. --- # Lists and Pagination Source: https://docs.ninepay.com.br/docs/pagination List routes return your company's items, newest first, in pages. ## Pagination | Parameter | Default | What it does | | --- | --- | --- | | `page` | `1` | The page, starting at 1 | | `limit` | `10` | Items per page, from 1 to 100 | | `sort` | `created_at` | The field to sort by | | `direction` | `desc` | `asc` or `desc` | The response contains the items in `data`, the page URLs in `links` and the totals in `meta`: ```json { "data": [{ "id": 315968, "name": "Maria Silva" }], "links": { "first": "https://payments.cefis.com.br/api/v1/customers?page=1", "last": "https://payments.cefis.com.br/api/v1/customers?page=4", "prev": null, "next": "https://payments.cefis.com.br/api/v1/customers?page=2" }, "meta": { "current_page": 1, "last_page": 4, "per_page": 10, "total": 37 } } ``` To read everything, increase `page` up to `meta.last_page`. > 📘 Plans > > `GET /plans` only paginates when you send `page` or `limit`. Without either, it returns all plans at once, without `links` and `meta`. ## Filters Filters that accept a list are repeated in the query string: `status[]=active&status[]=overdue`. Date ranges (`YYYY-MM-DD`) only apply when both the start **and** the end are sent. ### `GET /customers` | Filter | What it does | | --- | --- | | `query` | Searches the name, email, CPF or CNPJ | | `status[]` | `1` (active) or `2` (inactive) | ### `GET /subscriptions` | Filter | What it does | | --- | --- | | `customer_id` | A customer's subscriptions | | `plan_id` | A plan's subscriptions | | `status[]` | `new`, `trial`, `active`, `overdue`, `paused`, `canceled` or `expired` | | `query` | Searches the customer's name, email, CPF or CNPJ, the plan name and the `custom_data` values | | `created_at_start` and `created_at_end` | Created within the range | | `end_at_start` and `end_at_end` | Ending within the range | It also accepts `sort=customer_name`, to sort by customer name. ### `GET /payments` | Filter | What it does | | --- | --- | | `subscription_id` | A subscription's invoices | | `payment_link_id` | The payments of a payment link | | `show_only_individual` | `true`: only one-off charges (no subscription, link or payment booklet) | | `method[]` | `credit`, `debit`, `pix` or `billet` | | `status[]` | `unpaid`, `paid`, `unauthorized`, `canceled` or `reversed` | | `query` | Searches the customer's name, email, CPF or CNPJ, the amount and the card digits | | `created_at_start` and `created_at_end` | Created within the range | | `due_date_start` and `due_date_end` | Due within the range | | `paid_at_start` and `paid_at_end` | Paid within the range | Card validations and the start of trial periods do not appear in the payments list. --- # Collections Source: https://docs.ninepay.com.br/docs/collections-en Download the API ready to import into Postman or Insomnia. Every endpoint of the **NinePay API** is available in files ready for the most popular API clients. Import the file, pick the environment and enter your token: each request already has its URL, parameters, example body and authentication. Collection (format v2.1) and one environment per server. One export file with every environment. ## What the files contain - One folder per API Reference group, in sidebar order, and one request per endpoint. - URLs built on the `baseUrl` variable, which follows the selected environment: **Servidor de Testes** (sandbox) or **Servidor de Produção** (production). - Documented parameters: required ones enabled, optional ones unchecked and ready to turn on. - The same example body the docs and Try It show. - Authentication set up with the `token` variable, which starts empty. No credential is ever included. > 📘 Always up to date > > The files are generated again every time these docs are published, from the same OpenAPI description as the API Reference. The request names and descriptions inside the files are in Portuguese. --- # Postman Source: https://docs.ninepay.com.br/docs/postman-en Import the API collection into Postman, pick an environment and send your first request. Use the Postman collection to try the API endpoints before implementing the calls in your application. > 📘 By the end of this guide > > You will have the collection and an environment imported into Postman, your token set and a request sent. ## Before you start - Install [Postman](https://www.postman.com/downloads/) or use the web version. - Have an API token for the environment you are going to test (see [Authentication](/docs/authentication) and [Test Environment](/docs/sandbox-en)). > ⚠️ Keep your token safe > > Do not publish your token in public workspaces, screenshots, repositories or chats. Use sandbox tokens (`sk_hmlg_`) while testing. ## 1. Download the files Download the collection and the environment of the server you will use. The collection is the same for every environment: only the environment changes. - [NinePay API — coleção do Postman](https://docs.ninepay.com.br/postman/ninepay-api.postman_collection.json) - [NinePay API — ambiente Servidor de Testes (sandbox)](https://docs.ninepay.com.br/postman/ninepay-api.servidor-de-testes-sandbox.postman_environment.json) ## 2. Import into Postman 1. In Postman, open the workspace that will hold the collection and click **Import**. 2. Drop the collection and the environment into the window or click **files** to pick them. You can also paste a file address (right-click a download button and copy the link). 3. Confirm with **Import**. The collection shows up under **Collections** and the environment under **Environments**. ## 3. Select the environment Each environment sets the `baseUrl` variable to the base URL of one API server: | Environment | Base URL (`baseUrl`) | | --- | --- | | Servidor de Testes (sandbox) | `https://api.sandbox.cefispay.com.br/api/v1` | | Servidor de Produção (production) | `https://payments.cefis.com.br/api/v1` | In the environment selector at the top right, pick the imported environment. Without one, requests use the collection's own `baseUrl` (the first server, the sandbox). ## 4. Enter your token Authentication is already set on the collection's **Authorization** tab and the requests inherit it. It uses the variable below, which starts empty: | Variable | Use | Reference | | --- | --- | --- | | `token` | Access token, sent as `Authorization: Bearer ` | `{{token}}` | Under **Environments**, open the selected environment, fill in the variable value and save (**Ctrl+S** or **Cmd+S**). The credential variable is **secret**: the value is masked on screen. ## 5. Send a request 1. Open a folder of the collection and select an endpoint. 2. Check the URL and the **Params** tab: path variables (`:id`) and optional parameters, which start unchecked. 3. Adjust the example body on the **Body** tab, when there is one. 4. Click **Send** and check the status and the response body. A `2xx` status means success. The exact code and response format are on the endpoint page in the API Reference. ## Common errors | Problem | What to check | | --- | --- | | `401` or `403` | Whether the `token` variable is filled in on the selected environment and the token belongs to the same environment as the URL. | | URL shows `{{baseUrl}}` or an invalid URL error | Whether an environment is selected at the top right. | | `404` | Whether the path variables on the **Params** tab are filled in. | | `400` or `422` | Whether the body is valid JSON and follows the fields described on the endpoint page. | ## Updates To get new or changed endpoints, download the collection again and import it. Postman notices the collection already exists and asks whether to replace it or import a copy. ## Next steps - [Browse the API Reference](/reference/criar-novo-pagamento) - [Use the collection in Insomnia](/docs/insomnia-en) - [See all collections](/docs/collections-en) --- # Insomnia Source: https://docs.ninepay.com.br/docs/insomnia-en Import the API into Insomnia with environments and authentication ready, and send your first request. Import the API into Insomnia to get every endpoint in one collection, organised in folders, with environments and authentication ready. > 📘 By the end of this guide > > You will have the collection imported into Insomnia, an environment selected, your token set and a request sent. ## Before you start - Install [Insomnia](https://insomnia.rest/download). - Have an API token for the environment you are going to test (see [Authentication](/docs/authentication) and [Test Environment](/docs/sandbox-en)). > ⚠️ Keep your token safe > > Do not publish your token in public workspaces, screenshots, repositories or chats. Use sandbox tokens (`sk_hmlg_`) while testing. ## 1. Download the file A single file holds the collection and every environment. - [NinePay API — arquivo do Insomnia](https://docs.ninepay.com.br/insomnia/ninepay-api.insomnia.json) ## 2. Import into Insomnia 1. In Insomnia, open the project that will hold the collection and click **Import**. 2. Choose **File** and pick the downloaded file, or choose **URL** and paste the file address (below). 3. Click **Scan**, review the summary and confirm with **Import**. A collection named after the API is created, with one folder per API Reference group. Address for importing from a URL: ```text https://docs.ninepay.com.br/insomnia/ninepay-api.insomnia.json ``` > 📘 Alternative: the Postman collection > > Insomnia also imports the [Postman collection](/docs/postman-en) (format v2.1). Use it if your team already works with it. ## 3. Pick the environment The collection has a base environment and one sub environment per API server, each with the `baseUrl` variable: | Sub environment | Base URL (`baseUrl`) | | --- | --- | | Servidor de Testes (sandbox) | `https://api.sandbox.cefispay.com.br/api/v1` | | Servidor de Produção (production) | `https://payments.cefis.com.br/api/v1` | In the environment selector above the request list, pick the sub environment. Without one, the base environment applies (the first server, the sandbox). ## 4. Enter your token Every request already has authentication set on its **Auth** tab, using the variable below, which starts empty: | Variable | Use | Reference | | --- | --- | --- | | `token` | Access token, sent as `Authorization: Bearer ` | `{{ _.token }}` | Open **Manage Environments** (in the environment selector), select the sub environment in use and fill in the variable value. Changes are saved automatically. ## 5. Send a request 1. Open a folder of the collection and select an endpoint. 2. Check the URL and the **Params** (path variables and optional parameters, which start disabled) and **Headers** tabs. 3. Adjust the example body on the **Body** tab, when there is one. 4. Click **Send** and check the status and the response body. A `2xx` status means success. The exact code and response format are on the endpoint page in the API Reference. ## Common errors | Problem | What to check | | --- | --- | | The import finds no requests | Whether you picked the `.insomnia.json` file from this page (or its full URL) and it downloaded completely. | | `401` or `403` | Whether the `token` variable is filled in on the selected sub environment and the token belongs to the same environment as the URL. | | URL with an empty `_.baseUrl` | Whether a sub environment is selected or the base environment has `baseUrl` set. | | `400` or `422` | Whether the body is valid JSON and follows the fields described on the endpoint page. | ## Updates To get new or changed endpoints, import the file again. To compare with the previous version before replacing it, import it into a separate project. ## Next steps - [Browse the API Reference](/reference/criar-novo-pagamento) - [Use the Postman collection](/docs/postman-en) - [See all collections](/docs/collections-en) --- # MCP Server (AI) Source: https://docs.ninepay.com.br/docs/mcp-server-en Use the NinePay documentation with AI assistants # Use the NinePay documentation with AI assistants The NinePay MCP Server lets AI assistants such as Claude, ChatGPT, Cursor and VS Code query this documentation directly, always up to date: endpoints, parameters, examples and guides. With it, your assistant answers integration questions and writes code based on the official documentation, without you copying and pasting pages. > 📘 Documentation only > > The MCP Server is public and read-only. It doesn't access your account, doesn't use your API key and doesn't call the NinePay API. ## Connect your assistant to the MCP Server The MCP Server address is: ```text URL https://docs.ninepay.com.br/mcp ``` ### What the assistant can query | Tool | What it does | |---|---| | `list-specs` | Lists the available OpenAPI specs | | `list-endpoints` | Lists every API endpoint | | `search-endpoints` | Searches endpoints by path, name, parameter or field | | `get-endpoint` | Returns an endpoint's details: parameters, body, responses and examples | | `search` | Searches the guides and the reference | | `fetch` | Reads a documentation page as Markdown | ## Configure the MCP Server On any documentation page, the menu next to **Copiar página** has shortcuts to install in Cursor and VS Code and to copy the configuration. ### Cursor Add to `~/.cursor/mcp.json`: ```json mcp.json { "mcpServers": { "ninepay-docs-mcp": { "url": "https://docs.ninepay.com.br/mcp" } } } ``` ### VS Code Add to the project's `.vscode/mcp.json`: ```json .vscode/mcp.json { "servers": { "ninepay-docs-mcp": { "type": "http", "url": "https://docs.ninepay.com.br/mcp" } } } ``` ### Claude Code ```bash Terminal claude mcp add --transport http ninepay-docs-mcp https://docs.ninepay.com.br/mcp ``` ### Claude Desktop, ChatGPT and others Add a remote MCP connector with the URL `https://docs.ninepay.com.br/mcp`. The menu path depends on the app: check its documentation. ## Test the configuration Once connected, ask your assistant something like: - "How do I create a recurring subscription on NinePay?" - "Which fields are required to create a payment?" - "Show a Node.js example to charge with PIX in the sandbox." If the answer cites NinePay endpoints and guides, the MCP Server is working. ## Use the LLMs.txt file For assistants that don't use MCP, the documentation is also available as text files made for AI: - [`/llms.txt`](/llms.txt): an index of every page. - [`/llms-full.txt`](/llms-full.txt): the whole documentation in a single file. Every page also has a Markdown version: replace the end of its address with `.md`. ## Report incorrect information AI assistants can make mistakes. If you find a wrong answer based on this documentation, let our team know at [ti@ninepay.com.br](mailto:ti@ninepay.com.br). ## Next steps - [Authentication](/docs/authentication) - [Sandbox](/docs/sandbox-en) - [Postman and Insomnia collections](/docs/collections-en) --- # Charges Source: https://docs.ninepay.com.br/docs/charges One-off charges and recurring subscriptions by card, PIX and bank slip NinePay charges in two ways: one-off **payments** (a single charge by card, PIX or bank slip) and **subscriptions**, which generate an invoice on every billing cycle of the plan. Open charges, refunds and manual settlement. Pause, resume and discount with coupons. The fields of each call are in the reference: [Create payment](/reference/criar-novo-pagamento) and [Create subscription](/reference/criar-nova-assinatura). --- # Payments - Refunds and Manual Settlement Source: https://docs.ninepay.com.br/docs/payments ## Open charge To create a charge without charging right away, and let the customer choose how to pay in the embedded checkout, use `open: true` with `allowed_methods`. The step-by-step is in the [embedded checkout guide](/docs/embedded-checkout). ## Refund Refunds a payment paid by card or PIX and returns the amount to the customer: ```bash curl -X POST https://payments.cefis.com.br/api/v1/payment/reversal \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "payment": { "id": 1470491 } }' ``` A payment can only be refunded if it is: - `status: "paid"`; - paid by card (`credit`) less than 350 days ago, or by PIX (`pix`) less than 90 days ago; - covered by enough balance in your account for the refund. Bank slips and manual settlements cannot be refunded through the API. The payment's `can_refund` field tells whether it can be refunded right now. When it cannot, the API responds with **422** `REFUND_NOT_ALLOWED`, with the reason in `message` (already refunded, past the deadline, insufficient balance, refund already requested...). ### Tracking the refund Card refunds are usually immediate: the payment becomes `reversed` and the `payment.updated` webhook is sent. PIX refunds may stay in processing; fetch the payment with its refunds: ```bash curl "https://payments.cefis.com.br/api/v1/payments/1470491?with[]=refunds" \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" ``` Each item in `refunds` has `id`, `method`, `value` (cents), `status` and the dates. The `status` goes from `new` and `processing` to `completed` (returned) or `failed`. While a refund is `processing`, another request for the same payment is rejected. ## Manual settlement Marks as paid a payment that the customer settled outside NinePay (in cash or by bank transfer, for example): ```bash curl -X POST https://payments.cefis.com.br/api/v1/payment/1470491/write-off \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "value_type": "normal" }' ``` - `value_type`: `normal` (the default) keeps the payment amount; `zero` settles it with an amount of 0 (a waived payment, for example). - Only valid for `unpaid` or `unauthorized` payments. For others, the API responds with **400** `INVALID_STATE_ERROR`. - The payment becomes `paid` with `method: "manual"`, and the `payment.updated` webhook is sent. - Because it is `manual`, it cannot be refunded through the API. --- # Subscriptions - Pause and Coupons Source: https://docs.ninepay.com.br/docs/subscriptions ## Pause and resume Pausing a subscription stops it from generating invoices until you resume it. Existing invoices stay as they are. ```bash curl -X PUT https://payments.cefis.com.br/api/v1/subscription/pause \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "subscription": { "id": 205465 } }' ``` To resume, send the same body to `PUT /subscription/unpause`. The subscription goes back to `active` and, if the next charge fell due during the pause, it becomes due now: the invoice is issued in the next billing run. Both respond with `201` and `{ "subscription": { ... } }`, and trigger the `subscription.updated` webhook. Only `active`, `overdue` or `trial` subscriptions can be paused, and only `paused` ones can be resumed. In any other status, the API responds with **400** `INVALID_STATE_ERROR`. To end a subscription, cancel it with `POST /subscription/cancel`. ## Coupons A coupon gives a discount on a subscription's invoices (`type: "subscription"`) or on a payment link (`type: "payment_link"`). ### Creating a coupon ```bash curl -X POST https://payments.cefis.com.br/api/v1/coupons \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "type": "subscription", "name": "Black Friday", "code": "BLACK20", "percentage": 20, "value": 0, "duration": 3, "max_usages": 100, "expiration_date": "2026-11-30" }' ``` | Field | What it does | | --- | --- | | `code` | The code the customer types, up to 50 characters | | `percentage` | The discount as a percentage of the invoice amount | | `value` | A fixed discount, in cents, added to the percentage discount (`0` for percentage only) | | `duration` | How many of the subscription's invoices get the discount (optional; without it, all of them) | | `max_usages` | How many times, in total, the coupon can be used (optional) | | `expiration_date` | Until when the coupon can be applied (optional) | The discount never exceeds the invoice amount. To deactivate a coupon, send `PUT /coupons/{id}` with `"status": "canceled"`. ### Applying it to a subscription When creating the subscription, send the codes in `coupon_codes`: ```json { "customer": { "id": 315968 }, "plan": { "id": 413 }, "method": "credit", "coupon_codes": ["BLACK20"] } ``` On an existing subscription, apply it by the coupon id; the discount applies to the next invoices: ```bash curl -X POST https://payments.cefis.com.br/api/v1/subscriptions/205465/coupons \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "coupon_id": 77 }' ``` - `GET /subscriptions/{id}/coupons` lists the subscription's coupons, with the `status` of each: `unused` (not used yet), `using` (already gave a discount and invoices remain), `used` (duration finished) or `canceled`. - `PUT /subscription/coupons/{id}/cancel` removes the coupon from the subscription (the `id` is the coupon's id on the subscription, from the list above). Invoices that already got the discount do not change. Each discount appears as a negative item on the invoice, `Cupom `. --- # Webhooks Source: https://docs.ninepay.com.br/docs/webhooks-en Webhooks notify your system when a customer, a payment or a subscription changes. They, not the checkout return in the browser, are what should grant or suspend access for paying customers. ## Setting up Register the webhook in the dashboard, under **Integrações → Webhooks**, or through the API (`/v1/automations`): ```bash curl -X POST https://payments.cefis.com.br/api/v1/automations \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "type": "webhook", "name": "Meu sistema", "webhook_url": "https://meusistema.com.br/webhooks/ninepay", "triggers": ["payment.approved", "payment.updated", "subscription.updated"], "status": "active" }' ``` The `webhook_url` must be a public address (`http` or `https`, outside internal networks); otherwise the API responds with 422. Use `https`. To check the address before waiting for a real event, the webhook form in the dashboard has a test delivery for each event. You can send up to 10 test deliveries per minute; above that, the response is 429 and you just wait a minute. The limit also applies to the test delivery through the API (`POST /v1/automations/test`). > 📘 A webhook with a secret can only be edited in the dashboard > > The API still creates, lists and edits webhooks without a secret, but it responds with 422 in two cases: > > - **Webhook managed in Integrações**, such as one that already has a secret (see [Validating](#validating)): it can only be changed in the dashboard, even if it is just the name or the status. The API returns the error in `errors.webhook_url` and the webhook stays as it was. > - **Event not accepted in `triggers`**: the API returns the error in `errors.triggers.N`, where `N` is the position of the event in the list, starting at 0, and nothing is saved. Use the events from the [Events](#events) table. > > ```json > { > "message": "Evento inválido.", > "errors": { > "triggers.1": ["Evento inválido."] > } > } > ``` ## Events | Event | When | | --- | --- | | `customer.created` | A customer was created | | `customer.updated` | A customer was changed | | `payment.created` | A charge or invoice was created | | `payment.updated` | A payment changed (paid, declined, canceled, refunded, settled...) | | `payment.approved` | A payment was approved | | `subscription.created` | A subscription was created | | `subscription.updated` | A subscription changed (activated, overdue, paused, resumed, canceled...) | | `all` | All of the events above | ## What you receive A `POST` with `Content-Type: application/json`: ```json { "id": 987654, "type": "payment.approved", "data": { "id": 1470491, "uuid": "6f1c1a52-1d7e-4f43-a7a4-0a5a2b1c9e10", "status": "paid", "method": "credit", "value": 600 }, "created_at": "2026-10-22 14:05:12" } ``` - `id` identifies the delivery: it is the same on every attempt. Use it to avoid processing the same event twice. - `type` is the event. - `data` is the resource in the same format as the API (`GET /customers/{id}`, `GET /payments/{id}` or `GET /subscriptions/{id}`), without the API's `data` envelope. ## Responding - Respond with a **2xx** status within 10 seconds. Do heavy processing after responding (in a queue, for example). - Any other status, or no response, counts as a failure, and the delivery is retried: up to 10 attempts, at increasing intervals (1 s, 30 s, 1 min, 5 min, 10 min, 30 min and then every hour). - Since an event can arrive more than once, and out of order, treat each event as a signal to fetch the resource, not as the final state. ## Validating Every delivery of a webhook with a secret is signed, so you can be sure it was NinePay that sent it. Two headers come with the `POST`: | Header | Value | | --- | --- | | `X-Ninepay-Timestamp` | The time of the delivery, in seconds (Unix) | | `X-Ninepay-Signature` | `sha256=` followed by the HMAC-SHA256, in hexadecimal, of `{timestamp}.{body}` | The HMAC uses the webhook secret (`nep_whs_…`) as the key, and `{body}` is the raw request body, exactly as it arrived, before any JSON parsing. If you re-serialize the JSON, the bytes change and the computation does not match. To validate: 1. Read the raw body and the two headers. 2. Reject it if the timestamp is more than 5 minutes off from your clock. This prevents someone from replaying a captured delivery. 3. Compute `sha256=` + HMAC-SHA256 of `"{timestamp}.{body}"` with the secret. 4. Compare it with the `X-Ninepay-Signature` header in constant time (do not use `==`). If it does not match, respond with 401. On each attempt the timestamp is new, so the signature changes from one attempt to the next, even with the same body. Validate each one. ```javascript // Node.js (Express): the body must arrive raw // Mount this route before any global app.use(express.json()): otherwise // req.body arrives already converted to an object and the computation does not // match. Or apply express.json() only to the other routes and keep express.raw // only on this one. const crypto = require('crypto'); app.post('/webhooks/ninepay', express.raw({ type: 'application/json' }), (req, res) => { const timestamp = req.header('X-Ninepay-Timestamp') || ''; const signature = req.header('X-Ninepay-Signature') || ''; if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) { return res.sendStatus(401); } const expected = 'sha256=' + crypto .createHmac('sha256', process.env.NINEPAY_WEBHOOK_SECRET) .update(`${timestamp}.`) .update(req.body) // Buffer with the raw body .digest('hex'); const a = Buffer.from(signature); const b = Buffer.from(expected); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) { return res.sendStatus(401); } const event = JSON.parse(req.body.toString('utf8')); // queue the processing and respond right away res.sendStatus(200); }); ``` ```php 300) { http_response_code(401); exit; } $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('NINEPAY_WEBHOOK_SECRET')); if (!hash_equals($expected, $signature)) { http_response_code(401); exit; } $event = json_decode($body, true); // queue the processing and respond right away http_response_code(200); ``` ### The secret - The secret is shown only once: when you create the webhook or when you click **Gerar novo segredo**. Copy it and store it somewhere safe, such as an environment variable. - When you generate a new secret, the previous one stops working immediately. The following deliveries are already signed with the new one, so update your system before generating it, or validate both for a while. - Webhooks created before signing have no secret and stay unsigned until someone clicks **Gerar segredo**. Without the headers, there is nothing to validate. > 🚧 The signature does not replace the API lookup > > Even with a valid signature, before granting anything, confirm the state in the API with your token: when you receive `payment.approved` for payment `1470491`, call `GET /payments/1470491` and grant access only if the `status` is `paid`. That way, a forged or replayed `POST` to your address grants nothing. ## Error e-mail If your address keeps failing, NinePay warns you by e-mail, sent to the e-mail registered on the webhook. The e-mail links straight to that webhook's [logs](#logs). The notice goes out when the webhook accumulates 10 or more consecutive failures and has been failing for 6 hours or more. - At most 1 e-mail per day per webhook. - With no e-mail registered, no notice is sent. Webhooks created through the API (`/v1/automations`) have no e-mail: register one in the dashboard, under **Integrações → Webhooks**. - The webhook is never paused: new events keep going out and start arriving again as soon as your address responds 2xx. An event that exhausted its 10 attempts, however, is not resent. To lose nothing, check in the API what changed while the address was down. ## Logs To see what NinePay sent and what your server answered, open the dashboard under **Integrações → Logs de Webhooks**. The whole Integrações page is restricted to the account owner: anyone else is redirected. The list shows your company's webhook deliveries from the last 14 days. Webhook events are stored for 14 days; after that, the logs are deleted. Deleted webhooks keep their logs visible within that window. Use the filters to find the delivery you are looking for: - **Evento** (event) and **Webhook**. - **Status**: **Todos** (all), **Qualquer erro** (any error), or one of the codes 200, 400, 401, 404, 500, 502, 503 and 504. - **Período** (period): **Hoje** (today), **Últimos 7 dias** (last 7 days), **Últimos 14 dias** (last 14 days), or **Personalizado** (custom), always within the 14 days. Click a delivery and the **Detalhes do log** (log details) show the URL, the event, the date, the status and code, the duration, the number of attempts, the payload sent and the body of your server's response. - The screen does not resend events. An event that exhausted its attempts does not go out again; check in the API what changed. --- # Checkout Source: https://docs.ninepay.com.br/docs/checkout-en Take payments on your own page or on a page hosted by NinePay You can let NinePay collect the payment details in two ways: The ninepay.js iframe inside your page, matching its look. Redirect the customer to a NinePay page and get the result by webhook. In both cases, grant access through the [webhook](/docs/webhooks-en), never through the browser return. ## Which one to choose - **External checkout:** the quickest integration, with no payment screen to build. The customer leaves your site and pays on a NinePay page. - **Embedded checkout:** the customer stays on your site. Payment happens in a NinePay iframe inside your page, matching its look. ## Differences | | External checkout | Embedded checkout (ninepay.js) | |---|---|---| | Where the customer pays | On a NinePay page you redirect the customer to | On your page, in a NinePay iframe | | What stays on your page | No payment screen | Title, amount and the pay button; the iframe shows the methods, the card fields and the PIX QR code | | Before integrating | Nothing | Register in the dashboard the site addresses that open the iframe (up to five) | | Subscription | Create plan, customer and subscription, then redirect to `https://checkout.ninepay.com.br/subscription//activate` | Create the subscription with `method` and no card; its first invoice stays open and paying it in the iframe activates the subscription | | Methods | Those on the NinePay page | Card, PIX Automático, PIX and boleto (no Open Finance: banks don't open inside an iframe) | | Card data | Stays on the NinePay page | Goes from the iframe straight to NinePay; your page never sees it | In both cases, the API token stays on your server only. --- # Embedded Checkout (ninepay.js) Source: https://docs.ninepay.com.br/docs/embedded-checkout The embedded checkout puts the payment of a NinePay invoice inside your page. Your page has the title, the amount and **the pay button**; the NinePay iframe has only the payment methods, the card fields and the PIX QR code, styled to match your page. The card goes from the iframe straight to NinePay: your page never sees it. ## Before you integrate 1. **Register your site address.** In the dashboard (user menu → Integrações → Checkout embutido), register the addresses that will open the checkout: only `https://domain` or `https://domain:port`, without a path, up to five. Without any, the browser refuses the iframe. 2. **Check the plans' methods.** The iframe offers the invoice's `allowed_methods`, in this order: card, PIX Automático, PIX and bank slip. Open Finance does not appear in the embedded checkout, because the bank does not open in an iframe. Subscription invoices inherit the plan's methods. ## On your server Everything that uses the API token stays on your server. **The token never goes to the browser.** ### A one-off charge Create the open invoice, without a card, with `open: true` and the methods the iframe may offer: ```bash curl -X POST https://payments.cefis.com.br/api/v1/payments \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "customer": { "id": 123 }, "method": "credit", "open": true, "allowed_methods": ["credit", "pix", "billet"], "value": 5000, "due_date": "2026-11-10" }' ``` `allowed_methods` is required with `open`. Nothing is charged on creation: the invoice waits for payment in the iframe. Send the `payment.uuid` from the response to the page. ### A subscription Create the subscription with the `method` and **without a card**. It starts as `new`, with the first invoice open; that invoice is the one that goes to the iframe, and paying it activates the subscription. ```bash curl -X POST https://payments.cefis.com.br/api/v1/subscriptions \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "customer": { "id": 123 }, "plan": { "id": 456 }, "method": "credit", "allowed_methods": ["credit"] }' ``` - The response contains `customer`, `subscription` and `payment`: send the `payment.uuid` to the page. - **Without the `method`, the subscription is created without any invoice**, and there is nothing to pay in the iframe. - `allowed_methods` is optional: a subset of the plan's methods, for the first invoice only (later ones use the plan's). With `["credit"]`, the iframe shows only the card. ### After the payment - **Grant or suspend access only through the webhook**, confirming with `GET /payments/{id}`. The `paid` event on the page is for updating the screen: a browser event can be forged. - A `new` subscription that is not paid **is not canceled automatically**. Cancel the subscription with `POST /subscription/cancel` and the body `{ "subscription": { "id": 789 } }`, and the open invoice with `DELETE /payments/{id}`. - A canceled invoice can no longer be paid. Cancel the invoices you no longer want. ## On the page The script always has the same address, `https://checkout.ninepay.com.br/js/v1/ninepay.js`. Fixes and new v1 features reach it without you changing anything; a breaking change gets a new address (`/js/v2/`), and v1 stays online. `Ninepay.version` reports the exact build. The dashboard's Integrations screen generates this code with the look and options you choose. ```html
``` > 📘 Note > > The iframe has no pay button. The button belongs to your page: it calls `elements.submit()` and then `ninepay.confirmPayment({ elements })`. ## Changing the card The same script, with the subscription uuid and `mode: 'setup'`: only the card fields, and your page's button confirms. ```js const elements = ninepay.elements({ subscription: '', mode: 'setup' }) elements.create('payment').mount('#cartao') botao.onclick = async () => { const { error, subscription } = await ninepay.confirmSetup({ elements }) // subscription.status: 'succeeded' (the new card is now the subscription's card) } ``` Always use the subscription `uuid`, which comes from your server; never the numeric id. ## Reference | Call | What it does | | --- | --- | | `Ninepay(options?)` | Creates the client. `options.origin` only for another environment (sandbox); without it, the origin the script was loaded from applies | | `Ninepay.version` | The exact build, `1.` | | `ninepay.elements({ payment, appearance? })` | The group for a payment. To change the card: `{ subscription, mode: 'setup', appearance? }` | | `elements.create('payment', options?)` | The element. Options: `layout`, `paymentMethods`, `paymentMethodOrder`, `texts`, `saveCard`, `title` | | `elements.update({ appearance })` | Changes the look; the mounted element follows | | `elements.submit()` | Validates and shows errors in the iframe, without sending: `{ selectedPaymentMethod }` or `{ error }` | | `element.mount(target)`, `unmount()`, `destroy()` | Adds and removes the iframe; after `destroy`, it can no longer be mounted | | `element.update(options)` | Changes the element's options (does not change the invoice, the subscription or the mode) | | `element.focus()`, `blur()` | Focus on the first empty field, or out of the iframe | | `element.clear()` | Clears the card fields; the chosen method stays | | `element.collapse()` | Goes back to choosing the method (`accordion` and `steps` layouts) | | `element.on/once/off(event, fn)` | The events below; an unknown name throws an error | | `ninepay.confirmPayment({ elements })` | Pays: `{ payment: { id, status, method } }` or `{ error }`. Always resolves | | `ninepay.confirmSetup({ elements })` | Changes the card: `{ subscription: { id, status } }` or `{ error }` | ### Element options | Option | Values | What it does | | --- | --- | --- | | `layout` | `'tabs'` (default), `'accordion'`, `{ type: 'accordion', defaultCollapsed?, radios?: 'always' \| 'never', spacedAccordionItems? }` or `'steps'` | How the person chooses the method: tabs, a list that expands the chosen method, or a choice screen with "Trocar forma de pagamento" (change payment method) | | `paymentMethods` | list of methods | Only these methods appear, always within those the invoice accepts. Empty or missing, all of the invoice's methods | | `paymentMethodOrder` | list of methods | Only reorders the remaining methods | | `texts` | `'card.holder'`, `'card.number'`, `'card.expiry'`, `'card.cvc'` | Change the card field labels (text, up to 40 characters) | | `saveCard` | `'ask'` (default), `'always'` or `'never'` | The "Cobrar automaticamente os próximos pagamentos neste cartão" (charge future payments to this card automatically) checkbox, on subscription invoices. `'always'` only applies when the subscription is already on card; the subscription's method never changes without the person agreeing | | `title` | text | The iframe title, for screen readers | The methods are `credit`, `pix_automatic`, `pix` and `billet`. For example, on a one-off installment of a subscription with PIX Automático, `paymentMethods: ['credit', 'pix', 'billet']` hides PIX Automático. ### Events All of them come with `elementType: 'payment'`. | Event | Data | When | | --- | --- | --- | | `loaderstart` | — | Right after `mount` | | `ready` | `status`, `payable`, `methods` | Once per `mount`. `payable` is false when the invoice is paid, canceled or refunded, or has no available method | | `change` | `empty`, `complete`, `collapsed`, `value: { type }` | The person chooses or switches the method, or the fields become complete or empty | | `focus`, `blur` | — | Focus enters or leaves the iframe (also when switching browser tabs) | | `escape` | — | The person pressed Esc inside the iframe | | `copy` | `value: { type }` | The person copied the PIX code or the bank slip line (the code is not included). When you listen to `copy`, the "Código copiado!" notice is up to your page | | `paid` | — | The payment made on this screen, including PIX or bank slip paid later | | `loaderror` | `error` | The payment did not load, or the iframe did not open within 15 s (check the site address in the dashboard) | | `resize` | `height` | The content height changed | ### Errors Errors come as `{ type, code, message }`, with `message` in Portuguese, ready to display: | `type` | `code` | | --- | --- | | `validation_error` | `incomplete`, `no_method` | | `card_error` | `card_declined` | | `invalid_request_error` | `not_ready`, `not_payable`, `in_progress`, `not_mounted`, `destroyed`, `wrong_mode` | | `rate_limit_error` | `rate_limited` | | `api_error` | `api_error`, `unexpected` | | `api_connection_error` | `timeout` (no response within 65 s on `confirm` or 15 s on `submit`), `load_timeout` (the iframe did not open) | ## Appearance `appearance` accepts `{ theme?, labels?, inputs?, variables?, rules? }`. Anything that does not follow the format is ignored, without an error, and the default applies. - `theme`: `'default'` (neutral), `'night'` or `'flat'`, the starting point for the variables. - `labels`: `'auto'` (default), `'above'` (the label above the field) or `'floating'` (inside the field, moving up as you type). - `inputs`: `'spaced'` (default) or `'condensed'` (the card fields in a single block). **The company's default appearance.** In Integrações → Checkout embutido, in the dashboard, you save a theme and the variables; the iframe uses this appearance when the page does not send `appearance`. What the page sends wins, variable by variable; a `theme` different from the saved one discards the saved variables. A change in the dashboard shows up within a minute. ### Variables | Variable | Format | Where it applies | | --- | --- | --- | | `colorPrimary` | `#rgb` or `#rrggbb` | The buttons (methods, copy code) | | `colorOnPrimary` | `#rgb` or `#rrggbb` | The button text | | `colorText` | `#rgb` or `#rrggbb` | The text and the base of the field borders | | `colorTextSecondary` | `#rgb` or `#rrggbb` | Supporting text | | `colorTextPlaceholder` | `#rgb` or `#rrggbb` | The placeholder text inside the fields | | `colorBackground` | `#rgb` or `#rrggbb` | The background of the iframe and the fields | | `colorBorder` | `#rgb` or `#rrggbb` | The field borders | | `colorDanger` | `#rgb` or `#rrggbb` | Field errors | | `fontFamily` | comma-separated font names, up to 120 characters | All text. System fonts or `Inter`, which the checkout serves (external fonts cannot be loaded) | | `fontSizeBase` | number, 12 to 20 (px) | The text size. Below 16, phones zoom in when a field gets focus | | `borderRadius` | number, 0 to 24 (px) | The corners of fields and buttons | | `spacingUnit` | number, 1 to 8 (px) | The padding inside the fields (default 2) | > 🚧 Contrast > > Contrast of the variables is the integrator's responsibility. For the fields to be legible and accessible, use text and background with at least 4.5:1, and border and background with 3:1 (WCAG 1.4.3 and 1.4.11). ### Rules `rules` applies CSS by public class: `{ ".Tab--selected": { "borderColor": "#47a472" } }`. Each selector is a class, with at most one state and one pseudo-class, in that order (`.Class--state:pseudo`). | Class | States | Pseudo-classes | Where | | --- | --- | --- | --- | | `.Tab`, `.TabIcon`, `.TabLabel` | `--selected` | `:hover` `:focus` `:active` `:disabled` | The method tabs (`tabs` layout) | | `.AccordionItem` | `--selected` | `:hover` `:focus-visible` | Each method in the `accordion` layout | | `.RadioIcon` | `--checked` | — | The radio of each `accordion` method | | `.StepOption` | — | `:hover` `:focus-visible` | Each method in the `steps` layout choice | | `.StepHint` | — | — | The sentence under each method in `steps` | | `.StepBack` | — | `:hover` `:focus-visible` | The "Trocar forma de pagamento" (change payment method) link in `steps` | | `.Label` | `--empty` `--invalid` `--focused` `--floating` `--resting` | — | The label of each card field | | `.Input` | `--empty` `--invalid` `--complete` | `:hover` `:focus` `:disabled` `:autofill` `::placeholder` `::selection` | The card fields | | `.Error` | — | — | Each field's error and the card decline | | `.Block` | — | — | The box with the PIX code and the bank slip line | | `.Text` | — | — | The PIX, PIX Automático and bank slip texts | | `.CopyButton` | — | `:hover` `:focus-visible` | The copy buttons | | `.Link` | — | `:hover` `:focus-visible` | The "Acessar boleto" (open bank slip) link | | Property | Format | | --- | --- | | `color` | A color (as below) with alpha of 0.5 or higher | | `backgroundColor`, `borderColor` | `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb()`/`rgba()` or `transparent` | | `borderWidth` | Up to 4 lengths from 0 to 4px | | `borderStyle` | `solid`, `dashed` or `none` | | `borderRadius` | Up to 4 lengths from 0 to 999px | | `boxShadow` | `none`, or up to two shadows with offsets up to 24px, blur up to 24px and spread up to 4px | | `padding` | Up to 4 lengths from 0 to 32px | | `fontFamily` | Same as the variable | | `fontSize` | From 10 to 24px | | `fontWeight` | `normal`, `bold` or from `300` to `800` | | `letterSpacing` | From -2 to 4px | | `lineHeight` | From `1` to `2` unitless, or from 12 to 36px | | `textTransform` | `none`, `uppercase`, `lowercase` or `capitalize` | | `transition` | Up to 4 of `color`, `background-color`, `border-color` or `box-shadow`, with the duration in ms | A length is `0` or a number in `px`, `rem` or `em` (`rem` and `em` count as 16px). Not accepted: `url()`, `var()`, `!important`, attribute, descendant or sibling selectors, or properties that change the layout (`position`, `display`, `width`, `height`, `margin`, `transform` and similar). Limits: 40 selectors, 12 properties per selector and 120 characters per value. A rule with `color` or `backgroundColor` contrast below 3:1 is discarded. In older browsers (before Safari 16.4), rules do not apply and only the variables are used. ## Precautions - Always load the script from `https://checkout.ninepay.com.br`, never from a copy or a CDN: the iframe's origin is the one the script came from. For the sandbox, pass `Ninepay({ origin: 'https://...' })`; only `https` is accepted (or `http://localhost`, for testing). - The page's CSP must allow the checkout: `script-src` and `frame-src` with `https://checkout.ninepay.com.br`. - Display any data that comes from the iframe as text, never in `innerHTML`. - The script does not use SRI, because it receives fixes at the same address. If you need SRI, ask NinePay for a frozen copy. - Do not mount the checkout inside another iframe. If your page puts `sandbox` on a surrounding iframe, include `allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox` (the bank slip PDF opens in another tab). - The iframe limits confirmations (one at a time and five per minute), but protection against card testing is NinePay's, on the server. --- # External Checkout Source: https://docs.ninepay.com.br/docs/external-checkout The external checkout lets you process payments, links and subscriptions without building a checkout on your website or app. ## How it works The flow is very simple: you send the customer to a NinePay page where they make the payment and/or subscribe, and NinePay notifies your system via webhook! ## Example: creating a subscription with predefined customer data When creating a subscription, NinePay needs to collect basic customer information such as name and email. Sometimes you will prefer your own system to send this information instead of letting the customer fill it in at checkout, since they may make mistakes along the way. For that, we recommend that, before sending the customer to the external checkout, your system makes a call to create this customer's record in NinePay. Then, with their ID in your system, you can use the Create a Subscription resource already linked directly to this customer, and they will only need to fill in the payment details at checkout. > 👍 Tip > > Another advantage of this flow is that, with the customer ID saved in your system beforehand, you can receive and process notifications/webhooks more easily. ## Using the endpoints to create a subscription with predefined customer data 1. Use the [create plan endpoint](/reference/criar-novo-plano) or create one in the dashboard. You need a plan to create a subscription, because every subscription is linked to a plan. 2. Use the [create customer endpoint](/reference/criar-novo-cliente). Every subscription has a linked customer. 3. Use the [create subscription endpoint](/reference/criar-nova-assinatura). In this endpoint's success response, you will receive a `uuid` field in the subscription data. Use this value to redirect the user to the checkout page: - in production: `https://checkout.ninepay.com.br/subscription//activate`; - in the sandbox: `https://app.sandbox.cefispay.com.br/subscription//activate`. > 📘 Note > > Replace `` with the value received when creating the subscription. --- # Criar novo pagamento Source: https://docs.ninepay.com.br/reference/criar-novo-pagamento Realiza uma cobrança via cartão de crédito, boleto bancário ou PIX. **Nota:** Pagamentos com cartão devem incluir um hash de cartão (se você ainda não sabe como obter um, consulte a sessão [Hash de cartões](/referencia/ninepay-api#encriptando-os-dados-do-cartão)) ou os dados do cartão. Dê sempre preferência ao método de hash por ser uma alternativa mais segura. **Cartão de crédito:** o cliente do pagamento (informado em `customer` ou já cadastrado) precisa ter CPF ou CNPJ, telefone, CEP e número do endereço. Esses dados são enviados à adquirente como dados do titular, e a cobrança é recusada se algum deles estiver faltando. Recomendamos cadastrar o cliente com o máximo de informações possível, mesmo que hoje você só cobre por boleto ou Pix. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payments": { "post": { "summary": "Criar novo pagamento", "tags": [ "Pagamentos" ], "description": "Realiza uma cobrança via cartão de crédito, boleto bancário ou PIX. **Nota:** Pagamentos com cartão devem incluir um hash de cartão (se você ainda não sabe como obter um, consulte a sessão [Hash de cartões](/referencia/ninepay-api#encriptando-os-dados-do-cartão)) ou os dados do cartão. Dê sempre preferência ao método de hash por ser uma alternativa mais segura.\n**Cartão de crédito:** o cliente do pagamento (informado em `customer` ou já cadastrado) precisa ter CPF ou CNPJ, telefone, CEP e número do endereço. Esses dados são enviados à adquirente como dados do titular, e a cobrança é recusada se algum deles estiver faltando. Recomendamos cadastrar o cliente com o máximo de informações possível, mesmo que hoje você só cobre por boleto ou Pix.\n", "requestBody": { "$ref": "#/components/requestBodies/PaymentBody" }, "responses": { "200": { "description": "Pagamento criado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentStoreResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "operationId": "createPayment" } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PaymentStoreResponse": { "type": "object", "properties": { "payment": { "$ref": "#/components/schemas/Payment" }, "customer": { "$ref": "#/components/schemas/Customer" } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "Payment": { "type": "object", "properties": { "id": { "description": "The payment id", "type": "integer", "example": 42 }, "uuid": { "description": "Public payment id", "type": "string", "format": "uuid", "example": "8b2cad02-06f0-42be-84ac-101b23a1bb0d" }, "value": { "description": "Payment value in cents", "type": "integer", "minLength": 1, "example": 5000 }, "due_date": { "description": "Payment due date", "type": "string", "format": "date", "example": "2017-07-21" } } }, "PaymentBody": { "type": "object", "required": [ "method", "customer" ], "properties": { "method": { "description": "O método que será usado para este pagamento. Posteriormente, caso necessário, este pagamento pode ser pago com outros métodos.", "$ref": "#/components/schemas/PaymentMethods", "example": "credit" }, "value": { "description": "O valor do pagamento em centavos. Por exemplo, para R$50,00, o `value` deve ser 5000. Obrigatório se os `items` não forem informados. Se os `items` forem especificado para o pagamento, a soma do valor dos items será usada, considerando as quantidades, e este campo será ignorado.", "type": "string" }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: para multa de 2% sobre o valor do pagamento vencido, envie 2." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual. Ex.: para 2% ao mês, envie 2." }, "custom_data": { "description": "Objeto no formato `key-value` para armazenar juntamente ao pagamento.", "$ref": "#/components/schemas/CustomDataItem" }, "customer": { "description": "Os dados do cliente", "$ref": "#/components/schemas/Customer" }, "items": { "description": "Os items que compõe esse pagamento. Obrigatório se o `value` não for informado. Se os `items` forem especificado para o pagamento, a soma do valor dos items será usada, considerando as quantidades, e o `value` será ignorado mesmo que esteja presente.", "type": "array", "items": { "$ref": "#/components/schemas/PaymentItem" } }, "fraud_prevention": { "description": "Os dados referentes a análise antifraude, se a empresa tiver contratado este serviço.", "$ref": "#/components/schemas/FraudPrevention" }, "card_hash": { "description": "O hash encriptado dos dados do cartão. Para entender o fluxo de criptografia consulte a documentação para [`hash de cartões`](/referencia/ninepay-api#encriptando-os-dados-do-cartão). Obrigatório se o método é `credit` e o `card` não foi informado.", "type": "string", "example": "32_wbFpoccZsXLzRTU8QojcGR5E6Sd" }, "card": { "description": "Os dados do cartão em formato puro. Atenção, para trafegar dados neste formato a sua infraestrutura deve ser PCI compliance. Obrigatório se o método é `credit` e o `card_hash` não foi informado.", "$ref": "#/components/schemas/Card" }, "installments": { "description": "Se o método é `credit`, em quantas parcelas a compra deve ser processada.", "type": "integer", "minimum": 1, "maximum": 12 }, "due_date": { "description": "A data de vencimento da cobrança. É utilizada para o vencimento do boleto e do pix. Obrigatório se o método é `billet`.", "type": "string", "format": "date", "example": "2027-07-21" } } }, "Card": { "type": "object", "properties": { "number": { "description": "O número do cartão", "type": "string", "example": 5520392774513817 }, "holder": { "description": "O nome do portador conforme escrito no cartão", "type": "string", "example": "Jhon Doe" }, "expiration": { "description": "A expiração do cartão", "type": "string", "example": "06/2029" }, "cvv": { "description": "o código de verificação conforme escrito no cartão", "type": "string", "example": 123 } } }, "FraudPrevention": { "type": "object", "properties": { "session_id": { "description": "O id da sessão gerado pelo script de monitoramento", "type": "string", "example": "EoXa4RJP3bdGk" }, "ip": { "description": "O IP de onde o usuário fez o pedido", "type": "string", "example": "163.39.202.130" } } }, "PaymentItem": { "type": "object", "required": [ "name", "value", "amount" ], "properties": { "name": { "type": "string", "description": "O nome do item adicional", "example": "Serviço adicional de consultoria" }, "value": { "type": "integer", "description": "O valor desse item adicional. Por exemplo, para R$20,00, o `value` deve ser 2000", "example": 2000 }, "amount": { "type": "integer", "description": "A quantidade do item adicional", "example": 2 } } }, "CustomDataItem": { "type": "object", "additionalProperties": true, "example": { "rg": "21.526.170-7" } }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] } }, "requestBodies": { "PaymentBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição. **Atenção**: alguns dados deixam de ser e tornam obrigatórios de acordo com outros dados enviados juntos com a requisição. Leia atentamente a descrição de todos os campos.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentBody" } } } } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Atualizar pagamento existente Source: https://docs.ninepay.com.br/reference/atualizar-pagamento-existente Atualiza um pagamento existente com base no ID fornecido. **Nota:** É necessário fornecer o ID do pagamento que deseja atualizar. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payments/{id}": { "put": { "summary": "Atualizar pagamento existente", "tags": [ "Pagamentos" ], "description": "Atualiza um pagamento existente com base no ID fornecido. **Nota:** É necessário fornecer o ID do pagamento que deseja atualizar.\n", "requestBody": { "$ref": "#/components/requestBodies/PaymentBody" }, "responses": { "200": { "description": "Pagamento atualizado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentUpdateResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PaymentUpdateResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do pagamento." }, "uuid": { "type": "string", "description": "O UUID do pagamento." }, "customer_id": { "type": "string", "description": "O ID do cliente do pagamento." }, "customer": { "type": "object", "description": "O cliente do pagamento." }, "subscription_id": { "type": "string", "description": "O ID da assinatura do pagamento." }, "subscription": { "type": "object", "description": "A assinatura do pagamento." }, "payment_link": { "type": "object", "description": "O link de pagamento." }, "items": { "type": "array", "description": "Os itens do pagamento." }, "allowed_methods": { "type": "array", "description": "Os métodos com que o cliente pode pagar este pagamento.", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "method": { "$ref": "#/components/schemas/PaymentResponseMethods" }, "card_id": { "type": "string", "description": "O ID do cartão do pagamento." }, "card": { "type": "object", "description": "O cartão do pagamento." }, "digitable_line": { "type": "string", "description": "A linha digitável do boleto. Só é preenchida para o método `billet`." }, "pix_payload": { "type": "string", "description": "O código Pix copia e cola do pagamento. Só é preenchido para o método `pix`." }, "custom_data": { "type": "object", "description": "Os dados personalizados do pagamento." }, "value": { "type": "number", "description": "O valor original do pagamento em centavos, sem multa e juros. Ex.: 5000 para R$50,00." }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: 2 para multa de 2% sobre o valor do pagamento vencido." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual ao mês. Ex.: 2 para 2% ao mês." }, "current_value": { "type": "number", "description": "O valor a ser pago hoje, em centavos: `value` somado a `current_fine` e `current_interest`." }, "current_fine": { "type": "number", "description": "O valor da multa aplicada até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "current_interest": { "type": "number", "description": "O valor dos juros acumulados até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "installments": { "type": "number", "description": "O número de parcelas do pagamento. Só se aplica ao método `credit`." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "retries_number": { "type": "number", "description": "O número de tentativas do pagamento." }, "was_last_retry": { "type": "boolean", "description": "Se foi a última tentativa do pagamento." }, "can_refund": { "type": "boolean", "description": "Se o pagamento pode ser reembolsado agora. Só é `true` quando o `status` é `paid` e o método é `credit` (até 350 dias após o pagamento) ou `pix` (até 90 dias após o pagamento)." }, "refunds": { "type": "array", "description": "Os reembolsos solicitados para o pagamento. Só é retornado no endpoint de obter detalhes do pagamento quando a requisição inclui `with[]=refunds`.", "items": { "$ref": "#/components/schemas/PaymentRefund" } }, "receipts": { "type": "array", "description": "Os recibos do pagamento." }, "receipt_status": { "$ref": "#/components/schemas/ReceiptStatus" }, "due_date": { "type": "string", "format": "date", "description": "A data de vencimento do pagamento." }, "ref_date": { "type": "string", "format": "date", "description": "A data de referência do pagamento." }, "paid_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi pago. É nula enquanto o `status` não é `paid`." }, "company": { "type": "object", "description": "A empresa do pagamento." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do pagamento." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi atualizado." }, "url": { "type": "string", "description": "A URL do pagamento." }, "receipt_url": { "type": "string", "description": "A URL do recibo do pagamento." } } }, "ReceiptStatus": { "type": "integer", "enum": [ 0, 1, 2, 3, 4 ], "description": "- 0: Emissão de recibo não configurada. - 1: Recibo ainda não emitido. - 2: Recibo emitido. - 3: Erro na emissão do recibo. - 4: Emissão do recibo ignorada.\n" }, "PaymentRefund": { "type": "object", "properties": { "id": { "type": "integer", "description": "O ID do reembolso." }, "method": { "type": "string", "description": "O método do pagamento reembolsado: `credit` ou `pix`." }, "value": { "type": "number", "description": "O valor reembolsado, em centavos." }, "status": { "$ref": "#/components/schemas/RefundStatus" }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o reembolso foi solicitado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora da última atualização do reembolso." } } }, "RefundStatus": { "type": "string", "enum": [ "new", "processing", "completed", "failed" ], "description": "- new: Reembolso criado, ainda não enviado à adquirente. - processing: Reembolso em processamento na adquirente. - completed: Reembolso concluído; o valor foi devolvido ao cliente. - failed: A adquirente não conseguiu fazer o reembolso.\n" }, "PaymentStatus": { "type": "string", "enum": [ "unpaid", "paid", "unauthorized", "canceled", "reversed" ], "description": "- unpaid: Em aberto, aguardando pagamento. - paid: Pago. - unauthorized: Recusado. A tentativa de cobrança no cartão não foi autorizada; o pagamento continua em aberto e pode ser pago novamente. - canceled: Cancelado. - reversed: Estornado.\n" }, "PaymentResponseMethods": { "type": "string", "enum": [ "credit", "debit", "billet", "pix", "payment_initiation", "manual" ], "description": "- credit: Cartão de crédito. - debit: Cartão de débito. - billet: Boleto. - pix: Pix. - payment_initiation: Iniciação de pagamento via Open Finance. - manual: Baixa manual, registrada pelo endpoint de dar baixa em pagamento.\n" }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] }, "PaymentBody": { "type": "object", "required": [ "method", "customer" ], "properties": { "method": { "description": "O método que será usado para este pagamento. Posteriormente, caso necessário, este pagamento pode ser pago com outros métodos.", "$ref": "#/components/schemas/PaymentMethods", "example": "credit" }, "value": { "description": "O valor do pagamento em centavos. Por exemplo, para R$50,00, o `value` deve ser 5000. Obrigatório se os `items` não forem informados. Se os `items` forem especificado para o pagamento, a soma do valor dos items será usada, considerando as quantidades, e este campo será ignorado.", "type": "string" }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: para multa de 2% sobre o valor do pagamento vencido, envie 2." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual. Ex.: para 2% ao mês, envie 2." }, "custom_data": { "description": "Objeto no formato `key-value` para armazenar juntamente ao pagamento.", "$ref": "#/components/schemas/CustomDataItem" }, "customer": { "description": "Os dados do cliente", "$ref": "#/components/schemas/Customer" }, "items": { "description": "Os items que compõe esse pagamento. Obrigatório se o `value` não for informado. Se os `items` forem especificado para o pagamento, a soma do valor dos items será usada, considerando as quantidades, e o `value` será ignorado mesmo que esteja presente.", "type": "array", "items": { "$ref": "#/components/schemas/PaymentItem" } }, "fraud_prevention": { "description": "Os dados referentes a análise antifraude, se a empresa tiver contratado este serviço.", "$ref": "#/components/schemas/FraudPrevention" }, "card_hash": { "description": "O hash encriptado dos dados do cartão. Para entender o fluxo de criptografia consulte a documentação para [`hash de cartões`](/referencia/ninepay-api#encriptando-os-dados-do-cartão). Obrigatório se o método é `credit` e o `card` não foi informado.", "type": "string", "example": "32_wbFpoccZsXLzRTU8QojcGR5E6Sd" }, "card": { "description": "Os dados do cartão em formato puro. Atenção, para trafegar dados neste formato a sua infraestrutura deve ser PCI compliance. Obrigatório se o método é `credit` e o `card_hash` não foi informado.", "$ref": "#/components/schemas/Card" }, "installments": { "description": "Se o método é `credit`, em quantas parcelas a compra deve ser processada.", "type": "integer", "minimum": 1, "maximum": 12 }, "due_date": { "description": "A data de vencimento da cobrança. É utilizada para o vencimento do boleto e do pix. Obrigatório se o método é `billet`.", "type": "string", "format": "date", "example": "2027-07-21" } } }, "Card": { "type": "object", "properties": { "number": { "description": "O número do cartão", "type": "string", "example": 5520392774513817 }, "holder": { "description": "O nome do portador conforme escrito no cartão", "type": "string", "example": "Jhon Doe" }, "expiration": { "description": "A expiração do cartão", "type": "string", "example": "06/2029" }, "cvv": { "description": "o código de verificação conforme escrito no cartão", "type": "string", "example": 123 } } }, "FraudPrevention": { "type": "object", "properties": { "session_id": { "description": "O id da sessão gerado pelo script de monitoramento", "type": "string", "example": "EoXa4RJP3bdGk" }, "ip": { "description": "O IP de onde o usuário fez o pedido", "type": "string", "example": "163.39.202.130" } } }, "PaymentItem": { "type": "object", "required": [ "name", "value", "amount" ], "properties": { "name": { "type": "string", "description": "O nome do item adicional", "example": "Serviço adicional de consultoria" }, "value": { "type": "integer", "description": "O valor desse item adicional. Por exemplo, para R$20,00, o `value` deve ser 2000", "example": 2000 }, "amount": { "type": "integer", "description": "A quantidade do item adicional", "example": 2 } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "CustomDataItem": { "type": "object", "additionalProperties": true, "example": { "rg": "21.526.170-7" } } }, "requestBodies": { "PaymentBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição. **Atenção**: alguns dados deixam de ser e tornam obrigatórios de acordo com outros dados enviados juntos com a requisição. Leia atentamente a descrição de todos os campos.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentBody" } } } } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Obter detalhes do pagamento Source: https://docs.ninepay.com.br/reference/obter-detalhes-do-pagamento Retorna os detalhes de um pagamento específico, com base no ID fornecido. **Nota:** É necessário fornecer o ID do pagamento para obter os detalhes. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payments/{id}": { "get": { "summary": "Obter detalhes do pagamento", "tags": [ "Pagamentos" ], "description": "Retorna os detalhes de um pagamento específico, com base no ID fornecido. **Nota:** É necessário fornecer o ID do pagamento para obter os detalhes.\n", "parameters": [ { "name": "id", "in": "path", "required": true, "description": "O ID do pagamento.", "schema": { "type": "integer" } }, { "name": "with[]", "in": "query", "required": false, "description": "Os dados relacionados a incluir na resposta. Envie o parâmetro uma vez para cada valor. Ex.: `?with[]=customer&with[]=refunds`. Use `refunds` para acompanhar um reembolso.", "style": "form", "explode": true, "schema": { "type": "array", "items": { "type": "string", "enum": [ "customer", "subscription", "card", "payment_link", "payment_link.custom_fields", "items", "refunds", "booklet" ] } } } ], "responses": { "200": { "description": "Detalhes do pagamento retornados com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentDetailsResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "operationId": "getPayment" } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PaymentDetailsResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do pagamento." }, "uuid": { "type": "string", "description": "O UUID do pagamento." }, "customer_id": { "type": "string", "description": "O ID do cliente do pagamento." }, "customer": { "type": "object", "description": "O cliente do pagamento." }, "subscription_id": { "type": "string", "description": "O ID da assinatura do pagamento." }, "subscription": { "type": "object", "description": "A assinatura do pagamento." }, "payment_link": { "type": "object", "description": "O link de pagamento." }, "items": { "type": "array", "description": "Os itens do pagamento." }, "allowed_methods": { "type": "array", "description": "Os métodos com que o cliente pode pagar este pagamento.", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "method": { "$ref": "#/components/schemas/PaymentResponseMethods" }, "card_id": { "type": "string", "description": "O ID do cartão do pagamento." }, "card": { "type": "object", "description": "O cartão do pagamento." }, "digitable_line": { "type": "string", "description": "A linha digitável do boleto. Só é preenchida para o método `billet`." }, "pix_payload": { "type": "string", "description": "O código Pix copia e cola do pagamento. Só é preenchido para o método `pix`." }, "custom_data": { "type": "object", "description": "Os dados personalizados do pagamento." }, "value": { "type": "number", "description": "O valor original do pagamento em centavos, sem multa e juros. Ex.: 5000 para R$50,00." }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: 2 para multa de 2% sobre o valor do pagamento vencido." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual ao mês. Ex.: 2 para 2% ao mês." }, "current_value": { "type": "number", "description": "O valor a ser pago hoje, em centavos: `value` somado a `current_fine` e `current_interest`." }, "current_fine": { "type": "number", "description": "O valor da multa aplicada até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "current_interest": { "type": "number", "description": "O valor dos juros acumulados até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "installments": { "type": "number", "description": "O número de parcelas do pagamento. Só se aplica ao método `credit`." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "retries_number": { "type": "number", "description": "O número de tentativas do pagamento." }, "was_last_retry": { "type": "boolean", "description": "Se foi a última tentativa do pagamento." }, "can_refund": { "type": "boolean", "description": "Se o pagamento pode ser reembolsado agora. Só é `true` quando o `status` é `paid` e o método é `credit` (até 350 dias após o pagamento) ou `pix` (até 90 dias após o pagamento)." }, "refunds": { "type": "array", "description": "Os reembolsos solicitados para o pagamento. Só é retornado no endpoint de obter detalhes do pagamento quando a requisição inclui `with[]=refunds`.", "items": { "$ref": "#/components/schemas/PaymentRefund" } }, "receipts": { "type": "array", "description": "Os recibos do pagamento." }, "receipt_status": { "$ref": "#/components/schemas/ReceiptStatus" }, "due_date": { "type": "string", "format": "date", "description": "A data de vencimento do pagamento." }, "ref_date": { "type": "string", "format": "date", "description": "A data de referência do pagamento." }, "paid_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi pago. É nula enquanto o `status` não é `paid`." }, "company": { "type": "object", "description": "A empresa do pagamento." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do pagamento." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi atualizado." }, "url": { "type": "string", "description": "A URL do pagamento." }, "receipt_url": { "type": "string", "description": "A URL do recibo do pagamento." } } }, "ReceiptStatus": { "type": "integer", "enum": [ 0, 1, 2, 3, 4 ], "description": "- 0: Emissão de recibo não configurada. - 1: Recibo ainda não emitido. - 2: Recibo emitido. - 3: Erro na emissão do recibo. - 4: Emissão do recibo ignorada.\n" }, "PaymentRefund": { "type": "object", "properties": { "id": { "type": "integer", "description": "O ID do reembolso." }, "method": { "type": "string", "description": "O método do pagamento reembolsado: `credit` ou `pix`." }, "value": { "type": "number", "description": "O valor reembolsado, em centavos." }, "status": { "$ref": "#/components/schemas/RefundStatus" }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o reembolso foi solicitado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora da última atualização do reembolso." } } }, "RefundStatus": { "type": "string", "enum": [ "new", "processing", "completed", "failed" ], "description": "- new: Reembolso criado, ainda não enviado à adquirente. - processing: Reembolso em processamento na adquirente. - completed: Reembolso concluído; o valor foi devolvido ao cliente. - failed: A adquirente não conseguiu fazer o reembolso.\n" }, "PaymentStatus": { "type": "string", "enum": [ "unpaid", "paid", "unauthorized", "canceled", "reversed" ], "description": "- unpaid: Em aberto, aguardando pagamento. - paid: Pago. - unauthorized: Recusado. A tentativa de cobrança no cartão não foi autorizada; o pagamento continua em aberto e pode ser pago novamente. - canceled: Cancelado. - reversed: Estornado.\n" }, "PaymentResponseMethods": { "type": "string", "enum": [ "credit", "debit", "billet", "pix", "payment_initiation", "manual" ], "description": "- credit: Cartão de crédito. - debit: Cartão de débito. - billet: Boleto. - pix: Pix. - payment_initiation: Iniciação de pagamento via Open Finance. - manual: Baixa manual, registrada pelo endpoint de dar baixa em pagamento.\n" }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Cancelar um pagamento Source: https://docs.ninepay.com.br/reference/cancelar-um-pagamento Cancela o pagamento com base no ID fornecido. Um pagamento cancelado não gera mais envio de comunicações ao cliente. Caso seja um pagamento de assinatura, o sistema irá considerar o pagamento como pago a fim de determinar o status da assinatura. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payments/{id}": { "delete": { "summary": "Cancelar um pagamento", "tags": [ "Pagamentos" ], "description": "Cancela o pagamento com base no ID fornecido. Um pagamento cancelado não gera mais envio de comunicações ao cliente. Caso seja um pagamento de assinatura, o sistema irá considerar o pagamento como pago a fim de determinar o status da assinatura.\n", "parameters": [ { "name": "id", "in": "query", "required": true, "description": "O id do pagamento a ser cancelado", "schema": { "type": "integer" } }, { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Pagamento cancelado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentDetailsResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PaymentDetailsResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do pagamento." }, "uuid": { "type": "string", "description": "O UUID do pagamento." }, "customer_id": { "type": "string", "description": "O ID do cliente do pagamento." }, "customer": { "type": "object", "description": "O cliente do pagamento." }, "subscription_id": { "type": "string", "description": "O ID da assinatura do pagamento." }, "subscription": { "type": "object", "description": "A assinatura do pagamento." }, "payment_link": { "type": "object", "description": "O link de pagamento." }, "items": { "type": "array", "description": "Os itens do pagamento." }, "allowed_methods": { "type": "array", "description": "Os métodos com que o cliente pode pagar este pagamento.", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "method": { "$ref": "#/components/schemas/PaymentResponseMethods" }, "card_id": { "type": "string", "description": "O ID do cartão do pagamento." }, "card": { "type": "object", "description": "O cartão do pagamento." }, "digitable_line": { "type": "string", "description": "A linha digitável do boleto. Só é preenchida para o método `billet`." }, "pix_payload": { "type": "string", "description": "O código Pix copia e cola do pagamento. Só é preenchido para o método `pix`." }, "custom_data": { "type": "object", "description": "Os dados personalizados do pagamento." }, "value": { "type": "number", "description": "O valor original do pagamento em centavos, sem multa e juros. Ex.: 5000 para R$50,00." }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: 2 para multa de 2% sobre o valor do pagamento vencido." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual ao mês. Ex.: 2 para 2% ao mês." }, "current_value": { "type": "number", "description": "O valor a ser pago hoje, em centavos: `value` somado a `current_fine` e `current_interest`." }, "current_fine": { "type": "number", "description": "O valor da multa aplicada até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "current_interest": { "type": "number", "description": "O valor dos juros acumulados até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "installments": { "type": "number", "description": "O número de parcelas do pagamento. Só se aplica ao método `credit`." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "retries_number": { "type": "number", "description": "O número de tentativas do pagamento." }, "was_last_retry": { "type": "boolean", "description": "Se foi a última tentativa do pagamento." }, "can_refund": { "type": "boolean", "description": "Se o pagamento pode ser reembolsado agora. Só é `true` quando o `status` é `paid` e o método é `credit` (até 350 dias após o pagamento) ou `pix` (até 90 dias após o pagamento)." }, "refunds": { "type": "array", "description": "Os reembolsos solicitados para o pagamento. Só é retornado no endpoint de obter detalhes do pagamento quando a requisição inclui `with[]=refunds`.", "items": { "$ref": "#/components/schemas/PaymentRefund" } }, "receipts": { "type": "array", "description": "Os recibos do pagamento." }, "receipt_status": { "$ref": "#/components/schemas/ReceiptStatus" }, "due_date": { "type": "string", "format": "date", "description": "A data de vencimento do pagamento." }, "ref_date": { "type": "string", "format": "date", "description": "A data de referência do pagamento." }, "paid_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi pago. É nula enquanto o `status` não é `paid`." }, "company": { "type": "object", "description": "A empresa do pagamento." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do pagamento." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi atualizado." }, "url": { "type": "string", "description": "A URL do pagamento." }, "receipt_url": { "type": "string", "description": "A URL do recibo do pagamento." } } }, "ReceiptStatus": { "type": "integer", "enum": [ 0, 1, 2, 3, 4 ], "description": "- 0: Emissão de recibo não configurada. - 1: Recibo ainda não emitido. - 2: Recibo emitido. - 3: Erro na emissão do recibo. - 4: Emissão do recibo ignorada.\n" }, "PaymentRefund": { "type": "object", "properties": { "id": { "type": "integer", "description": "O ID do reembolso." }, "method": { "type": "string", "description": "O método do pagamento reembolsado: `credit` ou `pix`." }, "value": { "type": "number", "description": "O valor reembolsado, em centavos." }, "status": { "$ref": "#/components/schemas/RefundStatus" }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o reembolso foi solicitado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora da última atualização do reembolso." } } }, "RefundStatus": { "type": "string", "enum": [ "new", "processing", "completed", "failed" ], "description": "- new: Reembolso criado, ainda não enviado à adquirente. - processing: Reembolso em processamento na adquirente. - completed: Reembolso concluído; o valor foi devolvido ao cliente. - failed: A adquirente não conseguiu fazer o reembolso.\n" }, "PaymentStatus": { "type": "string", "enum": [ "unpaid", "paid", "unauthorized", "canceled", "reversed" ], "description": "- unpaid: Em aberto, aguardando pagamento. - paid: Pago. - unauthorized: Recusado. A tentativa de cobrança no cartão não foi autorizada; o pagamento continua em aberto e pode ser pago novamente. - canceled: Cancelado. - reversed: Estornado.\n" }, "PaymentResponseMethods": { "type": "string", "enum": [ "credit", "debit", "billet", "pix", "payment_initiation", "manual" ], "description": "- credit: Cartão de crédito. - debit: Cartão de débito. - billet: Boleto. - pix: Pix. - payment_initiation: Iniciação de pagamento via Open Finance. - manual: Baixa manual, registrada pelo endpoint de dar baixa em pagamento.\n" }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Dar baixa em pagamento Source: https://docs.ninepay.com.br/reference/dar-baixa-em-pagamento Marca um pagamento como pago. Utilize esta opção para registrar na NinePay um pagamento que foi feito fora do sistema, como por exemplo, um pagamento em dinheiro ou transferência bancária. Após ser marcado como pago, o cliente não receberá mais cobranças referentes a este pagamento. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payment/{id}/write-off": { "post": { "summary": "Dar baixa em pagamento", "tags": [ "Pagamentos" ], "description": "Marca um pagamento como pago. Utilize esta opção para registrar na NinePay um pagamento que foi feito fora do sistema, como por exemplo, um pagamento em dinheiro ou transferência bancária. Após ser marcado como pago, o cliente não receberá mais cobranças referentes a este pagamento.\n", "parameters": [ { "name": "id", "in": "path", "required": true, "description": "O id do pagamento a ser cancelado", "schema": { "type": "integer" } } ], "requestBody": { "$ref": "#/components/requestBodies/PaymentWriteOffBody" }, "responses": { "200": { "description": "Pagamento baixado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentUpdateResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PaymentUpdateResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do pagamento." }, "uuid": { "type": "string", "description": "O UUID do pagamento." }, "customer_id": { "type": "string", "description": "O ID do cliente do pagamento." }, "customer": { "type": "object", "description": "O cliente do pagamento." }, "subscription_id": { "type": "string", "description": "O ID da assinatura do pagamento." }, "subscription": { "type": "object", "description": "A assinatura do pagamento." }, "payment_link": { "type": "object", "description": "O link de pagamento." }, "items": { "type": "array", "description": "Os itens do pagamento." }, "allowed_methods": { "type": "array", "description": "Os métodos com que o cliente pode pagar este pagamento.", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "method": { "$ref": "#/components/schemas/PaymentResponseMethods" }, "card_id": { "type": "string", "description": "O ID do cartão do pagamento." }, "card": { "type": "object", "description": "O cartão do pagamento." }, "digitable_line": { "type": "string", "description": "A linha digitável do boleto. Só é preenchida para o método `billet`." }, "pix_payload": { "type": "string", "description": "O código Pix copia e cola do pagamento. Só é preenchido para o método `pix`." }, "custom_data": { "type": "object", "description": "Os dados personalizados do pagamento." }, "value": { "type": "number", "description": "O valor original do pagamento em centavos, sem multa e juros. Ex.: 5000 para R$50,00." }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: 2 para multa de 2% sobre o valor do pagamento vencido." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual ao mês. Ex.: 2 para 2% ao mês." }, "current_value": { "type": "number", "description": "O valor a ser pago hoje, em centavos: `value` somado a `current_fine` e `current_interest`." }, "current_fine": { "type": "number", "description": "O valor da multa aplicada até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "current_interest": { "type": "number", "description": "O valor dos juros acumulados até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "installments": { "type": "number", "description": "O número de parcelas do pagamento. Só se aplica ao método `credit`." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "retries_number": { "type": "number", "description": "O número de tentativas do pagamento." }, "was_last_retry": { "type": "boolean", "description": "Se foi a última tentativa do pagamento." }, "can_refund": { "type": "boolean", "description": "Se o pagamento pode ser reembolsado agora. Só é `true` quando o `status` é `paid` e o método é `credit` (até 350 dias após o pagamento) ou `pix` (até 90 dias após o pagamento)." }, "refunds": { "type": "array", "description": "Os reembolsos solicitados para o pagamento. Só é retornado no endpoint de obter detalhes do pagamento quando a requisição inclui `with[]=refunds`.", "items": { "$ref": "#/components/schemas/PaymentRefund" } }, "receipts": { "type": "array", "description": "Os recibos do pagamento." }, "receipt_status": { "$ref": "#/components/schemas/ReceiptStatus" }, "due_date": { "type": "string", "format": "date", "description": "A data de vencimento do pagamento." }, "ref_date": { "type": "string", "format": "date", "description": "A data de referência do pagamento." }, "paid_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi pago. É nula enquanto o `status` não é `paid`." }, "company": { "type": "object", "description": "A empresa do pagamento." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do pagamento." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi atualizado." }, "url": { "type": "string", "description": "A URL do pagamento." }, "receipt_url": { "type": "string", "description": "A URL do recibo do pagamento." } } }, "ReceiptStatus": { "type": "integer", "enum": [ 0, 1, 2, 3, 4 ], "description": "- 0: Emissão de recibo não configurada. - 1: Recibo ainda não emitido. - 2: Recibo emitido. - 3: Erro na emissão do recibo. - 4: Emissão do recibo ignorada.\n" }, "PaymentRefund": { "type": "object", "properties": { "id": { "type": "integer", "description": "O ID do reembolso." }, "method": { "type": "string", "description": "O método do pagamento reembolsado: `credit` ou `pix`." }, "value": { "type": "number", "description": "O valor reembolsado, em centavos." }, "status": { "$ref": "#/components/schemas/RefundStatus" }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o reembolso foi solicitado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora da última atualização do reembolso." } } }, "RefundStatus": { "type": "string", "enum": [ "new", "processing", "completed", "failed" ], "description": "- new: Reembolso criado, ainda não enviado à adquirente. - processing: Reembolso em processamento na adquirente. - completed: Reembolso concluído; o valor foi devolvido ao cliente. - failed: A adquirente não conseguiu fazer o reembolso.\n" }, "PaymentStatus": { "type": "string", "enum": [ "unpaid", "paid", "unauthorized", "canceled", "reversed" ], "description": "- unpaid: Em aberto, aguardando pagamento. - paid: Pago. - unauthorized: Recusado. A tentativa de cobrança no cartão não foi autorizada; o pagamento continua em aberto e pode ser pago novamente. - canceled: Cancelado. - reversed: Estornado.\n" }, "PaymentResponseMethods": { "type": "string", "enum": [ "credit", "debit", "billet", "pix", "payment_initiation", "manual" ], "description": "- credit: Cartão de crédito. - debit: Cartão de débito. - billet: Boleto. - pix: Pix. - payment_initiation: Iniciação de pagamento via Open Finance. - manual: Baixa manual, registrada pelo endpoint de dar baixa em pagamento.\n" }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] } }, "requestBodies": { "PaymentWriteOffBody": { "required": false, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "type": "object", "properties": { "valueType": { "type": "string", "enum": [ "normal", "zero" ], "description": "- normal: Mantém o valor do pagamento\n- zero: Zera o valor do pagamento (não será somado aos gráficos de faturamento)\n" } } } } } } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Reembolsar um pagamento Source: https://docs.ninepay.com.br/reference/reembolsar-um-pagamento Solicita o reembolso (estorno) integral de um pagamento já pago. O valor é devolvido ao cliente pelo mesmo meio em que ele pagou. Um pagamento só pode ser reembolsado quando: - o `status` é `paid`; - o `method` é `credit` ou `pix` (boleto e baixa manual não podem ser reembolsados); - o prazo não expirou: até 350 dias após o pagamento para `credit` e até 90 dias para `pix`; - não existe outro reembolso deste pagamento em andamento; - a sua conta tem saldo suficiente para cobrir o valor do pagamento que já foi liberado. O campo `can_refund` do pagamento indica se ele atende a essas condições, com exceção do saldo e de um reembolso em andamento. Consulte-o antes de chamar este endpoint. **Resultado:** uma resposta 200 quer dizer que o reembolso foi solicitado à adquirente, não que ele foi concluído. Confira o `status` do pagamento na resposta: - `reversed`: a adquirente concluiu o reembolso na hora. - `paid`: o reembolso ficou em processamento ou foi recusado pela adquirente. Para saber qual dos dois, consulte o pagamento no endpoint de obter detalhes do pagamento com `with[]=refunds` e veja o `status` do reembolso mais recente em `refunds`. Reembolsos em processamento são consultados na adquirente a cada 5 minutos. Quando um deles é concluído, o `status` do pagamento passa a ser `reversed` e o evento de pagamento atualizado é disparado. Um reembolso com `status` `failed` não é tentado de novo; você pode solicitar um novo reembolso por este endpoint. **Atenção:** quando o pagamento não pode ser reembolsado, a API responde com o status HTTP 500 e o código `UNEXPECTED_ERROR`, sem detalhar o motivo em produção. Verifique as condições acima antes de tentar novamente. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payment/reversal": { "post": { "summary": "Reembolsar um pagamento", "tags": [ "Pagamentos" ], "description": "Solicita o reembolso (estorno) integral de um pagamento já pago. O valor é devolvido ao cliente pelo mesmo meio em que ele pagou.\n\nUm pagamento só pode ser reembolsado quando:\n- o `status` é `paid`;\n- o `method` é `credit` ou `pix` (boleto e baixa manual não podem ser reembolsados);\n- o prazo não expirou: até 350 dias após o pagamento para `credit` e até 90 dias para `pix`;\n- não existe outro reembolso deste pagamento em andamento;\n- a sua conta tem saldo suficiente para cobrir o valor do pagamento que já foi liberado.\n\nO campo `can_refund` do pagamento indica se ele atende a essas condições, com exceção do saldo e de um reembolso em andamento. Consulte-o antes de chamar este endpoint.\n\n**Resultado:** uma resposta 200 quer dizer que o reembolso foi solicitado à adquirente, não que ele foi concluído. Confira o `status` do pagamento na resposta:\n- `reversed`: a adquirente concluiu o reembolso na hora.\n- `paid`: o reembolso ficou em processamento ou foi recusado pela adquirente. Para saber qual dos dois, consulte o pagamento no endpoint de obter detalhes do pagamento com `with[]=refunds` e veja o `status` do reembolso mais recente em `refunds`.\n\nReembolsos em processamento são consultados na adquirente a cada 5 minutos. Quando um deles é concluído, o `status` do pagamento passa a ser `reversed` e o evento de pagamento atualizado é disparado. Um reembolso com `status` `failed` não é tentado de novo; você pode solicitar um novo reembolso por este endpoint.\n\n**Atenção:** quando o pagamento não pode ser reembolsado, a API responde com o status HTTP 500 e o código `UNEXPECTED_ERROR`, sem detalhar o motivo em produção. Verifique as condições acima antes de tentar novamente.\n", "requestBody": { "$ref": "#/components/requestBodies/PaymentReversalBody" }, "responses": { "200": { "description": "Reembolso solicitado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentReversalResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "403": { "description": "O pagamento não pertence à sua conta", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "FORBIDDEN_ERROR", "message": "You do not have permission to perform this action.", "timestamp": "2026-09-28T13:00:00+00:00", "request_id": "9f3c2a1e-7b4d-4c1a-9e2f-0a1b2c3d4e5f", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "O `payment.id` não foi enviado, não é um número inteiro ou não corresponde a um pagamento existente", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "The given data was invalid.", "errors": { "payment.id": [ "Payment not found." ] }, "timestamp": "2026-09-28T13:00:00+00:00", "request_id": "9f3c2a1e-7b4d-4c1a-9e2f-0a1b2c3d4e5f", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Fix the fields and try again." } } } }, "500": { "description": "O pagamento não pode ser reembolsado ou houve um erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "There was an unexpected error.", "timestamp": "2026-09-28T13:00:00+00:00", "request_id": "9f3c2a1e-7b4d-4c1a-9e2f-0a1b2c3d4e5f", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Check all request parameters. If the error persists, contact our team in ti@ninepay.com.br." } } } } }, "operationId": "refundPayment" } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PaymentReversalResponse": { "type": "object", "properties": { "response": { "type": "object", "properties": { "data": { "type": "object", "properties": { "status": { "$ref": "#/components/schemas/PaymentStatus" } } }, "payment": { "$ref": "#/components/schemas/PaymentUpdateResponse" } } } } }, "PaymentUpdateResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do pagamento." }, "uuid": { "type": "string", "description": "O UUID do pagamento." }, "customer_id": { "type": "string", "description": "O ID do cliente do pagamento." }, "customer": { "type": "object", "description": "O cliente do pagamento." }, "subscription_id": { "type": "string", "description": "O ID da assinatura do pagamento." }, "subscription": { "type": "object", "description": "A assinatura do pagamento." }, "payment_link": { "type": "object", "description": "O link de pagamento." }, "items": { "type": "array", "description": "Os itens do pagamento." }, "allowed_methods": { "type": "array", "description": "Os métodos com que o cliente pode pagar este pagamento.", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "method": { "$ref": "#/components/schemas/PaymentResponseMethods" }, "card_id": { "type": "string", "description": "O ID do cartão do pagamento." }, "card": { "type": "object", "description": "O cartão do pagamento." }, "digitable_line": { "type": "string", "description": "A linha digitável do boleto. Só é preenchida para o método `billet`." }, "pix_payload": { "type": "string", "description": "O código Pix copia e cola do pagamento. Só é preenchido para o método `pix`." }, "custom_data": { "type": "object", "description": "Os dados personalizados do pagamento." }, "value": { "type": "number", "description": "O valor original do pagamento em centavos, sem multa e juros. Ex.: 5000 para R$50,00." }, "fine": { "type": "number", "description": "A multa do pagamento, em valor percentual. Ex.: 2 para multa de 2% sobre o valor do pagamento vencido." }, "interest": { "type": "number", "description": "O juros do pagamento, em valor percentual ao mês. Ex.: 2 para 2% ao mês." }, "current_value": { "type": "number", "description": "O valor a ser pago hoje, em centavos: `value` somado a `current_fine` e `current_interest`." }, "current_fine": { "type": "number", "description": "O valor da multa aplicada até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "current_interest": { "type": "number", "description": "O valor dos juros acumulados até hoje, em centavos. É 0 enquanto o pagamento não vence." }, "installments": { "type": "number", "description": "O número de parcelas do pagamento. Só se aplica ao método `credit`." }, "status": { "$ref": "#/components/schemas/PaymentStatus" }, "retries_number": { "type": "number", "description": "O número de tentativas do pagamento." }, "was_last_retry": { "type": "boolean", "description": "Se foi a última tentativa do pagamento." }, "can_refund": { "type": "boolean", "description": "Se o pagamento pode ser reembolsado agora. Só é `true` quando o `status` é `paid` e o método é `credit` (até 350 dias após o pagamento) ou `pix` (até 90 dias após o pagamento)." }, "refunds": { "type": "array", "description": "Os reembolsos solicitados para o pagamento. Só é retornado no endpoint de obter detalhes do pagamento quando a requisição inclui `with[]=refunds`.", "items": { "$ref": "#/components/schemas/PaymentRefund" } }, "receipts": { "type": "array", "description": "Os recibos do pagamento." }, "receipt_status": { "$ref": "#/components/schemas/ReceiptStatus" }, "due_date": { "type": "string", "format": "date", "description": "A data de vencimento do pagamento." }, "ref_date": { "type": "string", "format": "date", "description": "A data de referência do pagamento." }, "paid_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi pago. É nula enquanto o `status` não é `paid`." }, "company": { "type": "object", "description": "A empresa do pagamento." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do pagamento." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o pagamento foi atualizado." }, "url": { "type": "string", "description": "A URL do pagamento." }, "receipt_url": { "type": "string", "description": "A URL do recibo do pagamento." } } }, "ReceiptStatus": { "type": "integer", "enum": [ 0, 1, 2, 3, 4 ], "description": "- 0: Emissão de recibo não configurada. - 1: Recibo ainda não emitido. - 2: Recibo emitido. - 3: Erro na emissão do recibo. - 4: Emissão do recibo ignorada.\n" }, "PaymentRefund": { "type": "object", "properties": { "id": { "type": "integer", "description": "O ID do reembolso." }, "method": { "type": "string", "description": "O método do pagamento reembolsado: `credit` ou `pix`." }, "value": { "type": "number", "description": "O valor reembolsado, em centavos." }, "status": { "$ref": "#/components/schemas/RefundStatus" }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o reembolso foi solicitado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora da última atualização do reembolso." } } }, "RefundStatus": { "type": "string", "enum": [ "new", "processing", "completed", "failed" ], "description": "- new: Reembolso criado, ainda não enviado à adquirente. - processing: Reembolso em processamento na adquirente. - completed: Reembolso concluído; o valor foi devolvido ao cliente. - failed: A adquirente não conseguiu fazer o reembolso.\n" }, "PaymentStatus": { "type": "string", "enum": [ "unpaid", "paid", "unauthorized", "canceled", "reversed" ], "description": "- unpaid: Em aberto, aguardando pagamento. - paid: Pago. - unauthorized: Recusado. A tentativa de cobrança no cartão não foi autorizada; o pagamento continua em aberto e pode ser pago novamente. - canceled: Cancelado. - reversed: Estornado.\n" }, "PaymentResponseMethods": { "type": "string", "enum": [ "credit", "debit", "billet", "pix", "payment_initiation", "manual" ], "description": "- credit: Cartão de crédito. - debit: Cartão de débito. - billet: Boleto. - pix: Pix. - payment_initiation: Iniciação de pagamento via Open Finance. - manual: Baixa manual, registrada pelo endpoint de dar baixa em pagamento.\n" }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } } }, "requestBodies": { "PaymentReversalBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "type": "object", "required": [ "payment" ], "properties": { "payment": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "integer", "description": "O ID do pagamento a ser reembolsado.", "example": 42 } } } } } } } } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Obter PDF do boleto Source: https://docs.ninepay.com.br/reference/obter-pdf-do-boleto Retorna o PDF do boleto correspondente ao pagamento especificado pelo UUID especificado. **Atenção:** deve ser o UUID do pagamento, não o ID. Apenas para pagamentos que tenham boleto como método habilitado e ainda não estejam pagos. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/payment/{uuid}/invoice": { "get": { "summary": "Obter PDF do boleto", "tags": [ "Pagamentos" ], "description": "Retorna o PDF do boleto correspondente ao pagamento especificado pelo UUID especificado. **Atenção:** deve ser o UUID do pagamento, não o ID. Apenas para pagamentos que tenham boleto como método habilitado e ainda não estejam pagos.\n", "security": [], "parameters": [ { "name": "uuid", "in": "path", "required": true, "description": "O UUID do pagamento para o qual o boleto será gerado. Para obter o UUID, consulte a documentação de detalhes do pagamento.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "O boleto em PDF.", "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } } } }, "404": { "description": "Pagamento não encontrado" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "responses": { "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } } } }, "tags": [ { "name": "Pagamentos", "description": "Pagamentos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [] } ``` --- # Criar novo cliente Source: https://docs.ninepay.com.br/reference/criar-novo-cliente O cliente é uma figura essencial no fluxo de pagamentos, pois representa quem de fato irá realizar um pagamento. Ao criar um cliente na NinePay, um ID único será retornado. Recomendamos vincular este ID aos dados do usuário em seu sistema para evitar a criação de múltiplas contas na NinePay para o mesmo cliente. **Cartão de crédito:** para cobrar um cliente no cartão, o cadastro dele precisa ter CPF ou CNPJ, telefone, CEP e número do endereço. Esses dados são enviados à adquirente como dados do titular, e a cobrança é recusada se algum deles estiver faltando. Recomendamos cadastrar o cliente com o máximo de informações possível, mesmo que hoje você só cobre por boleto ou Pix. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/customers": { "post": { "summary": "Criar novo cliente", "tags": [ "Clientes" ], "description": "O cliente é uma figura essencial no fluxo de pagamentos, pois representa quem de fato irá realizar um pagamento. Ao criar um cliente na NinePay, um ID único será retornado. Recomendamos vincular este ID aos dados do usuário em seu sistema para evitar a criação de múltiplas contas na NinePay para o mesmo cliente.\n**Cartão de crédito:** para cobrar um cliente no cartão, o cadastro dele precisa ter CPF ou CNPJ, telefone, CEP e número do endereço. Esses dados são enviados à adquirente como dados do titular, e a cobrança é recusada se algum deles estiver faltando. Recomendamos cadastrar o cliente com o máximo de informações possível, mesmo que hoje você só cobre por boleto ou Pix.\n", "requestBody": { "$ref": "#/components/requestBodies/CustomerBody" }, "responses": { "200": { "description": "Cliente criado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Customer" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "operationId": "createCustomer" } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "CustomerBody": { "type": "object", "required": [ "name", "email" ], "properties": { "name": { "description": "O nome do cliente.", "type": "string" }, "email": { "description": "O email do cliente.", "type": "string" }, "cpf": { "description": "O CPF do cliente, se pessoa física. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "13690958725" }, "cnpj": { "description": "O CNPJ do cliente, se pessoa jurídica. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "32614517000167" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 95665-6556" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01001-010" }, "address": { "description": "O endereço do cliente.", "type": "string" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string" }, "complement": { "description": "O complemento do endereço do cliente.", "type": "string" }, "district": { "description": "O bairro do cliente.", "type": "string" }, "city": { "description": "A cidade do cliente.", "type": "string" }, "state": { "description": "A sigla do estado do cliente.", "type": "string", "example": "SP" }, "country": { "description": "A sigla do país do cliente.", "type": "string", "example": "BR" }, "status": { "description": "O status do cliente. `1 = Ativo` `2 = Inativo`\n", "$ref": "#/components/schemas/CustomerStatus", "example": 1 } } }, "CustomerStatus": { "type": "integer", "enum": [ 1, 2 ], "description": "- 1: Ativo - 2: Inativo\n" } }, "requestBodies": { "CustomerBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerBody" } } } } } }, "tags": [ { "name": "Clientes", "description": "Clientes" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Atualizar cliente existente Source: https://docs.ninepay.com.br/reference/atualizar-cliente-existente Atualiza um cliente existente com base no ID fornecido. **Nota:** É necessário fornecer o ID do cliente que deseja atualizar. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/customers": { "put": { "summary": "Atualizar cliente existente", "tags": [ "Clientes" ], "description": "Atualiza um cliente existente com base no ID fornecido. **Nota:** É necessário fornecer o ID do cliente que deseja atualizar.\n", "requestBody": { "$ref": "#/components/requestBodies/CustomerBody" }, "responses": { "200": { "description": "Cliente atualizado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerUpdateResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "CustomerUpdateResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do cliente." }, "name": { "type": "string", "description": "O nome do cliente." }, "email": { "type": "string", "description": "O email do cliente." }, "cc_emails": { "type": "string", "description": "Os emails CC do cliente." }, "cpf": { "type": "string", "description": "O CPF do cliente se pessoa física." }, "cnpj": { "type": "string", "description": "O CNPJ do cliente se pessoa jurídica." }, "phone": { "type": "string", "description": "O telefone do cliente." }, "cep": { "type": "string", "description": "O CEP do cliente." }, "address": { "type": "string", "description": "O endereço do cliente." }, "number": { "type": "string", "description": "O número do endereço do cliente." }, "complement": { "type": "string", "description": "O complemento do endereço do cliente." }, "district": { "type": "string", "description": "O bairro do cliente." }, "city": { "type": "string", "description": "A cidade do cliente." }, "state": { "type": "string", "description": "A sigla do estado do cliente." }, "country": { "type": "string", "description": "A sigla do país do cliente." }, "state_enrollment": { "type": "string", "description": "A inscrição estadual do cliente." }, "city_enrollment": { "type": "string", "description": "A inscrição municipal do cliente." }, "tax_regime": { "type": "string", "description": "O regime tributário do cliente." }, "special_tax_regime": { "type": "string", "description": "O regime tributário especial do cliente." }, "icms_taxpayer": { "type": "string", "description": "O contribuinte do ICMS do cliente." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o cliente foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o cliente foi atualizado." }, "receipt_config": { "type": "object", "description": "A configuração do recibo do cliente." } } }, "CustomerBody": { "type": "object", "required": [ "name", "email" ], "properties": { "name": { "description": "O nome do cliente.", "type": "string" }, "email": { "description": "O email do cliente.", "type": "string" }, "cpf": { "description": "O CPF do cliente, se pessoa física. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "13690958725" }, "cnpj": { "description": "O CNPJ do cliente, se pessoa jurídica. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "32614517000167" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 95665-6556" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01001-010" }, "address": { "description": "O endereço do cliente.", "type": "string" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string" }, "complement": { "description": "O complemento do endereço do cliente.", "type": "string" }, "district": { "description": "O bairro do cliente.", "type": "string" }, "city": { "description": "A cidade do cliente.", "type": "string" }, "state": { "description": "A sigla do estado do cliente.", "type": "string", "example": "SP" }, "country": { "description": "A sigla do país do cliente.", "type": "string", "example": "BR" }, "status": { "description": "O status do cliente. `1 = Ativo` `2 = Inativo`\n", "$ref": "#/components/schemas/CustomerStatus", "example": 1 } } }, "CustomerStatus": { "type": "integer", "enum": [ 1, 2 ], "description": "- 1: Ativo - 2: Inativo\n" } }, "requestBodies": { "CustomerBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerBody" } } } } } }, "tags": [ { "name": "Clientes", "description": "Clientes" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Obter detalhes do cliente Source: https://docs.ninepay.com.br/reference/obter-detalhes-do-cliente Retorna os detalhes de um cliente específico, com base no ID fornecido. **Nota:** É necessário fornecer o ID do cliente para obter os detalhes. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/customers": { "get": { "summary": "Obter detalhes do cliente", "tags": [ "Clientes" ], "description": "Retorna os detalhes de um cliente específico, com base no ID fornecido. **Nota:** É necessário fornecer o ID do cliente para obter os detalhes.\n", "responses": { "200": { "description": "Detalhes do cliente retornados com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomerDetailsResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "CustomerDetailsResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do cliente." }, "name": { "type": "string", "description": "O nome do cliente." }, "email": { "type": "string", "description": "O email do cliente." }, "cc_emails": { "type": "string", "description": "Os emails CC do cliente." }, "cpf": { "type": "string", "description": "O CPF do cliente se pessoa física." }, "cnpj": { "type": "string", "description": "O CNPJ do cliente se pessoa jurídica." }, "phone": { "type": "string", "description": "O telefone do cliente." }, "cep": { "type": "string", "description": "O CEP do cliente." }, "address": { "type": "string", "description": "O endereço do cliente." }, "number": { "type": "string", "description": "O número do endereço do cliente." }, "complement": { "type": "string", "description": "O complemento do endereço do cliente." }, "district": { "type": "string", "description": "O bairro do cliente." }, "city": { "type": "string", "description": "A cidade do cliente." }, "state": { "type": "string", "description": "A sigla do estado do cliente." }, "country": { "type": "string", "description": "A sigla do país do cliente." }, "state_enrollment": { "type": "string", "description": "A inscrição estadual do cliente." }, "city_enrollment": { "type": "string", "description": "A inscrição municipal do cliente." }, "tax_regime": { "type": "string", "description": "O regime tributário do cliente." }, "special_tax_regime": { "type": "string", "description": "O regime tributário especial do cliente." }, "icms_taxpayer": { "type": "string", "description": "O contribuinte do ICMS do cliente." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o cliente foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o cliente foi atualizado." }, "receipt_config": { "type": "object", "description": "A configuração do recibo do cliente." } } } } }, "tags": [ { "name": "Clientes", "description": "Clientes" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Criar novo plano Source: https://docs.ninepay.com.br/reference/criar-novo-plano Cria um novo plano que será utilizado no fluxo de pagamentos recorrente. O plano define as configurações para as assinaturas que farão parte dele, como valor, periodicidade, métodos aceitos e outras. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/plan": { "post": { "summary": "Criar novo plano", "tags": [ "Planos" ], "description": "Cria um novo plano que será utilizado no fluxo de pagamentos recorrente. O plano define as configurações para as assinaturas que farão parte dele, como valor, periodicidade, métodos aceitos e outras.\n", "requestBody": { "$ref": "#/components/requestBodies/PlanBody" }, "responses": { "200": { "description": "Plano criado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Plan" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "operationId": "createPlan" } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "Plan": { "type": "object", "properties": { "id": { "description": "O id do plano na NinePay.", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do plano.", "type": "string", "example": "Plano Recorrente Acme" }, "methods": { "description": "Os métodos de pagamento que são aceitos neste plano.", "type": "array", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "value": { "description": "O valor do plano em centavos.", "type": "number", "example": 2500 }, "interval": { "description": "O intervalo, ou seja, a periodicidade de cobrança do plano. Por exemplo, para cobrar a cada mês o intervalo é 1.", "type": "number", "example": 1 }, "interval_type": { "description": "O tipo de ciclo que será usado na contagem dos intervalos. Podendo ser dias, meses ou anos. Por exemplo, para cobrar mensalmente defina `interval` como `1` e `interval_type` como `months`", "$ref": "#/components/schemas/PlanIntervals", "example": "months" }, "dunning_id": { "description": "A régua de cobrança que será usada para enviar comunicações sobre as assinaturas criadas com este plano.", "type": "number", "example": 1 }, "created_at": { "description": "Data de criação", "type": "string", "format": "date-time", "example": "2017-07-21 05:20:00" } } }, "PlanIntervals": { "type": "string", "enum": [ "days", "months", "years" ] }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] }, "PlanBody": { "type": "object", "required": [ "name", "methods", "value", "interval", "interval_type", "dunning_id" ], "properties": { "name": { "description": "O nome do plano.", "type": "string", "example": "Plano Recorrente Acme" }, "methods": { "description": "Os métodos de pagamento que serão aceitos neste plano.", "type": "array", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "value": { "description": "O valor do plano em centavos.", "type": "number", "example": 2500 }, "status": { "description": "O status do plano.", "$ref": "#/components/schemas/PlanStatus", "example": "credit" }, "interval": { "description": "O intervalo, ou seja, a periodicidade de cobrança do plano. Por exemplo, para cobrar a cada mês o intervalo é 1.", "type": "number", "example": 1 }, "interval_type": { "description": "O tipo de ciclo que será usado na contagem dos intervalos. Podendo ser dias, meses ou anos. Por exemplo, para cobrar mensalmente defina `interval` como `1` e `interval_type` como `months`", "$ref": "#/components/schemas/PlanIntervals", "example": "months" }, "dunning_id": { "description": "Essa configuração se refere a régua de cobrança que será usada para enviar comunicações sobre as assinaturas criadas com este plano. Para testes utilize o valor `1` (régua padrão)", "type": "number", "example": 1 } } }, "PlanStatus": { "type": "string", "enum": [ "active", "inactive" ] } }, "requestBodies": { "PlanBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlanBody" } } } } } }, "tags": [ { "name": "Planos", "description": "Planos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Atualizar plano existente Source: https://docs.ninepay.com.br/reference/atualizar-plano-existente Atualiza um plano existente com base no ID fornecido. **Nota:** É necessário fornecer o ID do plano que deseja atualizar. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/plan": { "put": { "summary": "Atualizar plano existente", "tags": [ "Planos" ], "description": "Atualiza um plano existente com base no ID fornecido. **Nota:** É necessário fornecer o ID do plano que deseja atualizar.\n", "requestBody": { "$ref": "#/components/requestBodies/PlanBody" }, "responses": { "200": { "description": "Plano atualizado com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlanUpdateResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PlanUpdateResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do plano." }, "uuid": { "type": "string", "description": "O UUID do plano." }, "name": { "type": "string", "description": "O nome do plano." }, "public_name": { "type": "string", "description": "O nome público do plano." }, "methods": { "type": "array", "description": "Os métodos de pagamento do plano." }, "value": { "type": "number", "description": "O valor do plano." }, "fine": { "type": "number", "description": "A multa do plano." }, "interest": { "type": "number", "description": "O juros do plano." }, "interval": { "type": "number", "description": "O intervalo do plano." }, "interval_type": { "type": "string", "description": "O tipo de intervalo do plano." }, "charges": { "type": "number", "description": "As cobranças do plano." }, "trial_days": { "type": "number", "description": "Os dias de teste do plano." }, "retry_days": { "type": "number", "description": "Os dias de tentativa do plano." }, "automatic_cancel": { "type": "boolean", "description": "O cancelamento automático do plano." }, "url": { "type": "string", "description": "A URL do plano." }, "custom_layout_enabled": { "type": "boolean", "description": "A habilitação do layout personalizado do plano." }, "custom_layout": { "type": "string", "description": "O layout personalizado do plano." }, "redirect_uri": { "type": "string", "description": "A URI de redirecionamento do plano." }, "status": { "$ref": "#/components/schemas/PlanStatus" }, "custom_fields": { "type": "array", "description": "Os campos personalizados do plano." }, "billing_products": { "type": "array", "description": "Os produtos de cobrança do plano." }, "company": { "type": "object", "description": "A empresa do plano." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do plano." }, "billing_settings": { "type": "object", "description": "As configurações de cobrança do plano." }, "dunning": { "type": "object", "description": "O dunning do plano." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o plano foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o plano foi atualizado." } } }, "PlanStatus": { "type": "string", "enum": [ "active", "inactive" ] }, "PlanBody": { "type": "object", "required": [ "name", "methods", "value", "interval", "interval_type", "dunning_id" ], "properties": { "name": { "description": "O nome do plano.", "type": "string", "example": "Plano Recorrente Acme" }, "methods": { "description": "Os métodos de pagamento que serão aceitos neste plano.", "type": "array", "items": { "$ref": "#/components/schemas/PaymentMethods" } }, "value": { "description": "O valor do plano em centavos.", "type": "number", "example": 2500 }, "status": { "description": "O status do plano.", "$ref": "#/components/schemas/PlanStatus", "example": "credit" }, "interval": { "description": "O intervalo, ou seja, a periodicidade de cobrança do plano. Por exemplo, para cobrar a cada mês o intervalo é 1.", "type": "number", "example": 1 }, "interval_type": { "description": "O tipo de ciclo que será usado na contagem dos intervalos. Podendo ser dias, meses ou anos. Por exemplo, para cobrar mensalmente defina `interval` como `1` e `interval_type` como `months`", "$ref": "#/components/schemas/PlanIntervals", "example": "months" }, "dunning_id": { "description": "Essa configuração se refere a régua de cobrança que será usada para enviar comunicações sobre as assinaturas criadas com este plano. Para testes utilize o valor `1` (régua padrão)", "type": "number", "example": 1 } } }, "PlanIntervals": { "type": "string", "enum": [ "days", "months", "years" ] }, "PaymentMethods": { "type": "string", "enum": [ "credit", "billet", "pix" ] } }, "requestBodies": { "PlanBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlanBody" } } } } } }, "tags": [ { "name": "Planos", "description": "Planos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Obter detalhes do plano Source: https://docs.ninepay.com.br/reference/obter-detalhes-do-plano Retorna os detalhes de um plano específico, com base no ID fornecido. **Nota:** É necessário fornecer o ID do plano para obter os detalhes. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/plan": { "get": { "summary": "Obter detalhes do plano", "tags": [ "Planos" ], "description": "Retorna os detalhes de um plano específico, com base no ID fornecido. **Nota:** É necessário fornecer o ID do plano para obter os detalhes.\n", "responses": { "200": { "description": "Detalhes do plano retornados com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlanDetailsResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "PlanDetailsResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID do plano." }, "uuid": { "type": "string", "description": "O UUID do plano." }, "name": { "type": "string", "description": "O nome do plano." }, "public_name": { "type": "string", "description": "O nome público do plano." }, "methods": { "type": "array", "description": "Os métodos de pagamento do plano." }, "value": { "type": "number", "description": "O valor do plano." }, "fine": { "type": "number", "description": "A multa do plano." }, "interest": { "type": "number", "description": "O juros do plano." }, "interval": { "type": "number", "description": "O intervalo do plano." }, "interval_type": { "type": "string", "description": "O tipo de intervalo do plano." }, "charges": { "type": "number", "description": "As cobranças do plano." }, "trial_days": { "type": "number", "description": "Os dias de teste do plano." }, "retry_days": { "type": "number", "description": "Os dias de tentativa do plano." }, "automatic_cancel": { "type": "boolean", "description": "O cancelamento automático do plano." }, "url": { "type": "string", "description": "A URL do plano." }, "custom_layout_enabled": { "type": "boolean", "description": "A habilitação do layout personalizado do plano." }, "custom_layout": { "type": "string", "description": "O layout personalizado do plano." }, "redirect_uri": { "type": "string", "description": "A URI de redirecionamento do plano." }, "status": { "$ref": "#/components/schemas/PlanStatus" }, "custom_fields": { "type": "array", "description": "Os campos personalizados do plano." }, "billing_products": { "type": "array", "description": "Os produtos de cobrança do plano." }, "company": { "type": "object", "description": "A empresa do plano." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança do plano." }, "billing_settings": { "type": "object", "description": "As configurações de cobrança do plano." }, "dunning": { "type": "object", "description": "O dunning do plano." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o plano foi criado." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que o plano foi atualizado." } } }, "PlanStatus": { "type": "string", "enum": [ "active", "inactive" ] } } }, "tags": [ { "name": "Planos", "description": "Planos" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Criar nova assinatura Source: https://docs.ninepay.com.br/reference/criar-nova-assinatura Cria uma nova assinatura, que representa uma cobrança recorrente, vinculando um cliente a um plano específico. **Cartão de crédito:** se a assinatura for cobrada no cartão, o cadastro do cliente precisa ter CPF ou CNPJ, telefone, CEP e número do endereço. Esses dados são enviados à adquirente como dados do titular, e a cobrança é recusada se algum deles estiver faltando. Recomendamos cadastrar o cliente com o máximo de informações possível, mesmo que hoje você só cobre por boleto ou Pix. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions": { "post": { "summary": "Criar nova assinatura", "tags": [ "Assinaturas" ], "description": "Cria uma nova assinatura, que representa uma cobrança recorrente, vinculando um cliente a um plano específico.\n**Cartão de crédito:** se a assinatura for cobrada no cartão, o cadastro do cliente precisa ter CPF ou CNPJ, telefone, CEP e número do endereço. Esses dados são enviados à adquirente como dados do titular, e a cobrança é recusada se algum deles estiver faltando. Recomendamos cadastrar o cliente com o máximo de informações possível, mesmo que hoje você só cobre por boleto ou Pix.\n", "requestBody": { "$ref": "#/components/requestBodies/SubscriptionStoreBody" }, "responses": { "200": { "description": "Assinatura criada com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionStoreResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "operationId": "createSubscription" } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionStoreResponse": { "type": "object", "properties": { "subscription": { "$ref": "#/components/schemas/Subscription" }, "customer": { "$ref": "#/components/schemas/Customer" } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "Subscription": { "type": "object", "properties": { "id": { "description": "O id da assinatura.", "type": "integer", "example": 42, "readOnly": true }, "uuid": { "description": "O id publico da assinatura. Esse id pode ser usado para levar o cliente a página de checkout para pagamento.", "type": "string", "format": "uuid", "example": "8b2cad02-06f0-42be-84ac-101b23a1bb0d" }, "amount": { "description": "O valor da assinatura em centavos.", "type": "number", "example": 2500 }, "plan_id": { "description": "O id do plano.", "type": "number", "example": 2500 }, "status": { "description": "O status da assinatura.", "$ref": "#/components/schemas/SubscriptionStatus", "example": "credit" }, "created_at": { "description": "Data de criação.", "type": "string", "format": "date-time", "example": "2017-07-21 05:20:00" } } }, "SubscriptionStatus": { "type": "string", "enum": [ "new", "trial", "active", "overdue", "canceled", "expired", "paused" ] }, "SubscriptionStoreBody": { "allOf": [ { "$ref": "#/components/schemas/SubscriptionBody" }, { "type": "object", "properties": { "products": { "type": "array", "description": "Opcional. Os produtos da assinatura, cobrados junto do plano. Enviados aqui, eles entram já na primeira cobrança, e não apenas nas seguintes. Quando o plano tem trial, a primeira cobrança continua sendo R$ 0,00 e os produtos entram na cobrança seguinte ao trial. Os produtos também podem ser anexados depois da criação, pelo endpoint de produtos da assinatura.\n", "items": { "type": "object", "required": [ "identifier", "value" ], "properties": { "identifier": { "type": "string", "maxLength": 50, "description": "O nome do produto, como aparece na cobrança.", "example": "Membro adicional" }, "value": { "type": "integer", "description": "O valor do produto, em centavos.", "example": 1290 }, "name": { "type": "string", "maxLength": 191, "description": "Opcional. O que o cliente lê na cobrança. O `identifier` é a chave pela qual o produto é removido, então costuma ser um valor técnico; o `name` é o rótulo apresentável. Sem `name`, o `identifier` continua sendo exibido.\n", "example": "Membro adicional" } } } } } } ] }, "SubscriptionBody": { "type": "object", "required": [ "customer", "plan" ], "properties": { "customer": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "number", "description": "O ID do cliente na NinePay. Se você ainda não tem o ID do cliente, utilize o endpoint de criação de Cliente para fazer o cadastro e receber o ID." } } }, "plan": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "number", "description": "O ID do plano a que a nova assinatura será vinculada." } } }, "coupon_codes": { "type": "array", "items": { "type": "string", "example": [ "ABC123", "DEF456" ] } } } } }, "requestBodies": { "SubscriptionStoreBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionStoreBody" } } } } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Atualizar assinatura existente Source: https://docs.ninepay.com.br/reference/atualizar-assinatura-existente Atualiza uma assinatura existente com base no ID fornecido. **Nota:** É necessário fornecer o ID da assinatura que deseja atualizar. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions/{id}": { "put": { "summary": "Atualizar assinatura existente", "tags": [ "Assinaturas" ], "description": "Atualiza uma assinatura existente com base no ID fornecido. **Nota:** É necessário fornecer o ID da assinatura que deseja atualizar.\n", "requestBody": { "$ref": "#/components/requestBodies/SubscriptionBody" }, "responses": { "200": { "description": "Assinatura atualizada com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionUpdateResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionUpdateResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID da assinatura." }, "uuid": { "type": "string", "description": "O UUID da assinatura." }, "plan_id": { "type": "string", "description": "O ID do plano da assinatura." }, "plan": { "type": "object", "description": "O plano da assinatura." }, "customer_id": { "type": "string", "description": "O ID do cliente da assinatura." }, "customer": { "type": "object", "description": "O cliente da assinatura." }, "method": { "type": "string", "description": "O método da assinatura." }, "card_id": { "type": "string", "description": "O ID do cartão de crédito da assinatura." }, "credit_card": { "type": "object", "description": "O cartão de crédito da assinatura." }, "amount": { "type": "number", "description": "O valor recorrente da assinatura." }, "value": { "type": "number", "description": "O valor da assinatura." }, "last_card_digits": { "type": "string", "description": "Os últimos dígitos do cartão da assinatura." }, "products": { "type": "array", "description": "Os produtos da assinatura." }, "histories": { "type": "array", "description": "O histórico da assinatura." }, "coupons": { "type": "array", "description": "Os cupons da assinatura." }, "status": { "$ref": "#/components/schemas/SubscriptionStatus" }, "reason_cancel": { "type": "string", "description": "A razão do cancelamento da assinatura." }, "cancel_type": { "type": "string", "description": "O tipo de cancelamento da assinatura." }, "voluntary_cancel": { "type": "boolean", "description": "O cancelamento voluntário da assinatura." }, "start": { "type": "string", "format": "date", "description": "A data de início da assinatura." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que a assinatura foi criada." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que a assinatura foi atualizada." }, "end": { "type": "string", "format": "date", "description": "A data de término da assinatura." }, "next_payment": { "type": "string", "format": "date-time", "description": "A data e hora do próximo pagamento da assinatura." }, "retries": { "type": "number", "description": "As tentativas da assinatura." }, "custom_data": { "type": "object", "description": "Os dados personalizados da assinatura." }, "company": { "type": "object", "description": "A empresa da assinatura." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança da assinatura." }, "customerContract": { "type": "object", "description": "O contrato do cliente da assinatura." } } }, "SubscriptionStatus": { "type": "string", "enum": [ "new", "trial", "active", "overdue", "canceled", "expired", "paused" ] }, "SubscriptionBody": { "type": "object", "required": [ "customer", "plan" ], "properties": { "customer": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "number", "description": "O ID do cliente na NinePay. Se você ainda não tem o ID do cliente, utilize o endpoint de criação de Cliente para fazer o cadastro e receber o ID." } } }, "plan": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "number", "description": "O ID do plano a que a nova assinatura será vinculada." } } }, "coupon_codes": { "type": "array", "items": { "type": "string", "example": [ "ABC123", "DEF456" ] } } } } }, "requestBodies": { "SubscriptionBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionBody" } } } } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Obter detalhes da assinatura Source: https://docs.ninepay.com.br/reference/obter-detalhes-da-assinatura Retorna os detalhes de uma assinatura específica, com base no ID fornecido. **Nota:** É necessário fornecer o ID da assinatura para obter os detalhes. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions/{id}": { "get": { "summary": "Obter detalhes da assinatura", "tags": [ "Assinaturas" ], "description": "Retorna os detalhes de uma assinatura específica, com base no ID fornecido. **Nota:** É necessário fornecer o ID da assinatura para obter os detalhes.\n", "responses": { "200": { "description": "Detalhes da assinatura retornados com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionDetailsResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionDetailsResponse": { "type": "object", "properties": { "id": { "type": "string", "description": "O ID da assinatura." }, "uuid": { "type": "string", "description": "O UUID da assinatura." }, "plan_id": { "type": "string", "description": "O ID do plano da assinatura." }, "plan": { "type": "object", "description": "O plano da assinatura." }, "customer_id": { "type": "string", "description": "O ID do cliente da assinatura." }, "customer": { "type": "object", "description": "O cliente da assinatura." }, "method": { "type": "string", "description": "O método da assinatura." }, "card_id": { "type": "string", "description": "O ID do cartão de crédito da assinatura." }, "credit_card": { "type": "object", "description": "O cartão de crédito da assinatura." }, "amount": { "type": "number", "description": "O valor recorrente da assinatura." }, "value": { "type": "number", "description": "O valor da assinatura." }, "last_card_digits": { "type": "string", "description": "Os últimos dígitos do cartão da assinatura." }, "products": { "type": "array", "description": "Os produtos da assinatura." }, "histories": { "type": "array", "description": "O histórico da assinatura." }, "coupons": { "type": "array", "description": "Os cupons da assinatura." }, "status": { "$ref": "#/components/schemas/SubscriptionStatus" }, "reason_cancel": { "type": "string", "description": "A razão do cancelamento da assinatura." }, "cancel_type": { "type": "string", "description": "O tipo de cancelamento da assinatura." }, "voluntary_cancel": { "type": "boolean", "description": "O cancelamento voluntário da assinatura." }, "start": { "type": "string", "format": "date", "description": "A data de início da assinatura." }, "created_at": { "type": "string", "format": "date-time", "description": "A data e hora em que a assinatura foi criada." }, "updated_at": { "type": "string", "format": "date-time", "description": "A data e hora em que a assinatura foi atualizada." }, "end": { "type": "string", "format": "date", "description": "A data de término da assinatura." }, "next_payment": { "type": "string", "format": "date-time", "description": "A data e hora do próximo pagamento da assinatura." }, "retries": { "type": "number", "description": "As tentativas da assinatura." }, "custom_data": { "type": "object", "description": "Os dados personalizados da assinatura." }, "company": { "type": "object", "description": "A empresa da assinatura." }, "charge_enabled": { "type": "boolean", "description": "A habilitação da cobrança da assinatura." }, "customerContract": { "type": "object", "description": "O contrato do cliente da assinatura." } } }, "SubscriptionStatus": { "type": "string", "enum": [ "new", "trial", "active", "overdue", "canceled", "expired", "paused" ] } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Cancelar uma assinatura Source: https://docs.ninepay.com.br/reference/cancelar-uma-assinatura Cancela uma assinatura. Assinaturas canceladas não enviam mais cobranças aos clientes e não geram pagamentos futuros. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions/{id}": { "post": { "summary": "Cancelar uma assinatura", "tags": [ "Assinaturas" ], "description": "Cancela uma assinatura. Assinaturas canceladas não enviam mais cobranças aos clientes e não geram pagamentos futuros.\n", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "integer", "description": "O id da assinatura a ser cancelada", "example": 42 } } } } } }, "responses": { "200": { "description": "Assinatura criada com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionStoreResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionStoreResponse": { "type": "object", "properties": { "subscription": { "$ref": "#/components/schemas/Subscription" }, "customer": { "$ref": "#/components/schemas/Customer" } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "Subscription": { "type": "object", "properties": { "id": { "description": "O id da assinatura.", "type": "integer", "example": 42, "readOnly": true }, "uuid": { "description": "O id publico da assinatura. Esse id pode ser usado para levar o cliente a página de checkout para pagamento.", "type": "string", "format": "uuid", "example": "8b2cad02-06f0-42be-84ac-101b23a1bb0d" }, "amount": { "description": "O valor da assinatura em centavos.", "type": "number", "example": 2500 }, "plan_id": { "description": "O id do plano.", "type": "number", "example": 2500 }, "status": { "description": "O status da assinatura.", "$ref": "#/components/schemas/SubscriptionStatus", "example": "credit" }, "created_at": { "description": "Data de criação.", "type": "string", "format": "date-time", "example": "2017-07-21 05:20:00" } } }, "SubscriptionStatus": { "type": "string", "enum": [ "new", "trial", "active", "overdue", "canceled", "expired", "paused" ] } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Listar os produtos da assinatura Source: https://docs.ninepay.com.br/reference/listar-os-produtos-da-assinatura Lista os produtos de uma assinatura. Os produtos são cobrados junto do plano, e a soma deles com o valor do plano é o `amount` da assinatura. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions/{id}/products": { "get": { "summary": "Listar os produtos da assinatura", "tags": [ "Assinaturas" ], "description": "Lista os produtos de uma assinatura. Os produtos são cobrados junto do plano, e a soma deles com o valor do plano é o `amount` da assinatura.\n", "responses": { "200": { "description": "Produtos da assinatura retornados com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionProductsListResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionProductsListResponse": { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/SubscriptionProduct" } } } }, "SubscriptionProduct": { "type": "object", "properties": { "id": { "type": "integer", "description": "O ID do produto na NinePay." }, "subscription_id": { "type": "integer", "description": "O ID da assinatura a que o produto pertence." }, "identifier": { "type": "string", "description": "A chave do produto, pela qual ele é removido.", "example": "team_user" }, "name": { "type": "string", "nullable": true, "description": "O que o cliente lê na cobrança. Quando nulo, o `identifier` é exibido.\n", "example": "Membro adicional" }, "value": { "type": "integer", "description": "O valor do produto, em centavos.", "example": 1290 }, "created_at": { "type": "string", "description": "A data de criação do produto." }, "updated_at": { "type": "string", "description": "A data da última atualização do produto." } } } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Anexar produtos à assinatura Source: https://docs.ninepay.com.br/reference/anexar-produtos-a-assinatura Anexa produtos a uma assinatura já criada. Eles passam a ser cobrados junto do plano a partir da cobrança seguinte. Para que os produtos entrem já na primeira cobrança, envie-os no campo `products` na criação da assinatura. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions/{id}/products": { "post": { "summary": "Anexar produtos à assinatura", "tags": [ "Assinaturas" ], "description": "Anexa produtos a uma assinatura já criada. Eles passam a ser cobrados junto do plano a partir da cobrança seguinte. Para que os produtos entrem já na primeira cobrança, envie-os no campo `products` na criação da assinatura.\n", "requestBody": { "$ref": "#/components/requestBodies/SubscriptionProductsBody" }, "responses": { "200": { "description": "Produtos anexados com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionStoreResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionStoreResponse": { "type": "object", "properties": { "subscription": { "$ref": "#/components/schemas/Subscription" }, "customer": { "$ref": "#/components/schemas/Customer" } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "Subscription": { "type": "object", "properties": { "id": { "description": "O id da assinatura.", "type": "integer", "example": 42, "readOnly": true }, "uuid": { "description": "O id publico da assinatura. Esse id pode ser usado para levar o cliente a página de checkout para pagamento.", "type": "string", "format": "uuid", "example": "8b2cad02-06f0-42be-84ac-101b23a1bb0d" }, "amount": { "description": "O valor da assinatura em centavos.", "type": "number", "example": 2500 }, "plan_id": { "description": "O id do plano.", "type": "number", "example": 2500 }, "status": { "description": "O status da assinatura.", "$ref": "#/components/schemas/SubscriptionStatus", "example": "credit" }, "created_at": { "description": "Data de criação.", "type": "string", "format": "date-time", "example": "2017-07-21 05:20:00" } } }, "SubscriptionStatus": { "type": "string", "enum": [ "new", "trial", "active", "overdue", "canceled", "expired", "paused" ] }, "SubscriptionProductsBody": { "type": "object", "required": [ "products" ], "properties": { "products": { "type": "array", "description": "Os produtos a anexar à assinatura.", "items": { "type": "object", "required": [ "identifier", "value" ], "properties": { "identifier": { "type": "string", "maxLength": 50, "description": "O nome do produto, como aparece na cobrança.", "example": "Membro adicional" }, "value": { "type": "integer", "description": "O valor do produto, em centavos.", "example": 1290 }, "name": { "type": "string", "maxLength": 191, "description": "Opcional. O que o cliente lê na cobrança. O `identifier` é a chave pela qual o produto é removido, então costuma ser um valor técnico; o `name` é o rótulo apresentável. Sem `name`, o `identifier` continua sendo exibido.\n", "example": "Membro adicional" } } } } } } }, "requestBodies": { "SubscriptionProductsBody": { "required": true, "description": "Descreve os dados a serem incluídos no corpo da requisição.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionProductsBody" } } } } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Remover um produto da assinatura Source: https://docs.ninepay.com.br/reference/remover-um-produto-da-assinatura Remove um produto da assinatura, que deixa de ser cobrado a partir da cobrança seguinte. Quando nenhum produto da assinatura tem o `identifier` informado, o produto mais antigo é removido. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/subscriptions/{id}/products/{identifier}": { "delete": { "summary": "Remover um produto da assinatura", "tags": [ "Assinaturas" ], "description": "Remove um produto da assinatura, que deixa de ser cobrado a partir da cobrança seguinte. Quando nenhum produto da assinatura tem o `identifier` informado, o produto mais antigo é removido.\n", "responses": { "200": { "description": "Produto removido com sucesso", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionStoreResponse" } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "$ref": "#/components/responses/422" }, "500": { "$ref": "#/components/responses/500" } }, "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "identifier", "in": "path", "required": true, "schema": { "type": "string" } } ] } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } }, "422": { "description": "Erro na validação dos dados", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "VALIDATION_ERROR", "message": "Os dados informados são inválidos.", "errors": { "email": [ "O campo email é obrigatório." ] }, "timestamp": "2026-10-22T12:03:54+00:00", "request_id": "64d62d36-e797-45ed-a9d5-c203bde81032", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Corrija os campos e tente novamente." } } } }, "500": { "description": "Erro inesperado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "UNEXPECTED_ERROR", "message": "Houve um erro inesperado.", "timestamp": "2026-10-22T12:04:46+00:00", "request_id": "868443d3-38fd-4c92-8ea8-7cccbbce4308", "documentation_url": "https://docs.ninepay.com.br", "suggestion": "Verifique todos os parâmetros da requisição. Se o erro persistir, entre em contato com nossa equipe em ti@ninepay.com.br." } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } }, "SubscriptionStoreResponse": { "type": "object", "properties": { "subscription": { "$ref": "#/components/schemas/Subscription" }, "customer": { "$ref": "#/components/schemas/Customer" } } }, "Customer": { "type": "object", "required": [ "id", "name", "email", "cpf", "cnpj" ], "properties": { "id": { "description": "O id do cliente na NinePay. Obrigatório se os demais dados do usuário não forem informados", "type": "integer", "example": 42, "readOnly": true }, "name": { "description": "O nome do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "Jhon Doe" }, "email": { "description": "O email do cliente. Obrigatório se o `id` não for informado", "type": "string", "example": "jhon@doe.com" }, "cpf": { "description": "O cpf do cliente. Obrigatório se o `id` ou o `cnpj` não forem informados", "type": "string", "example": "833.828.600-15" }, "cnpj": { "description": "O cnpj do cliente. Obrigatório se o `id` ou o `cpf` não forem informados", "type": "string", "example": "95.135.209/0001-79" }, "phone": { "description": "O telefone celular do cliente, com DDD. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "(11) 11111-1111" }, "cep": { "description": "O CEP do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": "01423-910" }, "address": { "description": "O endereço do cliente", "type": "string", "example": "Rua José Maria Lisboa" }, "number": { "description": "O número do endereço do cliente. Obrigatório para cobranças no cartão de crédito.", "type": "string", "example": 42 }, "district": { "description": "O bairro do cliente", "type": "string", "example": "Jardim Paulista" }, "city": { "description": "A cidade do cliente", "type": "string", "example": "São Paulo" }, "state": { "description": "O estado do cliente", "type": "string", "example": "SP" }, "country": { "description": "O código do país do cliente, no formato Alpha 2 de acordo com a [ISO 3166](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).", "type": "string", "example": "BR" } } }, "Subscription": { "type": "object", "properties": { "id": { "description": "O id da assinatura.", "type": "integer", "example": 42, "readOnly": true }, "uuid": { "description": "O id publico da assinatura. Esse id pode ser usado para levar o cliente a página de checkout para pagamento.", "type": "string", "format": "uuid", "example": "8b2cad02-06f0-42be-84ac-101b23a1bb0d" }, "amount": { "description": "O valor da assinatura em centavos.", "type": "number", "example": 2500 }, "plan_id": { "description": "O id do plano.", "type": "number", "example": 2500 }, "status": { "description": "O status da assinatura.", "$ref": "#/components/schemas/SubscriptionStatus", "example": "credit" }, "created_at": { "description": "Data de criação.", "type": "string", "format": "date-time", "example": "2017-07-21 05:20:00" } } }, "SubscriptionStatus": { "type": "string", "enum": [ "new", "trial", "active", "overdue", "canceled", "expired", "paused" ] } } }, "tags": [ { "name": "Assinaturas", "description": "Assinaturas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Consultar CNPJ Source: https://docs.ninepay.com.br/reference/consultar-cnpj Retorna os dados cadastrais de uma empresa pelo CNPJ, como razão social, nome fantasia e endereço, para preencher formulários de cadastro e cobrança. O CNPJ pode ser enviado com ou sem pontuação (sem a barra, que quebraria o caminho). **Limite:** 30 consultas por minuto por token. # OpenAPI definition ```json { "openapi": "3.0.3", "info": { "title": "NinePay API", "version": "1.0", "x-logo": { "url": "" }, "termsOfService": "https://bit.ly/cefispay-contrato", "contact": { "name": "NinePay Engineering Team", "email": "ti@ninepay.com.br" }, "description": "# Introdução\nBem vindo a referência da **API NinePay**.\nÉ através desta API que você integra o seu sistema ao nosso.\nNossa API é RESTful, e todas suas respostas são em JSON, no endpoint base:\n```\nhttps://payments.cefis.com.br/api/v1/\n```\n\n## Ambientes\nAlém do ambiente de **produção** temos disponível o ambiente **sandbox**\npara a realização de testes de integração. Toda a comunicação com o ambiente sandbox deve ser feita através da url:\n\n```\nhttps://api.sandbox.cefispay.com.br/api/v1/\n```\n\n## Autenticação\nTodas as requisições que requerem autenticação, deverão informar no header `Authorization` o token de api enviado pela nossa equipe, observando sempre o ambiente desejado e no seguinte formato:\n```\nAuthorization: Bearer \n```\n\n## Headers\nRecomendamos que os cabeçalhos da requisições sejam corretamente adicionados para trabalhar com os tipos de dados enviados e recebidos da API:\n - Todas as requisições devem contem o header `Content-Type: application/json`, a menos que o endpoint específico defina diferente\n - Todas as requisições devem contem o header `Accept: application/json`, a menos que o endpoint específico defina diferente\n\n## Respostas\n- Os recursos vêm dentro de `data`: `GET /customers/{id}` responde `{ \"data\": { ... } }`.\n- As criações respondem **201**. `POST /payments` responde `{ \"customer\", \"payment\", \"invoice_url\" }` (o `invoice_url` só no boleto) e `POST /subscriptions` responde `{ \"customer\", \"subscription\" }`.\n- Valores sempre em centavos (inteiro): R$ 50,00 é `5000`.\n\n## Erros\nTodo erro tem o mesmo formato:\n```json\n{\n \"code\": \"VALIDATION_ERROR\",\n \"message\": \"Os dados informados são inválidos.\",\n \"errors\": { \"email\": [\"O campo email é obrigatório.\"] },\n \"timestamp\": \"2026-10-22T12:03:54+00:00\",\n \"request_id\": \"64d62d36-e797-45ed-a9d5-c203bde81032\",\n \"documentation_url\": \"https://docs.ninepay.com.br\",\n \"suggestion\": \"Corrija os campos e tente novamente.\"\n}\n```\n`errors` só vem na validação, com uma **lista** de mensagens por campo. `suggestion` vem quando há uma. Guarde o `request_id` para falar com o suporte.\n\n| `code` | HTTP | Quando |\n| --- | --- | --- |\n| `AUTHENTICATION_ERROR` | 401 | Token ausente ou inválido |\n| `FORBIDDEN_ERROR` | 403 | O recurso não é da sua empresa. Em várias rotas, um id que não existe também responde 403, para não revelar quais ids existem |\n| `COMPANYBLOCKED_ERROR` | 403 | A conta está suspensa |\n| `RESOURCE_NOT_FOUND_ERROR` | 404 | O recurso não existe |\n| `ROUTE_NOT_FOUND_ERROR` | 404 | A rota não existe |\n| `METHOD_NOT_ALLOWED_ERROR` | 405 | O método não vale para a rota (ex.: `PUT /customers` sem o id) |\n| `INVALID_STATE_ERROR` | 400 | O recurso não está num estado que permita a ação (ex.: cancelar um pagamento já pago) |\n| `VALIDATION_ERROR` | 422 | Dados inválidos; veja `errors` |\n| `REFUND_NOT_ALLOWED` | 422 | O pagamento não pode ser reembolsado |\n| `TOO_MANY_ATTEMPTS` | 429 | Muitas requisições; respeite o cabeçalho `Retry-After` |\n| `UNEXPECTED_ERROR` | 500 | Erro nosso; se repetir, fale com o suporte com o `request_id` |\n\n## Paginação\nAs listagens recebem `page` (a partir de 1) e `limit` (itens por página: **10** por padrão, de 1 a **100**). A resposta traz `data` (os itens), `links` e `meta` (`current_page`, `last_page`, `per_page`, `total`).\n\n## Datas\n- Datas de calendário (`due_date`, `next_payment`) vão como `AAAA-MM-DD` na requisição.\n- Nas respostas, os campos de data e hora aparecem em dois formatos: ISO 8601 em UTC (`2026-10-27T03:00:00.000000Z`, em `due_date` e `ref_date` do pagamento) e `AAAA-MM-DD HH:MM:SS` no horário de Brasília (`created_at` e `updated_at`). Leia o campo pelo formato dele.\n\n## Identificadores\nCada recurso tem um `id` numérico e um `uuid`. Use o `id` nas rotas autenticadas e o **`uuid`** em tudo que é público (o link de pagamento, o checkout e o PDF do boleto).\n\n# Hash de cartões\nO hash de um cartão é uma versão criptografada de todos os dados do cartão do cliente, essa é a maneira mais segura de enviar os dados do cartão\ndo seu cliente diretamente para a gente. Abaixo vamos detalhar como ele funciona e como obter um hash a partir dos dados inseridos pelo cliente.\nÉ importante ressaltar que esse fluxo deve ser executado no front-end, seja ele web ou mobile, e que os dados do cartão do cliente nunca devem\nser enviados para o backend, já o hash do cartão pode ser enviado para o backend sem problemas.\n\n## Gerando uma encryption key\nA encryption key consiste basicamente em uma chave pública RSA usada para encriptar os dados do cartão, essa chave só pode ser usada uma vez, então\nsempre que precisar encriptar um novo cartão você precisa gerar uma nova encryption key.\n\nPara gerar uma encryption key é necessário fazer uma requisição ``POST`` para a rota ``/encryption_keys`` da API enviando como parâmetro seu\n``merchant_id``. Ela não usa o token da API (nunca o coloque no front-end): o ``merchant_id`` identifica a sua conta, e um\n``merchant_id`` que não existe responde 401.\n\nA requisição deverá ter esse formato:\n```\ncurl -X POST https://payments.cefis.com.br/api/v1/encryption_keys -H 'Content-Type: application/json' -d '{\"merchant_id\":\"SEU_MERCHANT_ID\"}'\n```\n\nComo resposta você receberá um objeto JSON no seguinte formato:\n```\n{\n \"id\": 1111,\n \"public_key\": \"-----BEGIN PUBLIC KEY-----\\n ... \\n-----END PUBLIC KEY-----\\n\",\n \"ip_address\": \"000.000.000.000\",\n \"created_at\": \"2017-07-03\",\n}\n```\n\n## Encriptando os dados do cartão\nPara encriptar os dados do cartão primeiro você precisa formatá-los de forma correta, para isso crie uma string JSON\ncontendo todos os dados do cartão no seguinte formato:\n\n```\n{\"card_number\":\"1111111111111111\",\"card_holder_name\":\"Jose Silva\",\"card_expiration_date\":\"11/2021\",\"card_cvv\":\"111\"}\n```\n\nEm seguida faça a criptografia RSA dessa string utilizando a chave pública (public_key) gerada no passo anterior, com\npadding do tipo PKCS1.\n\nApós criptografar os dados converta a string resultante para base64 e você terá um resultado parecido com esse:\n\n```\nGprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nO último passo é concatenar o id da sua encryption key (recebido como resposta da requisição anterior) com a string\nem base64 que representa os dados do cartão, da seguinte forma:\n\n``encryption_key_id + \"_\" + base64_encrypted_card``\n\nO resultado será o seu card hash:\n\n```\n1111_GprWNDlg+MKGgtr6y9wJgz7oOUxGuhhdX3EwydmoOvb+yNDWrjDKktDtWicReOrvSo0u5rsNvCqBFjyoMfaRXn4Qq3KdV8vuuFXubb4FT3OcSSVpu6ddaOjm2tf82+GKjdCy54Lgw4T0m9mWdLPveh/zkYP0+4lhHCFhBKX5EqBdR4918wP5oUV1KpzV4V94ldSA+Cy2n1NUxyUiSnv87H2m+HkdqKDIcX9X8KEtUmorC/EjTPzoeEdpRdj8t3ThcL73mirJvDcYthSdT/ADyaFQ2hMRMkKEVOQjUYMn76vzWyQkqsZ+HVQlxQMOBVHPh5yfrmi1HAVd1jLQuOK8wQ==\n```\n\nPronto, agora os dados do cartão do cliente já estão em um formato seguro para serem transportados pela rede, podendo\nagora ser enviados para o seu back-end dar sequência ao fluxo. Vale lembrar que cada card hash pode ser usado apenas\numa vez e além disso tem uma validade máxima de 30 minutos após a geração da encryption key. Caso seja necessário utilizar\no cartão mais de uma vez faça uma requisição para a rota de cartões e salve esse cartão vinculado ao usuário que o detém,\nassim você receberá um ID único que poderá ser usado para realizar mais transações com o mesmo cartão.\n" }, "paths": { "/cnpj/{cnpj}": { "get": { "summary": "Consultar CNPJ", "tags": [ "Consultas" ], "description": "Retorna os dados cadastrais de uma empresa pelo CNPJ, como razão social, nome fantasia e endereço, para preencher formulários de cadastro e cobrança. O CNPJ pode ser enviado com ou sem pontuação (sem a barra, que quebraria o caminho). **Limite:** 30 consultas por minuto por token.\n", "parameters": [ { "name": "cnpj", "in": "path", "required": true, "description": "CNPJ com 14 dígitos.", "schema": { "type": "string", "example": "00000000000191" } } ], "responses": { "200": { "description": "Dados da empresa", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "object", "properties": { "taxId": { "type": "string", "example": "00000000000191" }, "alias": { "type": "string", "nullable": true, "description": "Nome fantasia", "example": "Banco do Brasil" }, "company": { "type": "object", "properties": { "name": { "type": "string", "description": "Razão social", "example": "BANCO DO BRASIL SA" } } }, "address": { "type": "object", "properties": { "street": { "type": "string" }, "number": { "type": "string" }, "details": { "type": "string", "nullable": true }, "district": { "type": "string" }, "city": { "type": "string" }, "state": { "type": "string" }, "zip": { "type": "string" } } } } } } } } } }, "401": { "$ref": "#/components/responses/401" }, "422": { "description": "CNPJ sem 14 dígitos", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "CNPJ inválido." } } } } } }, "429": { "description": "Limite de consultas por minuto atingido" }, "502": { "description": "Não foi possível consultar o CNPJ no momento", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string", "example": "Não foi possível consultar o CNPJ." } } } } } } } } } }, "components": { "securitySchemes": { "Bearer": { "type": "apiKey", "in": "header", "name": "Authorization", "description": "Todas as requisições que requerem autenticação devem incluir no cabeçalho `Authorization` o token de API enviado pela nossa equipe. O token deve ser fornecido no seguinte formato:\n```\nAuthorization: Bearer \n```" } }, "responses": { "401": { "description": "Não autenticado", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" }, "example": { "code": "AUTHENTICATION_ERROR", "message": "Não autenticado.", "timestamp": "2026-10-22T12:03:44+00:00", "request_id": "f2a7e962-6083-4525-816e-f14d4a602e0a", "documentation_url": "https://docs.ninepay.com.br" } } } } }, "schemas": { "ApiError": { "type": "object", "properties": { "code": { "type": "string", "description": "O código do erro. Ex.: `VALIDATION_ERROR`, `FORBIDDEN_ERROR`, `UNEXPECTED_ERROR`." }, "message": { "type": "string", "description": "A mensagem do erro." }, "errors": { "type": "object", "description": "Os erros de cada campo, quando o `code` é `VALIDATION_ERROR`.", "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "timestamp": { "type": "string", "format": "date-time", "description": "A data e hora do erro, em UTC." }, "request_id": { "type": "string", "description": "Um identificador único gerado para esta resposta de erro." }, "documentation_url": { "type": "string", "description": "O endereço desta documentação." }, "suggestion": { "type": "string", "description": "Uma sugestão de como resolver o erro." } } } } }, "tags": [ { "name": "Consultas", "description": "Consultas" } ], "x-tagGroups": [ { "name": "API Calls", "tags": [ "Pagamentos", "Clientes", "Planos", "Assinaturas", "Consultas" ] } ], "servers": [ { "url": "https://api.sandbox.cefispay.com.br/api/v1", "description": "Servidor de Testes (sandbox)" } ], "security": [ { "Bearer": [] } ] } ``` --- # Checkout embutido (ninepay.js) Source: https://docs.ninepay.com.br/recipes/checkout-embutido Coloque o pagamento de uma fatura dentro da sua página, com o seu botão de pagar e o iframe da NinePay para cartão, PIX e boleto. ## Crie a fatura no servidor > Linhas destacadas: 8 No seu servidor, com o token da API, crie a cobrança em aberto com `open: true` e os métodos que o iframe pode oferecer. Nada é cobrado na criação. Mande para a página só o `payment.uuid` da resposta; o token nunca vai para o navegador. ```bash curl -X POST https://api.sandbox.cefispay.com.br/api/v1/payments \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "customer": { "id": 123 }, "method": "credit", "open": true, "allowed_methods": ["credit", "pix", "billet"], "value": 5000, "due_date": "2026-11-10" }' ``` Antes, cadastre no dashboard (Integrações → Checkout embutido) o endereço do site que vai abrir o checkout; sem ele, o navegador recusa o iframe. ## Carregue o ninepay.js > Linhas destacadas: 4-6 Carregue o script sempre de `https://checkout.ninepay.com.br/js/v1/ninepay.js`, nunca de uma cópia. Para o sandbox, passe a origem do ambiente em `Ninepay({ origin })`. O CSP da página precisa liberar `https://checkout.ninepay.com.br` em `script-src` e `frame-src`. ## Monte o elemento > Linhas destacadas: 7-11 `ninepay.elements` recebe o `uuid` da fatura (e, se quiser, `appearance` para combinar com a sua página). `elements.create('payment')` cria o iframe com os métodos da fatura, e `mount` o coloca no lugar. ## Habilite o botão > Linhas destacadas: 13-16 O botão de pagar é da sua página. O evento `change` avisa quando os campos do método escolhido estão completos. ## Valide e pague > Linhas destacadas: 18-31 `elements.submit()` valida e mostra os erros no iframe, sem enviar. Depois, `ninepay.confirmPayment({ elements })` paga e sempre resolve, com `{ payment }` ou `{ error }`. No sandbox, teste com os [cartões de teste](/docs/test-cards). ## Acompanhe o pagamento > Linhas destacadas: 33-35 O evento `paid` chega quando o pagamento é feito nesta tela, inclusive o PIX ou o boleto pagos depois. Use para atualizar a tela; libere o acesso só pelo webhook, confirmando com `GET /payments/{id}`. ## Código ```html HTML
``` --- # Cobrança no cartão de crédito Source: https://docs.ninepay.com.br/recipes/cobranca-no-cartao-de-credito Gere um card hash com um cartão de teste do sandbox, cobre no cartão em parcelas e confira o status. ## Gere uma encryption key > Linhas destacadas: 5-11 `POST /encryption_keys` recebe o seu `merchant_id` e devolve uma chave pública RSA (`public_key`) e o `id` dela. Cada chave serve para um cartão só. > 🚧 Em produção, isto roda no front-end > Os passos 1 e 2 estão em Node só para testar no sandbox com um cartão de teste. Com cartões de verdade, gere o hash no navegador ou no app: os dados do cartão nunca devem passar pelo seu back-end. Veja [Hash de cartões](/docs/introducao-api#hash-de-cartoes). ## Encripte o cartão > Linhas destacadas: 13-24 Monte o JSON do cartão, encripte com a `public_key` usando padding PKCS1, converta para base64 e junte com o `id` da chave: `id_base64`. O hash vale uma vez e por até 30 minutos. No sandbox, o último dígito do cartão decide o resultado: final **0** aprova, finais **1 a 9** recusam. Use validade futura e qualquer CVV de 3 dígitos. Veja os [cartões de teste](/docs/test-cards). ## Cobre no cartão > Linhas destacadas: 26-42 Envie `method: "credit"`, o `card_hash` e, se quiser, `installments` (de 1 a 12). O cliente precisa ter CPF ou CNPJ, telefone, CEP e número do endereço no cadastro; sem eles, a cobrança é recusada. ## Confira o status > Linhas destacadas: 44-49 `paid` quer dizer aprovado. `unauthorized` quer dizer recusado: o pagamento continua em aberto e pode ser pago de novo. Para liberar o acesso, prefira o webhook `payment.approved`, confirmando com esta mesma consulta. ## Código ```js Node.js import { publicEncrypt, constants } from 'node:crypto'; const BASE_URL = 'https://api.sandbox.cefispay.com.br/api/v1'; // 1. Gere uma encryption key (não usa o token da API) const keyRes = await fetch(`${BASE_URL}/encryption_keys`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ merchant_id: process.env.NINEPAY_MERCHANT_ID }), }); const key = await keyRes.json(); // 2. Encripte o cartão de teste (final 0 = aprovado no sandbox) const card = JSON.stringify({ card_number: '5358732681618240', card_holder_name: 'Maria Souza', card_expiration_date: '05/2030', card_cvv: '123', }); const encrypted = publicEncrypt( { key: key.public_key, padding: constants.RSA_PKCS1_PADDING }, Buffer.from(card), ).toString('base64'); const cardHash = `${key.id}_${encrypted}`; // 3. Cobre no cartão com o card hash const paymentRes = await fetch(`${BASE_URL}/payments`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.NINEPAY_TOKEN}`, 'Content-Type': 'application/json', Accept: 'application/json', }, body: JSON.stringify({ method: 'credit', customer: { id: 315968 }, value: 15000, // R$ 150,00 installments: 3, card_hash: cardHash, }), }); const { payment } = await paymentRes.json(); // 4. Confira o resultado const detailsRes = await fetch(`${BASE_URL}/payments/${payment.id}`, { headers: { Authorization: `Bearer ${process.env.NINEPAY_TOKEN}`, Accept: 'application/json' }, }); const { data } = await detailsRes.json(); console.log(data.status); // 'paid' (aprovado) ou 'unauthorized' (recusado) ``` --- # Criar um cliente e cobrar no PIX Source: https://docs.ninepay.com.br/recipes/criar-cliente-e-cobrar-no-pix Cadastre o cliente com POST /customers e gere uma cobrança PIX para ele com POST /payments. ## Aponte para o sandbox > Linhas destacadas: 1-6 Todas as chamadas de teste vão para `https://api.sandbox.cefispay.com.br/api/v1`, com a chave de sandbox (prefixo `sk_hmlg_`) no header `Authorization: Bearer`. Guarde a chave em variável de ambiente e chame a API só do seu servidor. ## Cadastre o cliente > Linhas destacadas: 8-18 `name` e `email` são obrigatórios; CPF (ou CNPJ) ajuda a identificar o pagador. Guarde o `id` retornado junto do usuário no seu sistema, para não criar o mesmo cliente duas vezes. Se depois for cobrar no cartão, o cadastro também precisa de `phone`, `cep` e `number`. ## Crie a cobrança PIX > Linhas destacadas: 20-30 Envie `method: "pix"`, o cliente e o valor em centavos. O `due_date` (`AAAA-MM-DD`) define o vencimento do PIX. Para emitir um boleto, troque o `method` por `billet`: o `due_date` passa a ser obrigatório e a resposta traz também o `invoice_url`. ## Use o id e o uuid > Linhas destacadas: 31-32 Use o `id` nas rotas autenticadas, como `GET /payments/{id}`, e o `uuid` no que é público, como o [checkout embutido](/docs/checkout-embutido). Para saber quando o cliente pagou, escute o webhook `payment.approved` (veja a recipe [Receber e validar webhooks](/recipes/receber-e-validar-webhooks)). ## Código ```js Node.js const BASE_URL = 'https://api.sandbox.cefispay.com.br/api/v1'; const headers = { Authorization: `Bearer ${process.env.NINEPAY_TOKEN}`, 'Content-Type': 'application/json', Accept: 'application/json', }; // 1. Cadastre o cliente const customerRes = await fetch(`${BASE_URL}/customers`, { method: 'POST', headers, body: JSON.stringify({ name: 'Maria Souza', email: 'maria@exemplo.com.br', cpf: '13690958725', }), }); const customer = await customerRes.json(); // 2. Crie a cobrança PIX para esse cliente const paymentRes = await fetch(`${BASE_URL}/payments`, { method: 'POST', headers, body: JSON.stringify({ method: 'pix', customer: { id: customer.id }, value: 5000, // R$ 50,00, sempre em centavos due_date: '2026-11-10', }), }); const { payment } = await paymentRes.json(); console.log(payment.id, payment.uuid); ``` ## Resposta ```json { "customer": { "id": 315968, "name": "Maria Souza", "email": "maria@exemplo.com.br", "cpf": "13690958725" }, "payment": { "id": 1470491, "uuid": "6f1c1a52-1d7e-4f43-a7a4-0a5a2b1c9e10", "value": 5000, "due_date": "2026-11-10T03:00:00.000000Z" } } ``` --- # Criar plano e assinatura recorrente Source: https://docs.ninepay.com.br/recipes/criar-plano-e-assinatura Crie um plano mensal, assine um cliente nele e mande o cliente para o checkout ativar a assinatura. ## Crie o plano > Linhas destacadas: 3-15 O plano define valor (em centavos), periodicidade e métodos aceitos. Para cobrar todo mês, use `interval: 1` e `interval_type: "months"` (também aceita `days` e `years`). No sandbox, use `dunning_id: 1`, a régua de cobrança padrão. Você também pode criar o plano pelo dashboard e usar só o `id` dele. ## Assine o cliente > Linhas destacadas: 17-25 Toda assinatura liga um cliente a um plano. Se o cliente ainda não existe, crie antes com `POST /customers` (veja [Criar um cliente e cobrar no PIX](/recipes/criar-cliente-e-cobrar-no-pix)). Para aplicar desconto, envie os códigos de cupom em `coupon_codes`. ## Ative no checkout > Linhas destacadas: 27-28 Com o `uuid` da assinatura, redirecione o cliente para a página de ativação. Ele só preenche os dados de pagamento. Em produção, o endereço é `https://checkout.ninepay.com.br/subscription//activate`. Para cobrar dentro da sua própria página, veja o [checkout embutido](/docs/checkout-embutido). As mudanças da assinatura chegam pelo webhook `subscription.updated`. ## Código ```bash cURL BASE_URL=https://api.sandbox.cefispay.com.br/api/v1 # 1. Crie o plano: R$ 49,90 por mês PLAN_ID=$(curl -s -X POST "$BASE_URL/plan" \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "name": "Plano Mensal", "methods": ["credit", "pix", "billet"], "value": 4990, "interval": 1, "interval_type": "months", "dunning_id": 1 }' | jq -r '.id') # 2. Assine o plano para um cliente já cadastrado SUBSCRIPTION_UUID=$(curl -s -X POST "$BASE_URL/subscriptions" \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d "{ \"customer\": { \"id\": $CUSTOMER_ID }, \"plan\": { \"id\": $PLAN_ID } }" | jq -r '.subscription.uuid') # 3. Mande o cliente para o checkout ativar a assinatura echo "https://app.sandbox.cefispay.com.br/subscription/$SUBSCRIPTION_UUID/activate" ``` ## Resposta ```json { "customer": { "id": 315968, "name": "Maria Souza", "email": "maria@exemplo.com.br" }, "subscription": { "id": 205465, "uuid": "8b2cad02-06f0-42be-84ac-101b23a1bb0d", "amount": 4990, "plan_id": 413 } } ``` --- # Estornar um pagamento Source: https://docs.ninepay.com.br/recipes/estornar-um-pagamento Devolva ao cliente um pagamento pago no cartão ou no PIX e acompanhe o reembolso até ser concluído. ## Confira se pode estornar > Linhas destacadas: 1 Só pode ser estornado o pagamento com `status: "paid"`, pago no cartão (`credit`) há menos de 350 dias ou no PIX (`pix`) há menos de 90 dias, e com saldo suficiente na sua conta. Boleto e baixa manual não têm estorno pela API. O campo `can_refund` do pagamento ajuda a conferir antes. ## Peça o estorno > Linhas destacadas: 2-6 Envie o `id` do pagamento em `payment.id`. O estorno é sempre do valor integral e volta ao cliente pelo mesmo meio em que ele pagou. ## Acompanhe o reembolso > Linhas destacadas: 8-11 O estorno do cartão costuma ser imediato: o pagamento passa a `reversed` e sai o webhook `payment.updated`. O do PIX pode ficar em processamento. Consulte o pagamento com `with[]=refunds`: o `status` de cada reembolso vai de `new` e `processing` para `completed` (devolvido) ou `failed`. Enquanto um estorno está em `processing`, outro pedido para o mesmo pagamento é recusado. ## Código ```bash cURL # 1. Peça o estorno do pagamento curl -X POST https://api.sandbox.cefispay.com.br/api/v1/payment/reversal \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "payment": { "id": 1470491 } }' # 2. Acompanhe o reembolso curl -g "https://api.sandbox.cefispay.com.br/api/v1/payments/1470491?with[]=refunds" \ -H "Authorization: Bearer $NINEPAY_TOKEN" \ -H "Accept: application/json" ``` --- # Receber e validar webhooks Source: https://docs.ninepay.com.br/recipes/receber-e-validar-webhooks Receba os eventos da NinePay, responda rápido, valide a assinatura, ignore entregas repetidas e confirme o pagamento na API antes de liberar o acesso. ## Exponha um endereço público > Linhas destacadas: 8-11, 41 Cadastre a URL do seu servidor em **Integrações → Webhooks** no dashboard ou com `POST /automations` (veja [Webhooks](/docs/webhooks)). O endereço precisa ser público; use `https`. O formulário do webhook tem um envio de teste para cada evento. ## Leia o corpo cru e o evento > Linhas destacadas: 12-14, 31 Cada entrega é um `POST` com JSON: `id` (o da entrega), `type` (o evento, como `payment.approved` ou `subscription.updated`), `data` (o recurso no mesmo formato da API) e `created_at`. Leia o corpo como bytes, sem fazer parse antes de validar: a assinatura é calculada sobre o corpo exatamente como chegou. ## Valide a assinatura > Linhas destacadas: 16-29 Cada entrega de um webhook com segredo traz `X-Ninepay-Timestamp` e `X-Ninepay-Signature`. Calcule `sha256=` + HMAC-SHA256 de `"{timestamp}.{corpo cru}"`, com o segredo do webhook (`nep_whs_…`) como chave, e compare com o header em tempo constante. Recuse com 401 se não bater ou se o timestamp tiver mais de 5 minutos de diferença para o seu relógio. A assinatura muda a cada tentativa, então valide todas. O segredo aparece uma vez só, ao criar o webhook ou ao clicar em **Gerar novo segredo**. Webhooks criados antes da assinatura não têm segredo, e não vêm assinados, até você clicar em **Gerar segredo** em **Integrações → Webhooks**. ## Responda em até 10 segundos > Linhas destacadas: 33-34 Qualquer status fora de 2xx, ou a falta de resposta, conta como falha, e a entrega é repetida até 10 vezes, com intervalos crescentes. Responda primeiro e deixe o processamento pesado para depois (numa fila, por exemplo). ## Ignore entregas repetidas > Linhas destacadas: 36-37 O `id` é o mesmo em todas as tentativas de uma entrega. Guarde os ids já processados para não liberar nada duas vezes. Os eventos também podem chegar fora de ordem. ## Confirme na API antes de liberar > Linhas destacadas: 39-51 A assinatura prova que foi a NinePay quem enviou, mas não substitui a consulta. Trate o evento como um aviso: consulte `GET /payments/{id}` com o seu token e libere só se o `status` for `paid`. Assim, um `POST` forjado ou repetido para o seu endereço não libera nada. ## Código ```js Node.js import { createServer } from 'node:http'; import { createHmac, timingSafeEqual } from 'node:crypto'; const BASE_URL = 'https://api.sandbox.cefispay.com.br/api/v1'; const processed = new Set(); // em produção, guarde no seu banco createServer(async (req, res) => { if (req.method !== 'POST' || req.url !== '/webhooks/ninepay') { res.writeHead(404).end(); return; } const chunks = []; for await (const chunk of req) chunks.push(chunk); const rawBody = Buffer.concat(chunks); // corpo cru, antes de qualquer parse // Valide a assinatura antes de confiar no evento const timestamp = req.headers['x-ninepay-timestamp'] ?? ''; const signature = req.headers['x-ninepay-signature'] ?? ''; const expected = 'sha256=' + createHmac('sha256', process.env.NINEPAY_WEBHOOK_SECRET) .update(`${timestamp}.`) .update(rawBody) .digest('hex'); const a = Buffer.from(signature); const b = Buffer.from(expected); const fresh = /^\d+$/.test(timestamp) && Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300; if (!fresh || a.length !== b.length || !timingSafeEqual(a, b)) { res.writeHead(401).end(); return; } const event = JSON.parse(rawBody.toString('utf8')); // Responda 2xx logo (em até 10 s) e processe depois res.writeHead(200).end(); if (processed.has(event.id)) return; // a mesma entrega pode chegar de novo processed.add(event.id); if (event.type === 'payment.approved') { // Mesmo com a assinatura válida, confirme o estado na API const apiRes = await fetch(`${BASE_URL}/payments/${event.data.id}`, { headers: { Authorization: `Bearer ${process.env.NINEPAY_TOKEN}`, Accept: 'application/json', }, }); const { data: payment } = await apiRes.json(); if (payment.status === 'paid') { // libere o acesso do cliente aqui } } }).listen(3000); ```