Index: backend/src/main/java/com/trekr/backend/controller/FinanceController.java
===================================================================
--- backend/src/main/java/com/trekr/backend/controller/FinanceController.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/controller/FinanceController.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,72 @@
+package com.trekr.backend.controller;
+
+import com.trekr.backend.dto.finance.CreateIncomeRequest;
+import com.trekr.backend.dto.finance.FinanceProfileResponse;
+import com.trekr.backend.dto.finance.FinanceStartRequest;
+import com.trekr.backend.dto.finance.IncomeDto;
+import com.trekr.backend.dto.finance.IncomesResponse;
+import com.trekr.backend.dto.finance.TrackingStatusResponse;
+import com.trekr.backend.security.UserPrincipal;
+import com.trekr.backend.service.FinanceService;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/finance")
+public class FinanceController {
+
+    private final FinanceService financeService;
+
+    public FinanceController(FinanceService financeService) {
+        this.financeService = financeService;
+    }
+
+    @GetMapping("/status")
+    public TrackingStatusResponse status(Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        boolean tracking = financeService.isTracking(principal.getUserId());
+        return new TrackingStatusResponse(tracking);
+    }
+
+    @PostMapping("/start")
+    @ResponseStatus(HttpStatus.CREATED)
+    public TrackingStatusResponse start(@Valid @RequestBody FinanceStartRequest request,
+            Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        financeService.startOrUpdateTracking(principal.getUserId(), request);
+        return new TrackingStatusResponse(true);
+    }
+
+    @GetMapping("/profile")
+    public FinanceProfileResponse profile(Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return financeService.getProfile(principal.getUserId());
+    }
+
+    @GetMapping("/incomes")
+    public IncomesResponse incomes(
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "5") int size,
+            Authentication authentication) {
+        if (size < 1)
+            size = 5;
+        if (size > 50)
+            size = 50;
+        if (page < 0)
+            page = 0;
+
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return financeService.getIncomes(principal.getUserId(), page, size);
+    }
+
+    @PostMapping("/incomes")
+    @ResponseStatus(HttpStatus.CREATED)
+    public IncomeDto createIncome(@Valid @RequestBody CreateIncomeRequest request,
+            Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return financeService.createIncome(principal.getUserId(), request);
+    }
+}
+
Index: backend/src/main/java/com/trekr/backend/dto/finance/CreateIncomeRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/finance/CreateIncomeRequest.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/dto/finance/CreateIncomeRequest.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,35 @@
+package com.trekr.backend.dto.finance;
+
+import jakarta.validation.constraints.NotNull;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+public class CreateIncomeRequest {
+
+    @NotNull
+    private LocalDate date;
+
+    @NotNull
+    private BigDecimal amount;
+
+    public CreateIncomeRequest() {
+    }
+
+    public LocalDate getDate() {
+        return date;
+    }
+
+    public void setDate(LocalDate date) {
+        this.date = date;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public void setAmount(BigDecimal amount) {
+        this.amount = amount;
+    }
+}
+
Index: backend/src/main/java/com/trekr/backend/dto/finance/FinanceProfileResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/finance/FinanceProfileResponse.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/dto/finance/FinanceProfileResponse.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,12 @@
+package com.trekr.backend.dto.finance;
+
+import java.math.BigDecimal;
+
+public record FinanceProfileResponse(
+        BigDecimal spendingBudget,
+        BigDecimal savingBudget,
+        BigDecimal investingBudget,
+        BigDecimal donationBudget,
+        BigDecimal credit) {
+}
+
Index: backend/src/main/java/com/trekr/backend/dto/finance/FinanceStartRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/finance/FinanceStartRequest.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/dto/finance/FinanceStartRequest.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,67 @@
+package com.trekr.backend.dto.finance;
+
+import jakarta.validation.constraints.NotNull;
+
+import java.math.BigDecimal;
+
+public class FinanceStartRequest {
+
+    @NotNull
+    private BigDecimal spendingBudget;
+
+    @NotNull
+    private BigDecimal savingBudget;
+
+    @NotNull
+    private BigDecimal investingBudget;
+
+    @NotNull
+    private BigDecimal donationBudget;
+
+    @NotNull
+    private BigDecimal credit;
+
+    public FinanceStartRequest() {
+    }
+
+    public BigDecimal getSpendingBudget() {
+        return spendingBudget;
+    }
+
+    public void setSpendingBudget(BigDecimal spendingBudget) {
+        this.spendingBudget = spendingBudget;
+    }
+
+    public BigDecimal getSavingBudget() {
+        return savingBudget;
+    }
+
+    public void setSavingBudget(BigDecimal savingBudget) {
+        this.savingBudget = savingBudget;
+    }
+
+    public BigDecimal getInvestingBudget() {
+        return investingBudget;
+    }
+
+    public void setInvestingBudget(BigDecimal investingBudget) {
+        this.investingBudget = investingBudget;
+    }
+
+    public BigDecimal getDonationBudget() {
+        return donationBudget;
+    }
+
+    public void setDonationBudget(BigDecimal donationBudget) {
+        this.donationBudget = donationBudget;
+    }
+
+    public BigDecimal getCredit() {
+        return credit;
+    }
+
+    public void setCredit(BigDecimal credit) {
+        this.credit = credit;
+    }
+}
+
Index: backend/src/main/java/com/trekr/backend/dto/finance/IncomeDto.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/finance/IncomeDto.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/dto/finance/IncomeDto.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,11 @@
+package com.trekr.backend.dto.finance;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+public record IncomeDto(
+        Long incomeId,
+        LocalDate date,
+        BigDecimal amount) {
+}
+
Index: backend/src/main/java/com/trekr/backend/dto/finance/IncomesResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/finance/IncomesResponse.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/dto/finance/IncomesResponse.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,7 @@
+package com.trekr.backend.dto.finance;
+
+import java.util.List;
+
+public record IncomesResponse(List<IncomeDto> incomes, boolean hasMore) {
+}
+
Index: backend/src/main/java/com/trekr/backend/dto/finance/TrackingStatusResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/finance/TrackingStatusResponse.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/dto/finance/TrackingStatusResponse.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,5 @@
+package com.trekr.backend.dto.finance;
+
+public record TrackingStatusResponse(boolean tracking) {
+}
+
Index: backend/src/main/java/com/trekr/backend/repository/FinanceUserRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/FinanceUserRepository.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/repository/FinanceUserRepository.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,8 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.finance.FinanceUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface FinanceUserRepository extends JpaRepository<FinanceUser, Long> {
+}
+
Index: backend/src/main/java/com/trekr/backend/repository/IncomeRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/IncomeRepository.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/repository/IncomeRepository.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,12 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.finance.Income;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface IncomeRepository extends JpaRepository<Income, Long> {
+
+    Page<Income> findByFinanceUser_UserIdOrderByDateDescIncomeIdDesc(Long userId, Pageable pageable);
+}
+
Index: backend/src/main/java/com/trekr/backend/service/FinanceService.java
===================================================================
--- backend/src/main/java/com/trekr/backend/service/FinanceService.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
+++ backend/src/main/java/com/trekr/backend/service/FinanceService.java	(revision 8449b9cc520ba82bb2945db031d4b9973a038e59)
@@ -0,0 +1,157 @@
+package com.trekr.backend.service;
+
+import com.trekr.backend.dto.finance.CreateIncomeRequest;
+import com.trekr.backend.dto.finance.FinanceProfileResponse;
+import com.trekr.backend.dto.finance.FinanceStartRequest;
+import com.trekr.backend.dto.finance.IncomeDto;
+import com.trekr.backend.dto.finance.IncomesResponse;
+import com.trekr.backend.entity.User;
+import com.trekr.backend.entity.finance.FinanceUser;
+import com.trekr.backend.entity.finance.Income;
+import com.trekr.backend.repository.FinanceUserRepository;
+import com.trekr.backend.repository.IncomeRepository;
+import com.trekr.backend.repository.UserRepository;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.util.List;
+
+@Service
+public class FinanceService {
+
+    private final FinanceUserRepository financeUserRepository;
+    private final IncomeRepository incomeRepository;
+    private final UserRepository userRepository;
+
+    public FinanceService(
+            FinanceUserRepository financeUserRepository,
+            IncomeRepository incomeRepository,
+            UserRepository userRepository) {
+        this.financeUserRepository = financeUserRepository;
+        this.incomeRepository = incomeRepository;
+        this.userRepository = userRepository;
+    }
+
+    public boolean isTracking(Long userId) {
+        return financeUserRepository.existsById(userId);
+    }
+
+    @Transactional
+    public void startOrUpdateTracking(Long userId, FinanceStartRequest request) {
+        FinanceUser financeUser = financeUserRepository.findById(userId).orElse(null);
+        if (financeUser == null) {
+            User user = userRepository.findById(userId)
+                    .orElseThrow(() -> new RuntimeException("User not found"));
+            financeUser = new FinanceUser();
+            financeUser.setUser(user);
+        }
+
+        BigDecimal spending = normalizePercent(request.getSpendingBudget());
+        BigDecimal saving = normalizePercent(request.getSavingBudget());
+        BigDecimal investing = normalizePercent(request.getInvestingBudget());
+        BigDecimal donation = normalizePercent(request.getDonationBudget());
+        BigDecimal credit = normalizePercent(request.getCredit());
+
+        validateSumTo100(spending, saving, investing, donation, credit);
+
+        financeUser.setSpendingBudget(spending);
+        financeUser.setSavingBudget(saving);
+        financeUser.setInvestingBudget(investing);
+        financeUser.setDonationBudget(donation);
+        financeUser.setCredit(credit);
+
+        financeUserRepository.save(financeUser);
+    }
+
+    public FinanceProfileResponse getProfile(Long userId) {
+        FinanceUser financeUser = financeUserRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("Finance tracking is not enabled for this user"));
+
+        return new FinanceProfileResponse(
+                financeUser.getSpendingBudget(),
+                financeUser.getSavingBudget(),
+                financeUser.getInvestingBudget(),
+                financeUser.getDonationBudget(),
+                financeUser.getCredit());
+    }
+
+    public IncomesResponse getIncomes(Long userId, int page, int size) {
+        if (!financeUserRepository.existsById(userId)) {
+            return new IncomesResponse(List.of(), false);
+        }
+
+        Page<Income> result = incomeRepository.findByFinanceUser_UserIdOrderByDateDescIncomeIdDesc(
+                userId, PageRequest.of(page, size));
+
+        List<IncomeDto> dtos = result.getContent().stream()
+                .map(i -> new IncomeDto(i.getIncomeId(), i.getDate(), i.getAmount()))
+                .toList();
+
+        return new IncomesResponse(dtos, result.hasNext());
+    }
+
+    @Transactional
+    public IncomeDto createIncome(Long userId, CreateIncomeRequest request) {
+        FinanceUser financeUser = financeUserRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("Finance tracking is not enabled for this user"));
+
+        LocalDate date = request.getDate();
+        if (date == null) {
+            throw new RuntimeException("Date is required");
+        }
+        if (date.isAfter(LocalDate.now().plusDays(1))) {
+            throw new RuntimeException("Date cannot be in the future");
+        }
+
+        BigDecimal amount = request.getAmount();
+        if (amount == null) {
+            throw new RuntimeException("Amount is required");
+        }
+        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new RuntimeException("Amount must be greater than 0");
+        }
+
+        Income income = new Income();
+        income.setFinanceUser(financeUser);
+        income.setDate(date);
+        income.setAmount(amount);
+
+        Income saved = incomeRepository.save(income);
+        return new IncomeDto(saved.getIncomeId(), saved.getDate(), saved.getAmount());
+    }
+
+    private static BigDecimal normalizePercent(BigDecimal value) {
+        if (value == null) {
+            return null;
+        }
+        return value.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private static void validateSumTo100(BigDecimal spending, BigDecimal saving, BigDecimal investing, BigDecimal donation,
+            BigDecimal credit) {
+        if (spending == null || saving == null || investing == null || donation == null || credit == null) {
+            throw new RuntimeException("All 5 percentage values are required");
+        }
+
+        List<BigDecimal> list = List.of(spending, saving, investing, donation, credit);
+
+        for (BigDecimal v : list) {
+            if (v.compareTo(BigDecimal.ZERO) < 0 || v.compareTo(new BigDecimal("100")) > 0) {
+                throw new RuntimeException("Percentages must be between 0 and 100");
+            }
+        }
+
+        BigDecimal sum = spending.add(saving).add(investing).add(donation).add(credit);
+        // allow tiny rounding errors (e.g. 33.33 * 3 + 0.01)
+        BigDecimal diff = sum.subtract(new BigDecimal("100")).abs();
+        if (diff.compareTo(new BigDecimal("0.01")) > 0) {
+            throw new RuntimeException("Percentages must sum to 100");
+        }
+    }
+}
+
