diff --git a/react-app/src/App.js b/react-app/src/App.js
index df17437..60a8b65 100644
--- a/react-app/src/App.js
+++ b/react-app/src/App.js
@@ -5,7 +5,7 @@ import Home from './home/Home.js';
import Signup from './signup/Signup.js';
import Dashboard from './dashboard/Dashboard.js';
import Products from './dashboard/Products.js';
-import Payments from './dashboard/Payments.js';
+import Transactions from './dashboard/Transactions.js';
import Payouts from './dashboard/Payouts.js';
import Settings from './dashboard/Settings.js';
import Login from './signup/Login.js';
@@ -19,7 +19,7 @@ function App() {
} />
} />
} />
- } />
+ } />
} />
} />
diff --git a/react-app/src/dashboard/Dashboard.js b/react-app/src/dashboard/Dashboard.js
index 5ab76d1..82d6aa2 100644
--- a/react-app/src/dashboard/Dashboard.js
+++ b/react-app/src/dashboard/Dashboard.js
@@ -62,9 +62,10 @@ export default function Dashboard() {
-
-
+
+
+
diff --git a/react-app/src/dashboard/DashboardDrawer.js b/react-app/src/dashboard/DashboardDrawer.js
index af5c765..439d4c5 100644
--- a/react-app/src/dashboard/DashboardDrawer.js
+++ b/react-app/src/dashboard/DashboardDrawer.js
@@ -65,12 +65,12 @@ export default function DashboardDrawer() {
-
+
-
+
diff --git a/react-app/src/dashboard/Payments.js b/react-app/src/dashboard/Payments.js
deleted file mode 100644
index d3c4fc5..0000000
--- a/react-app/src/dashboard/Payments.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import * as React from 'react';
-
-import Box from '@mui/material/Box';
-import Chip from '@mui/material/Chip';
-
-import Toolbar from '@mui/material/Toolbar';
-import Divider from '@mui/material/Divider';
-import { useParams } from "react-router-dom";
-
-import DashboardHeader from "./DashboardHeader.js";
-import DashboardDrawer from "./DashboardDrawer.js";
-
-export default function Products() {
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/react-app/src/dashboard/Payouts.js b/react-app/src/dashboard/Payouts.js
index 8c9387c..006885d 100644
--- a/react-app/src/dashboard/Payouts.js
+++ b/react-app/src/dashboard/Payouts.js
@@ -26,8 +26,8 @@ export default function Payouts() {
-
-
+
+
diff --git a/react-app/src/dashboard/Products.js b/react-app/src/dashboard/Products.js
index d2fda6d..e401466 100644
--- a/react-app/src/dashboard/Products.js
+++ b/react-app/src/dashboard/Products.js
@@ -28,9 +28,10 @@ export default function Products() {
-
-
+
+
+
diff --git a/react-app/src/dashboard/Settings.js b/react-app/src/dashboard/Settings.js
index 058691d..00ef50a 100644
--- a/react-app/src/dashboard/Settings.js
+++ b/react-app/src/dashboard/Settings.js
@@ -27,8 +27,8 @@ export default function Settings() {
-
-
+
+
diff --git a/react-app/src/dashboard/Transactions.js b/react-app/src/dashboard/Transactions.js
new file mode 100644
index 0000000..2795016
--- /dev/null
+++ b/react-app/src/dashboard/Transactions.js
@@ -0,0 +1,138 @@
+import React, { useState, useEffect } from 'react';
+import axios from "axios";
+
+import Box from '@mui/material/Box';
+import Chip from '@mui/material/Chip';
+import Toolbar from '@mui/material/Toolbar';
+import Divider from '@mui/material/Divider';
+import { useNavigate } from 'react-router-dom';
+
+import Paper from '@mui/material/Paper';
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell from '@mui/material/TableCell';
+import TableContainer from '@mui/material/TableContainer';
+import TableHead from '@mui/material/TableHead';
+import TablePagination from '@mui/material/TablePagination';
+import TableRow from '@mui/material/TableRow';
+
+import DashboardHeader from "./DashboardHeader.js";
+import DashboardDrawer from "./DashboardDrawer.js";
+
+export default function Products() {
+
+ const navigate = useNavigate()
+
+ const [page, setPage] = useState(0);
+ const [rowsPerPage, setRowsPerPage] = useState(10);
+ const [rows, setRows] = useState([]);
+
+ const handleChangePage = (event, newPage) => {
+ setPage(newPage);
+ };
+
+ const handleChangeRowsPerPage = (event) => {
+ setRowsPerPage(+event.target.value);
+ setPage(0);
+ };
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const response = await axios.post('/api/dashboard/getTransactions');
+ setRows(response.data);
+ } catch (error) {
+ console.error('API request error:', error);
+ navigate('/');
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {columns.map((column) => (
+
+ {column.label}
+
+ ))}
+
+
+
+ {rows
+ .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
+ .map((row) => {
+ return (
+
+ {columns.map((column) => {
+ const value = row[column.id];
+ return (
+
+ {column.format && typeof value === 'number'? column.format(value) : value}
+
+ );
+ })}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const columns = [
+ { id: 'id', label: 'ID', minWidth: 100 },
+ { id: 'status', label: 'Status', minWidth: 120 },
+ { id: 'type', label: 'Type', minWidth: 120 },
+ { id: 'created', label: 'Created', minWidth: 220 },
+ { id: 'amount', label: 'Amount', minWidth: 150 },
+];
\ No newline at end of file
diff --git a/src/main/java/com/adyen/controller/DashboardController.java b/src/main/java/com/adyen/controller/DashboardController.java
index 3f128f8..0d99ec1 100644
--- a/src/main/java/com/adyen/controller/DashboardController.java
+++ b/src/main/java/com/adyen/controller/DashboardController.java
@@ -1,6 +1,7 @@
package com.adyen.controller;
import com.adyen.model.OnboardingLinkProperties;
+import com.adyen.model.TransactionItem;
import com.adyen.model.User;
import com.adyen.model.balanceplatform.AccountHolder;
import com.adyen.model.legalentitymanagement.LegalEntity;
@@ -15,6 +16,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
+import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
@RestController
@@ -92,6 +95,18 @@ ResponseEntity getOnboardingLink(@RequestBody OnboardingLinkProperties o
);
}
+ @PostMapping("/getTransactions")
+ ResponseEntity> getTransactions() {
+
+ if (getUserIdOnSession() == null) {
+ log.warn("User is not logged in");
+ return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+ }
+
+ return new ResponseEntity<>(
+ getConfigurationAPIService().getTransactions(getUserIdOnSession()), HttpStatus.ACCEPTED);
+ }
+
public ConfigurationAPIService getConfigurationAPIService() {
return configurationAPIService;
}
diff --git a/src/main/java/com/adyen/model/TransactionItem.java b/src/main/java/com/adyen/model/TransactionItem.java
new file mode 100644
index 0000000..2ca76c6
--- /dev/null
+++ b/src/main/java/com/adyen/model/TransactionItem.java
@@ -0,0 +1,75 @@
+package com.adyen.model;
+
+public class TransactionItem {
+ private String id;
+ private String status;
+ private String type;
+ private String created;
+ private String amount;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getCreated() {
+ return created;
+ }
+
+ public void setCreated(String created) {
+ this.created = created;
+ }
+
+ public String getAmount() {
+ return amount;
+ }
+
+ public void setAmount(String amount) {
+ this.amount = amount;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public TransactionItem id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public TransactionItem status(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public TransactionItem created(String created) {
+ this.created = created;
+ return this;
+ }
+
+ public TransactionItem amount(String amount) {
+ this.amount = amount;
+ return this;
+ }
+
+ public TransactionItem type(String type) {
+ this.type = type;
+ return this;
+ }
+
+}
diff --git a/src/main/java/com/adyen/service/ConfigurationAPIService.java b/src/main/java/com/adyen/service/ConfigurationAPIService.java
index d42b13e..19eb50f 100644
--- a/src/main/java/com/adyen/service/ConfigurationAPIService.java
+++ b/src/main/java/com/adyen/service/ConfigurationAPIService.java
@@ -4,14 +4,22 @@
import com.adyen.config.ApplicationProperty;
import com.adyen.enums.Environment;
import com.adyen.model.AccountHolderStatus;
+import com.adyen.model.TransactionItem;
import com.adyen.model.balanceplatform.*;
+import com.adyen.model.transfers.Transaction;
+import com.adyen.model.transfers.TransactionSearchResponse;
import com.adyen.service.balanceplatform.AccountHoldersApi;
import com.adyen.service.balanceplatform.BalanceAccountsApi;
+import com.adyen.service.transfers.TransactionsApi;
+import com.adyen.util.TransactionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.time.OffsetDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@@ -32,6 +40,9 @@ public class ConfigurationAPIService {
@Autowired
private ApplicationProperty applicationProperty;
+ @Autowired
+ private TransactionHandler transactionHandler;
+
public Optional getAccountHolder(String accountHolderId) {
Optional accountHolder = Optional.empty();
@@ -119,6 +130,38 @@ public BalanceAccount createBalanceAccount(String accountHolderId) {
return balanceAccount;
}
+ /**
+ * Get all transactions for the user (accountHolder)
+ * @param accountHolderId
+ * @return
+ */
+ public List getTransactions(String accountHolderId) {
+
+ List transactionItems = null;
+
+ try {
+
+ // in the last X days
+ OffsetDateTime createdSince = OffsetDateTime.now().minus(365, ChronoUnit.DAYS);
+ // until today
+ OffsetDateTime createdUntil = OffsetDateTime.now();
+ // max number of transactions to fetch
+ Integer limit = 100;
+
+ TransactionSearchResponse transactionSearchResponse = getTransactionsApi().getAllTransactions(
+ null, null, accountHolderId, null,
+ null, createdSince, createdUntil, limit, null);
+
+ transactionItems = getTransactionHandler().getTransactionItems(transactionSearchResponse.getData());
+ } catch (Exception e) {
+ log.error(e.toString(), e);
+ throw new RuntimeException("Cannot create BalanceAccount: " + e.getMessage());
+ }
+
+ return transactionItems;
+
+ }
+
// AccountHoldersApi handler
private AccountHoldersApi getAccountHoldersApi() {
return new AccountHoldersApi(getApiClient());
@@ -129,6 +172,11 @@ private BalanceAccountsApi getBalanceAccountsApi() {
return new BalanceAccountsApi(getApiClient());
}
+ private TransactionsApi getTransactionsApi() {
+ return new TransactionsApi(getApiClient());
+ }
+
+
// create client to access the Configuration API
private Client getApiClient() {
if (apiClient == null) {
@@ -149,4 +197,11 @@ public void setApplicationProperty(ApplicationProperty applicationProperty) {
this.applicationProperty = applicationProperty;
}
+ public TransactionHandler getTransactionHandler() {
+ return transactionHandler;
+ }
+
+ public void setTransactionHandler(TransactionHandler transactionHandler) {
+ this.transactionHandler = transactionHandler;
+ }
}
diff --git a/src/main/java/com/adyen/util/TransactionHandler.java b/src/main/java/com/adyen/util/TransactionHandler.java
new file mode 100644
index 0000000..aed6eaf
--- /dev/null
+++ b/src/main/java/com/adyen/util/TransactionHandler.java
@@ -0,0 +1,87 @@
+package com.adyen.util;
+
+import com.adyen.model.TransactionItem;
+import com.adyen.model.transfers.Amount;
+import com.adyen.model.transfers.Transaction;
+import org.springframework.stereotype.Service;
+
+import java.text.NumberFormat;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to work with the Transaction class
+ */
+@Service
+public class TransactionHandler {
+
+ /**
+ * Create a list of TransactionItem from the given list of Transaction
+ * @param transactions
+ * @return
+ */
+ public List getTransactionItems(List transactions) {
+ List transactionItems = new ArrayList<>();
+
+ for(Transaction transaction : transactions) {
+ transactionItems.add(getTransactionItem(transaction));
+ }
+
+ return transactionItems;
+ }
+
+ /**
+ * Create a TransactionItem from the given Transaction
+ * @param transaction
+ * @return
+ */
+ public TransactionItem getTransactionItem(Transaction transaction) {
+ return new TransactionItem()
+ .id(transaction.getId())
+ .status(transaction.getStatus().getValue())
+ .type(getType(transaction.getAmount()))
+ .created(formatDate(transaction.getCreationDate()))
+ .amount(formatAmount(transaction.getAmount()));
+ }
+
+ private String formatAmount(Amount amount) {
+ String ret = "";
+
+ if(amount != null) {
+ NumberFormat numberFormat = NumberFormat.getNumberInstance();
+ // display absolute amount and format
+ String formattedAmount = numberFormat.format(Math.abs(amount.getValue()));
+ ret = amount.getCurrency() + " " + formattedAmount;
+ }
+ return ret;
+ }
+
+ private String formatDate(OffsetDateTime offsetDateTime) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ return offsetDateTime.format(formatter);
+ }
+
+ /**
+ * Determines type of the transaction
+ * Incoming: positive amount, funds added
+ * Outgoing: negative amount, funds deducted
+ * @param amount
+ * @return
+ */
+ private String getType(Amount amount) {
+ String ret = "";
+
+ if(amount != null) {
+ if(amount.getValue() > 0) {
+ ret = "Incoming";
+ } if(amount.getValue() < 0) {
+ ret = "Outgoing";
+ }
+ }
+
+ return ret;
+ }
+}