
Conheça o projeto
O Sistema de reembolso é o segundo desafio da formação React 2025.
Neste projeto, o objetivo é construir um aplicativo de solicitações de reembolso para ambientes corporativos.
A aplicação contempla criação, listagem e remoção de reembolsos. Além disso, também contempla o upload de recibo relacionado ao reembolso, busca pelo nome do autor da solicitação, paginação e página de detalhes do pedido.
Esse desafio reforça conceitos fundamentais do React e aprofunda em novos tópicos que serão usados em qualquer aplicação.
Recursos
Materiais para você usar como base para o desenvolvimento
Instruções
Estrutura, regras e requisitos do projeto
1. Objetivo
Construir um aplicativo de sistema de reembolso funcional, aplicando os seguintes pontos:
- Criação, listagem e exclusão de reembolsos.
- Upload de recibo da solicitação.
- Paginação da listagem de reembolsos.
- Busca pelo nome do autor da solicitação.
- Página de detalhes da solicitação.
- Exibição do recibo enviado.
- Consumo da API fornecida pela Rocketseat.
2. Conceitos e Regras
- Consumo de API
- Utilizar Axios para chamadas HTTP.
- Integrar com a API disponibilizada do desafio.
- Integrar com React Query para melhor gerenciamento do estados assíncronos
- Gerenciar erros e exibir mensagens de feedback para o usuário.
 
- Formulários
- Utilizar React Hook Form para formulários, com validação.
- Criar formulários para cadastro de reembolso.
 
- Imagem e Upload
- Enviar um recibo em formato JPG, PNG ou PDF.
- A imagem não deve ser maior do que 2mb.
 
- Funcionalidades do App
- Home: página que lista reembolsos de forma paginada, com barra de busca.
- Detalhes do Reembolso: página que exibe detalhes do reembolso. Nessa página deve ser possível exibir o recibo e excluir o reembolso.
- Criar Reembolso: modal que exibe formulário com os seguintes campos: nome da solicitação, valor, categoria e arquivo do recibo.
- Sucesso da solicitação: página que exibe uma mensagem de sucesso ao registrar o reembolso.
- Confirmação de exlusão: modal que exibe uma confirmação se o usuário quer realmente apagar aquele reembolso.
 
3. Desenvolvendo o projeto
Para desenvolver esse projeto, recomendamos utilizar as principais ferramentas que utilizamos durante o desenvolvimento do projeto Álbum de fotos dessa formação.
Além disso, você deve consumir a API que preparamos para esse projeto, assim você pode se concentrar em desenvolver apenas a parte web frontend. Nas 2 próximas seções, abordaremos com mais detalhes sobre a estrutura e como utilizá-la.
3.1 Como utilizar a API?
Para que você consiga utilizar a API, é preciso baixar o código no repositório, seja clonando utilizando o comando git clone git@github.com:rocketseat-education/refund-api.git ou clicando na opção Download ZIP.
Após o download, você deve acessar a pasta do seu projeto e instalar as dependências com o comando npm i. Para executar o projeto, é preciso preparar o banco de dados primeiro. Para isso, execute o comando npm run db:prepare, isso criará o banco de dados SQLite. Por fim, para executar a aplicação basta executar o comando npm run dev.
Com isso, a sua API estará pronta para ser consumida no endereço http://localhost:3333.
3.2 Estrutura da API
A API desse desafio foi estruturada seguindo a opinião do framework Adonis. Porém, você não é obrigado a entender o framework/node para entender as rotas, pois documentaremos elas mais abaixo. Para interagir com o banco e API, sugerimos duas ferramentas para te auxiliar:
- Beekeeper: Recomendamos o uso do Beekeeper para visualização dos dados do banco de dados. Basta apontar para o arquivo db.sqlite3gerado na pastatmpapós a execução do comandonpm run db:prepare.
- Insomnia: Caso queira interagir com a API de uma maneira bem direta e fácil, você pode utilizar um API Rest Client como o Insomnia. Basta executar o download dele e importar as configurações que disponibilizamos aqui. Dessa forma, você terá acesso a todas as rotas e poderá fazer as requisições diretamente por ele.
3.3 Rotas
Clique aqui para expandir
Refund API — Documentação da API
Base URL: http://localhost:3333
Receipts
POST /receipts — Cria um recibo
Faz upload de um comprovante/nota fiscal.
- 
Headers: Content-Type: multipart/form-data
- 
Body (multipart/form-data): - receiptFile(file, obrigatório) — arquivo do recibo (imagem/PDF)
 
- 
Exemplo de resposta de sucesso (200) { "receipt": { "originalFilename": "rodas-pintadas", "path": "/tmp/aii8m2goy0eyutg7ui07lz0i", "extname": "jpeg", "filename": "rodas-pintadas-u3nd3Eiipi", "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "createdAt": "2025-10-07T20:32:24.543+00:00", "updatedAt": "2025-10-07T20:32:24.543+00:00" } }
GET /receipts/{id} — Lista um recibo específico
Retorna metadados de um recibo.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) { "receipt": { "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "originalFilename": "rodas-pintadas", "filename": "rodas-pintadas-u3nd3Eiipi", "path": "uploads/rodas-pintadas-u3nd3Eiipi.jpeg", "extname": "jpeg", "refundId": "a0dfc724-5369-4962-9c95-4250f1d928fb", "createdAt": "2025-10-07T20:32:24.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00" } }
DELETE /receipts/{id} — Deleta um recibo específico
Remove um recibo. Essa rota também dispara a remoção do arquivo físico no sistema.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) { "message": "receipt rodas-pintadas-UzS2-fsrEx deleted succesfully." }
GET /receipts/download/{id} — Gera uma URL assinada do recibo para download
Retorna uma URL assinada do recibo, permitindo o download do arquivo caso o usuário queira. O link é válido por 60 segundos.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) Redireciona para a rota que exibe o arquivo binário
Refunds
GET /refunds — Lista todos os reembolsos
Lista reembolsos de forma pagina, com busca textual pelo nome da solicitação.
- 
Query params (opcional): - q(string) — termo de busca
- page(number) - número da página da paginação.
 
- 
Exemplo de resposta de sucesso (200) { "refunds": { "meta": { "total": 1, "perPage": 10, "currentPage": 1, "lastPage": 1, "firstPage": 1, "firstPageUrl": "/?page=1", "lastPageUrl": "/?page=1", "nextPageUrl": null, "previousPageUrl": null }, "data": [ { "id": "a0dfc724-5369-4962-9c95-4250f1d928fb", "title": "Elias", "category": "hosting", "value": 0, "deletedAt": null, "createdAt": "2025-10-07T20:32:33.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00", "receipt": { "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "originalFilename": "rodas-pintadas", "filename": "rodas-pintadas-u3nd3Eiipi", "path": "uploads/rodas-pintadas-u3nd3Eiipi.jpeg", "extname": "jpeg", "refundId": "a0dfc724-5369-4962-9c95-4250f1d928fb", "createdAt": "2025-10-07T20:32:24.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00" } } ] } }
GET /refunds/{id} — Lista reembolso específico
Retorna um reembolso específico.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) { "refund": { "id": "a0dfc724-5369-4962-9c95-4250f1d928fb", "title": "Elias", "category": "hosting", "value": 0, "deletedAt": null, "createdAt": "2025-10-07T20:32:33.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00", "receipt": { "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "originalFilename": "rodas-pintadas", "filename": "rodas-pintadas-u3nd3Eiipi", "path": "uploads/rodas-pintadas-u3nd3Eiipi.jpeg", "extname": "jpeg", "refundId": "a0dfc724-5369-4962-9c95-4250f1d928fb", "createdAt": "2025-10-07T20:32:24.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00" } } }
POST /refunds — Cria um reembolso
Cria um novo reembolso associado a um recibo. É obrigatório o fornecimento do ID do recibo criado previamente.
Dica: recomendamos chamar a rota de criação do recibo assim que o usuário selecionar o arquivo, armazenar o ID e informá-lo no body dessa requisição juntamente com os outros campos.
- Headers: Content-Type: application/json
- Body (JSON):
- title(string, obrigatório)
- category(string, obrigatório) — Precisa necessariamente atender a pelo menos um dos seguintes valores:- food,- hosting,- transport,- services,- other.
- value(number, obrigatório) — valor do reembolso. Esse valor deve ser informado em centavos, ou seja, sem casas decimais.
- receipt(uuid, obrigatório) — ID do recibo associado
 
- Exemplo (body):
{ "title": "Elias", "category": "hosting", "value": 50050, "receipt": "3813f678-2ad7-4c68-9a39-55fa8780b323" }
- 
Exemplo de resposta de sucesso (200) { "refund": { "title": "Elias", "category": "hosting", "value": 50050, "id": "a87d7af3-0aad-457e-95d8-8b15edf496ec", "createdAt": "2025-10-07T21:31:23.569+00:00", "updatedAt": "2025-10-07T21:31:23.569+00:00", "receipt": { "id": "5c0d2fd6-95d6-4236-835f-dc44f93fb7d1", "originalFilename": "IPVA-2024", "filename": "ipva-2024-cwG9pOWhVi", "path": "uploads/ipva-2024-cwG9pOWhVi.pdf", "extname": "pdf", "refundId": "a87d7af3-0aad-457e-95d8-8b15edf496ec", "createdAt": "2025-10-07T21:31:17.000+00:00", "updatedAt": "2025-10-07T21:31:23.000+00:00" } } }
DELETE /refunds/{id} — Remoção de um reembolso específico
Remove um reembolso.
- Path params:
- id(uuid, obrigatório)
 
- Exemplo de resposta de sucesso (200)
{ "message": "refund Elias deleted succesfully." }
4. Desenvolvendo o projeto
Para desenvolver esse projeto, recomendamos utilizar as principais ferramentas que utilizamos durante o desenvolvimento do primeiro módulo da formação.
Caso você tenha alguma dificuldade você pode ir no nosso fórum e deixar sua dúvida por lá!
Após terminar o desafio, caso você queira, você pode tentar dar o próximo passo e deixar a aplicação com a sua cara. Tente mudar o layout, cores, ou até adicionar novas funcionalidades para ir além 🚀
5. Entrega
Após concluir o desafio, você deve enviar a URL do seu código no Github.
Além disso, que tal fazer um post no LinkedIn compartilhando o seu aprendizado e contando como foi a experiência?
É uma excelente forma de demonstrar seus conhecimentos e atrair novas oportunidades!
Obs: Se você se sentir à vontade, pode postar um print do resultado final e nos marcar! Vai ser incrível acompanhar a sua evolução! 💜
Feito com 💜 por Rocketseat 👋
1. Objetivo
Construir um aplicativo de sistema de reembolso funcional, aplicando os seguintes pontos:
- Criação, listagem e exclusão de reembolsos.
- Upload de recibo da solicitação.
- Paginação da listagem de reembolsos.
- Busca pelo nome do autor da solicitação.
- Página de detalhes da solicitação.
- Exibição do recibo enviado.
- Consumo da API fornecida pela Rocketseat.
2. Conceitos e Regras
- Consumo de API
- Utilizar Axios para chamadas HTTP.
- Integrar com a API disponibilizada do desafio.
- Integrar com React Query para melhor gerenciamento do estados assíncronos
- Gerenciar erros e exibir mensagens de feedback para o usuário.
 
- Formulários
- Utilizar React Hook Form para formulários, com validação.
- Criar formulários para cadastro de reembolso.
 
- Imagem e Upload
- Enviar um recibo em formato JPG, PNG ou PDF.
- A imagem não deve ser maior do que 2mb.
 
- Funcionalidades do App
- Home: página que lista reembolsos de forma paginada, com barra de busca.
- Detalhes do Reembolso: página que exibe detalhes do reembolso. Nessa página deve ser possível exibir o recibo e excluir o reembolso.
- Criar Reembolso: modal que exibe formulário com os seguintes campos: nome da solicitação, valor, categoria e arquivo do recibo.
- Sucesso da solicitação: página que exibe uma mensagem de sucesso ao registrar o reembolso.
- Confirmação de exlusão: modal que exibe uma confirmação se o usuário quer realmente apagar aquele reembolso.
 
3. Desenvolvendo o projeto
Para desenvolver esse projeto, recomendamos utilizar as principais ferramentas que utilizamos durante o desenvolvimento do projeto Álbum de fotos dessa formação.
Além disso, você deve consumir a API que preparamos para esse projeto, assim você pode se concentrar em desenvolver apenas a parte web frontend. Nas 2 próximas seções, abordaremos com mais detalhes sobre a estrutura e como utilizá-la.
3.1 Como utilizar a API?
Para que você consiga utilizar a API, é preciso baixar o código no repositório, seja clonando utilizando o comando git clone git@github.com:rocketseat-education/refund-api.git ou clicando na opção Download ZIP.
Após o download, você deve acessar a pasta do seu projeto e instalar as dependências com o comando npm i. Para executar o projeto, é preciso preparar o banco de dados primeiro. Para isso, execute o comando npm run db:prepare, isso criará o banco de dados SQLite. Por fim, para executar a aplicação basta executar o comando npm run dev.
Com isso, a sua API estará pronta para ser consumida no endereço http://localhost:3333.
3.2 Estrutura da API
A API desse desafio foi estruturada seguindo a opinião do framework Adonis. Porém, você não é obrigado a entender o framework/node para entender as rotas, pois documentaremos elas mais abaixo. Para interagir com o banco e API, sugerimos duas ferramentas para te auxiliar:
- Beekeeper: Recomendamos o uso do Beekeeper para visualização dos dados do banco de dados. Basta apontar para o arquivo db.sqlite3gerado na pastatmpapós a execução do comandonpm run db:prepare.
- Insomnia: Caso queira interagir com a API de uma maneira bem direta e fácil, você pode utilizar um API Rest Client como o Insomnia. Basta executar o download dele e importar as configurações que disponibilizamos aqui. Dessa forma, você terá acesso a todas as rotas e poderá fazer as requisições diretamente por ele.
3.3 Rotas
Clique aqui para expandir
Refund API — Documentação da API
Base URL: http://localhost:3333
Receipts
POST /receipts — Cria um recibo
Faz upload de um comprovante/nota fiscal.
- 
Headers: Content-Type: multipart/form-data
- 
Body (multipart/form-data): - receiptFile(file, obrigatório) — arquivo do recibo (imagem/PDF)
 
- 
Exemplo de resposta de sucesso (200) { "receipt": { "originalFilename": "rodas-pintadas", "path": "/tmp/aii8m2goy0eyutg7ui07lz0i", "extname": "jpeg", "filename": "rodas-pintadas-u3nd3Eiipi", "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "createdAt": "2025-10-07T20:32:24.543+00:00", "updatedAt": "2025-10-07T20:32:24.543+00:00" } }
GET /receipts/{id} — Lista um recibo específico
Retorna metadados de um recibo.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) { "receipt": { "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "originalFilename": "rodas-pintadas", "filename": "rodas-pintadas-u3nd3Eiipi", "path": "uploads/rodas-pintadas-u3nd3Eiipi.jpeg", "extname": "jpeg", "refundId": "a0dfc724-5369-4962-9c95-4250f1d928fb", "createdAt": "2025-10-07T20:32:24.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00" } }
DELETE /receipts/{id} — Deleta um recibo específico
Remove um recibo. Essa rota também dispara a remoção do arquivo físico no sistema.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) { "message": "receipt rodas-pintadas-UzS2-fsrEx deleted succesfully." }
GET /receipts/download/{id} — Gera uma URL assinada do recibo para download
Retorna uma URL assinada do recibo, permitindo o download do arquivo caso o usuário queira. O link é válido por 60 segundos.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) Redireciona para a rota que exibe o arquivo binário
Refunds
GET /refunds — Lista todos os reembolsos
Lista reembolsos de forma pagina, com busca textual pelo nome da solicitação.
- 
Query params (opcional): - q(string) — termo de busca
- page(number) - número da página da paginação.
 
- 
Exemplo de resposta de sucesso (200) { "refunds": { "meta": { "total": 1, "perPage": 10, "currentPage": 1, "lastPage": 1, "firstPage": 1, "firstPageUrl": "/?page=1", "lastPageUrl": "/?page=1", "nextPageUrl": null, "previousPageUrl": null }, "data": [ { "id": "a0dfc724-5369-4962-9c95-4250f1d928fb", "title": "Elias", "category": "hosting", "value": 0, "deletedAt": null, "createdAt": "2025-10-07T20:32:33.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00", "receipt": { "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "originalFilename": "rodas-pintadas", "filename": "rodas-pintadas-u3nd3Eiipi", "path": "uploads/rodas-pintadas-u3nd3Eiipi.jpeg", "extname": "jpeg", "refundId": "a0dfc724-5369-4962-9c95-4250f1d928fb", "createdAt": "2025-10-07T20:32:24.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00" } } ] } }
GET /refunds/{id} — Lista reembolso específico
Retorna um reembolso específico.
- 
Path params: - id(uuid, obrigatório)
 
- 
Exemplo de resposta de sucesso (200) { "refund": { "id": "a0dfc724-5369-4962-9c95-4250f1d928fb", "title": "Elias", "category": "hosting", "value": 0, "deletedAt": null, "createdAt": "2025-10-07T20:32:33.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00", "receipt": { "id": "3813f678-2ad7-4c68-9a39-55fa8780b323", "originalFilename": "rodas-pintadas", "filename": "rodas-pintadas-u3nd3Eiipi", "path": "uploads/rodas-pintadas-u3nd3Eiipi.jpeg", "extname": "jpeg", "refundId": "a0dfc724-5369-4962-9c95-4250f1d928fb", "createdAt": "2025-10-07T20:32:24.000+00:00", "updatedAt": "2025-10-07T20:32:33.000+00:00" } } }
POST /refunds — Cria um reembolso
Cria um novo reembolso associado a um recibo. É obrigatório o fornecimento do ID do recibo criado previamente.
Dica: recomendamos chamar a rota de criação do recibo assim que o usuário selecionar o arquivo, armazenar o ID e informá-lo no body dessa requisição juntamente com os outros campos.
- Headers: Content-Type: application/json
- Body (JSON):
- title(string, obrigatório)
- category(string, obrigatório) — Precisa necessariamente atender a pelo menos um dos seguintes valores:- food,- hosting,- transport,- services,- other.
- value(number, obrigatório) — valor do reembolso. Esse valor deve ser informado em centavos, ou seja, sem casas decimais.
- receipt(uuid, obrigatório) — ID do recibo associado
 
- Exemplo (body):
{ "title": "Elias", "category": "hosting", "value": 50050, "receipt": "3813f678-2ad7-4c68-9a39-55fa8780b323" }
- 
Exemplo de resposta de sucesso (200) { "refund": { "title": "Elias", "category": "hosting", "value": 50050, "id": "a87d7af3-0aad-457e-95d8-8b15edf496ec", "createdAt": "2025-10-07T21:31:23.569+00:00", "updatedAt": "2025-10-07T21:31:23.569+00:00", "receipt": { "id": "5c0d2fd6-95d6-4236-835f-dc44f93fb7d1", "originalFilename": "IPVA-2024", "filename": "ipva-2024-cwG9pOWhVi", "path": "uploads/ipva-2024-cwG9pOWhVi.pdf", "extname": "pdf", "refundId": "a87d7af3-0aad-457e-95d8-8b15edf496ec", "createdAt": "2025-10-07T21:31:17.000+00:00", "updatedAt": "2025-10-07T21:31:23.000+00:00" } } }
DELETE /refunds/{id} — Remoção de um reembolso específico
Remove um reembolso.
- Path params:
- id(uuid, obrigatório)
 
- Exemplo de resposta de sucesso (200)
{ "message": "refund Elias deleted succesfully." }
4. Desenvolvendo o projeto
Para desenvolver esse projeto, recomendamos utilizar as principais ferramentas que utilizamos durante o desenvolvimento do primeiro módulo da formação.
Caso você tenha alguma dificuldade você pode ir no nosso fórum e deixar sua dúvida por lá!
Após terminar o desafio, caso você queira, você pode tentar dar o próximo passo e deixar a aplicação com a sua cara. Tente mudar o layout, cores, ou até adicionar novas funcionalidades para ir além 🚀
5. Entrega
Após concluir o desafio, você deve enviar a URL do seu código no Github.
Além disso, que tal fazer um post no LinkedIn compartilhando o seu aprendizado e contando como foi a experiência?
É uma excelente forma de demonstrar seus conhecimentos e atrair novas oportunidades!
Obs: Se você se sentir à vontade, pode postar um print do resultado final e nos marcar! Vai ser incrível acompanhar a sua evolução! 💜
Feito com 💜 por Rocketseat 👋
Tarefas
Use este checklist para ajudar a organizar a sua entrega