RocketseatBlack Friday 2025
Rocketseat
/
Projetos

Sistema de reembolso

FrontendFrontendIntermediárioIntermediário
ReactReactRest APIReact Query+2
Sistema de reembolso

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

Figma

Figma

Layout · Figma

API do Projeto

API do Projeto

Código · GitHub

Arquivo do Insomnia

Arquivo do Insomnia

Link · GitHub

Figma

Figma

Layout · Figma

API do Projeto

API do Projeto

Código · GitHub

Arquivo do Insomnia

Arquivo do Insomnia

Link · GitHub

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

  1. 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.
  2. Formulários
    • Utilizar React Hook Form para formulários, com validação.
    • Criar formulários para cadastro de reembolso.
  3. Imagem e Upload
    • Enviar um recibo em formato JPG, PNG ou PDF.
    • A imagem não deve ser maior do que 2mb.
  4. 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.sqlite3 gerado na pasta tmp após a execução do comando npm 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

  1. 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.
  2. Formulários
    • Utilizar React Hook Form para formulários, com validação.
    • Criar formulários para cadastro de reembolso.
  3. Imagem e Upload
    • Enviar um recibo em formato JPG, PNG ou PDF.
    • A imagem não deve ser maior do que 2mb.
  4. 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.sqlite3 gerado na pasta tmp após a execução do comando npm 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

Detalhes

Tipo de projetoDesafio prático

Projetos relacionados